Go言語のチュートリアルをやってみた。ついでにまとめて日本語化した。

#はじめに
Go言語をさわりたくてチュートリアルをやりましたが、全部英語でした。
せっかくなので日本語版を作りました。
内容はほぼ一緒（なはず）で、最低限これでわかるであろうの感覚で書いたので
端折ってるところは多々ありますが参考になればうれしいです。

#環境
- Windows10
- visual studio code
- Go言語バージョン 1.16.6 （現時点で最新）

#Go言語をインストール
[ここからインストール](https://golang.org/doc/install)

インストールが終わるとgoコマンドが打てるようになる。

#チュートリアル：Goを使い始める（Tutorial: Get started with Go）をやってみる
[チュートリアル：Goを使い始める](https://golang.org/doc/tutorial/create-module)

任意のディレクトリを作る。
→デスクトップにgo/helloというフォルダを作成

VScodeのターミナルでモジュールを初期化する。
go mod initで行うが、最初はこれをやるというおまじないという感覚でよさそう。

```ターミナル.
C:\Users\ryouta\Desktop\go\hello>go mod init example.com/hello
go: creating new go.mod: module example.com/hello
```

go.modというファイルが出来た。
モジュールを管理するファイルらしい。たくさん触ることになりそうな予感。

``` go.mod
module example.com/hello

go 1.16
```

hello worldを出力するファイルを作る。
ファイルは普通に作った。
ソースはチュートリアルから指定のものを張り付けた。

```hello.go
package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}
```
go run コマンドで実行

```ターミナル.
PS C:\Users\ryouta\Desktop\go\hello> go run .
Hello, World!
```

間隔を掴むため色々実験。ソースを変えてgo runすると色々なエラーが出た。

```ターミナル.
//main→aaaへ変更
go run: cannot run non-main package

//packageの行をコメントアウト
hello.go:3:1: expected 'package', found 'import'

//fmt.Printlnの行をコメントアウト
# example.com/hello
.\hello.go:3:8: imported and not used: "fmt"

//func main()をfunc Main()へ変更
# example.com/hello
runtime.main_main·f: function main is undeclared in the main package
```

###外部パッケージの使い方
Goにはいろいろなパッケージがありその中には便利な関数が用意されているようだ。
例えば[このrsc.io/quoteパッケージ](https://pkg.go.dev/rsc.io/quote)を見ると

func Glass（）文字列
func Go（）文字列
func Hello（）文字列
func Opt（）文字列

という**ことわざ**を返却する関数を提供している。
デモンストレーション用の関数でしょう。
一応全部使ってみる。
流れとしては、goファイル内でimportしたものをgoコマンドで取り込み、その後実行できるようになる。


```hello.go
package main

import "fmt"

import "rsc.io/quote" //追加

func main() {
    fmt.Println(quote.Hello()) //修正
}
```

go mod tidyコマンドでモジュールを追加したり消したりしてくれる。

```ターミナル.
C:\Users\ryouta\Desktop\go\hello>go mod tidy
go: finding module for package rsc.io/quote
go: downloading rsc.io/quote v1.5.2
go: found rsc.io/quote in rsc.io/quote v1.5.2
go: downloading rsc.io/sampler v1.3.0
go: downloading golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
```

go runで実行する。

```
PS C:\Users\ryouta\Desktop\go\hello> go run .   
こんにちは世界。
```

そのほかの関数も使ってみる。

```
// fmt.Println(quote.Glass())に修正
PS C:\Users\ryouta\Desktop\go\hello> go run .   
I can eat glass and it doesn't hurt me.

// fmt.Println(quote.Go())に修正
PS C:\Users\ryouta\Desktop\go\hello> go run .
Don't communicate by sharing memory, share memory by communicating.

// fmt.Println(quote.Opt())に修正
PS C:\Users\ryouta\Desktop\go\hello> go run .
If a program is too slow, it must have a loop.
```
うん、ことわざの意味はよくわかってないけど、動きは分かった。

#チュートリアル：Goモジュールを作成する（Tutorial: Create a Go module）をやってみる
[チュートリアル：Goモジュールを作成する](https://golang.org/doc/tutorial/create-module)

任意のディレクトリを作る。
→デスクトップにgo/greetingsというフォルダを作成

ターミナルでモジュールを初期化する。

```ターミナル.
C:\Users\ryouta\Desktop\go\greetings>go mod init example.com/greetings
go: creating new go.mod: module example.com/greetings
```

greeetings.goというファイルを作り、ソースを張り付ける。

```greeetings.go
package greetings

import "fmt"

// Helloは、指定された人の挨拶を返します。
func Hello(name string) string {
    //メッセージに名前を埋め込む挨拶を返します。
    message := fmt.Sprintf("Hi, %v. Welcome!", name)
    return message
}
```

message：の　：　は宣言と初期化を一括で行うときに使う。

#チュートリアル：別のモジュールからコードを呼び出す（Call your code from another module）
[別のモジュールからコードを呼び出す](https://golang.org/doc/tutorial/call-module-code)
上記で作ったhello.goに以下を張り付ける

```hello.go
package main

import (
    "fmt"
    "example.com/greetings"
)

func main() {
    // Get a greeting message and print it.
    message := greetings.Hello("Gladys")
    fmt.Println(message)
}
```

ここで go mod tidyしてモジュールを読み込もうとすると以下のエラーとなる。

```ターミナル.
PS C:\Users\ryouta\Desktop\go\hello> go mod tidy
go: finding module for package example.com/greetings
exeample.com/hello imports
        example.com/greetings: cannot find module providing package example.com/greetings: unrecognized import path "example.com/greetings": reading https://example.com/greet
```
go mod tidyは公開されているモジュールの中からさがすので、example.com/greetingsが見つからないためにエラーになった。
本番環境ではexample.com/greetingsを公開状態にする必要があるようだが、現状はローカル環境だけに存在しているので、
向き先をローカルへ向ける必要がある。

```ターミナル.
PS C:\Users\ryouta\Desktop\go\hello> go mod edit -replace example.com/greetings=../greetings
```

mod editはgo.modを直接編集するコマンドで、-replaceは置き換えするオプションだ。
単純にexample.com/greetingsは../greetingsに置き換えてね、ってだけ。
go.modを見てみると以下のように更新されていることが確認できる。

```go.mod
module exeample.com/hello

go 1.16

replace example.com/greetings => ../greetings
```
go mod tidyでモジュールを読み込む。

```ターミナル.
PS C:\Users\ryouta\Desktop\go\hello> go mod tidy
go: found example.com/greetings in example.com/greetings v0.0.0-00010101000000-000000000000
```

再度go.modを見てみると以下のように更新されている。

```go.mod
module exeample.com/hello

go 1.16

replace example.com/greetings => ../greetings

require example.com/greetings v0.0.0-00010101000000-000000000000
```
v0.0.0-00010101000000-000000000000　は借採番された疑似バージョン。
これで動かせるようだ。
helloディレクトリ配下でgo runして動かしてみる。

```ターミナル.
PS C:\Users\ryouta\Desktop\go\hello> go run .
Hi, Gladys. Welcome!
```

#チュートリアル：エラーを返して処理する（Return and handle an error）
[エラーを返して処理する](https://golang.org/doc/tutorial/handle-errors)
エラーを返却したときの動きを確認する。

上記で作ったgreetings.goに以下のようにする。

```greetings.go
package greetings

import(
	"errors"　//追加
	"fmt"
)

// Helloは、指定された人の挨拶を返します。
func Hello(name string) (string, error) {    //修正

	if name == "" {                           //追加
        return "", errors.New("empty name")   //追加
    }                                         //追加

    //メッセージに名前を埋め込む挨拶を返します。
    message := fmt.Sprintf("Hi, %v. Welcome!", name)
    return message, nil                               //編集
}
```
・errorsを読み込み。
・返り値にerrorを追加。
・if文でnameが空の時は空文字とエラーの何かを返す。
　→　goは複数の値を返却可能。ただし返却箇所が複数ある場合は返り値の個数は統一する必要がある。
・nilを追加
　→　返り値の二つ目でエラーがないことを表現している。つまりnilなら処理成功ということ。

次にHello.goを修正していく。

```hello.go
package main

import (
    "fmt"
    "example.com/greetings"
    "log"
)

func main() {
    // ログ出力の準備
    // ログのプレフィックスを設定
    log.SetPrefix("greetings: ")
    // ログの全設定（フラグ）を一括オフにして日付情報など出さないようにする。
    log.SetFlags(0)

    // greetings.Helloメソッド呼び出し
    message, err := greetings.Hello("")

    // エラーの場合はコンソールに出力して処理終了
    if err != nil {
        //log.fatal()と記載すると処理終了。
        log.Fatal(err)
    }

    // 正常の場合は、返り値をコンソールに出力
    fmt.Println(message)
}
```
実行すると想定通りエラーが発生する。

```ターミナル.
PS C:\Users\ryouta\Desktop\go\hello> go run .
greetings: empty name
exit status 1
```
引数のnameに値を入れてやるとエラーが発生せずに動く。

```ターミナル.
//  message, err := greetings.Hello("ルフィ")に変更
PS C:\Users\ryouta\Desktop\go\hello> go run .
Hi, ルフィ. Welcome!
```

#チュートリアル：ランダムな挨拶を返す（Return a random greeting）
[ランダムな挨拶を返す](https://golang.org/doc/tutorial/random-greeting)
配列のような複数の値を持てるスライスという型があって、とても便利なので使い方を学んでいく。
greetings.goに以下を記載する。
もろもろの説明はコメントに記載。

```greetings.go
package greetings

import(
	"errors"
	"fmt"
	"math/rand" //乱数の生成のため
    "time"
)

func Hello(name string) (string, error) {

	if name == "" {
        return "", errors.New("empty name")
    }
	
    //メッセージに名前を埋め込む挨拶を返します。randomFormatを呼ぶ。
	message := fmt.Sprintf(randomFormat(), name)
    return message, nil
}

// initメソッドはプログラム実行時に自動実行される。
func init() {
	// Seedメソッドで初期化する。
	// time.Now().UnixNano()を設定することで、毎回違う値が出力されるようになる。
	// Seedメソッドを実行しないと毎回同じ値になるので注意。
    rand.Seed(time.Now().UnixNano())
}

// メソッド名が小文字から始まると外部から呼べない内部専用メソッドとなる。
func randomFormat() string {
	// スライスの生成
    formats := []string{
        "Hi, %v. Welcome!",
        "Great to see you, %v!",
        "Hail, %v! Well met!",
    }

	// Intn()は引数の範囲でランダムな値を返却する
	return formats[rand.Intn(len(formats))]
}
```

hello.goは以下のように記載する。

```hello.go
package main

import (
    "fmt"
    "example.com/greetings"
    "log"
)

func main() {
    log.SetPrefix("greetings: ")
    log.SetFlags(0)

    // greetings.Helloメソッド呼び出し
    message, err := greetings.Hello("Gladys") //ここだけ修正

    if err != nil {
        log.Panic(err)
    }

    fmt.Println(message)
}
```
実行すると以下のようにランダムな値が出力される。

```ターミナル.
PS C:\Users\ryouta\Desktop\go\hello> go run .
Hi, Gladys. Welcome!
PS C:\Users\ryouta\Desktop\go\hello> go run .
Great to see you, Gladys!
PS C:\Users\ryouta\Desktop\go\hello> go run .
Hail, Gladys! Well met!
```

#チュートリアル：複数の人に挨拶を返す（Return greetings for multiple people）
[複数の人に挨拶を返す](https://golang.org/doc/tutorial/greetings-multiple-people)
for文の書き方、mapの使い方、引数がスライスの書き方を学んでいく。
具体的にはHellosという関数を作り、複数の人から挨拶された際の挙動をかいていく。

```greetings.go
package greetings

...省略...

// Hellosメソッドを追加
// namesは挨拶してきた人の名前が設定されている
// 返り値をmapで返す
func Hellos(names []string) (map[string]string, error) {
    // map生成
    messages := make(map[string]string)

	// 拡張for文
	// 構文
	// for [index(_で省略可)], [変数] := range スライス変数{}
    for _, name := range names {
        message, err := Hello(name)
        if err != nil {
            return nil, err
        }

		// 挨拶してきた人の名前をキーにvalueに挨拶をセット。
        messages[name] = message
    }
    return messages, nil
}

...省略...
```
次にhello.goを以下のようにかいていく。

```hello.go
package main

import (
    "fmt"
    "example.com/greetings"
    "log"
)

func main() {
    log.SetPrefix("greetings: ")
    log.SetFlags(0)

    // スライスで名前をセット。
    names := []string{"Gladys", "Samantha", "Darrin"} //追加
    
    // greetings.Helloメソッド呼び出し
    message, err := greetings.Hellos(names) //hello→hellosへ修正

    if err != nil {
        log.Panic(err)
    }

    fmt.Println(message)
}
```
実行する。

```コンソール.
PS C:\Users\ryouta\Desktop\go\hello> go run .
map[Darrin:Hail, Darrin! Well met! Gladys:Hail, Gladys! Well met! Samantha:Great to see you, Samantha!]
```

#終わりに
面白かったです。