初心者視点で読むGoチュートリアル

## はじめに
　個人開発でバックエンドにGoを使いたかったので、Goの勉強を始めました。
　疑問をその都度解決していく勉強法なため、横道にそれまくりますが、Goを勉強し始目た時に同じ壁にぶつかった人の助けになればと思っております。コードは[本家様](https://go.dev/doc/tutorial/z)をご参照ください。
 
## Goとは
　Goとは、Googleがサポートするオープンソースのプログラミング言語です。
## 環境構築編
　早速環境構築を始めます。
　まず、[Go公式](https://go.dev/dl/)からGoのインストーラーをダウンロードします。[homebrew](https://brew.sh/ja/)を使わないのは`brew upgrade`で意図せずバージョンアップさせそうだからです。
　インスターラーでのインストールが完了したら、[.zshrc](https://qiita.com/siakio/items/f9ab8a02d7b7fa6963ba)に下記(パス)を記述します。
```
export PATH=$PATH:/usr/local/go/bin
```
　次は、Goのバージョンを確認します。
```zsh
% go version
go version go1.22.2 darwin/arm64
```
　問題なくインストールされていますね。
　ついでに、VScodeに[Goの拡張機能をインストール](https://qiita.com/melty_go/items/c977ba594efcffc8b567)します。
## チュートリアル　Get started with Go
　環境構築が終わったら、Go公式の[チュートリアル](https://go.dev/doc/tutorial/getting-started)に取り組みます。
```zsh
# 適当なディレクトリに移動
% cd example
# 作業ディレクトリの作成
% mkdir hello
# 作業ディレクトリに移動
% cd hello
```

```zsh
# チュートリアル用のファイルを作成
% touch hello.go
# hello.goを編集
% vim hello.go
# go.modファイルを生成
% go mod init example/hello
go: creating new go.mod: module example/hello
```
### 疑問１： go.modって何？
　[go.modファイルは使用されるモジュールの依存関係を管理するファイルです。](https://qiita.com/soicchi/items/2637a9195e64fdc73609)とのこと。つまり、Pythonでいう`pip install requirements.txt`の`requirements.txt`にあたるファイルのことです。
※go.modはgo本体のバージョンや設定を含むため、厳密には違います。また、**go.modはGOPATHの外側で開発を行う際には必須なファイルなので、絶対作成しましょう。**
<br><br>

　結論が出たところでチュートリアルに戻ります。

```hello.go
package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}
```
　goは`go run`で実行できます。
```
% go run hello.go
Hello, World!
```
　恒例の"Hello,World"はうまくいきました。ですが、ここで一つ疑問が生まれます。`package main`とは何でしょうか？
### 疑問２ ： packageとは？
　パッケージはGoのコードをまとめる基本単位です。Goでは全てのコードがパッケージの中に含まれています。
　以下のようにパッケージを読み込むことで、他パッケージ内にある識別子(関数,変数,etc)にアクセスすることができます。　この時、以下の要件を満たすようにします。
- **mainパッケージとmain()を準備する**
    - Goではリポジトリ内に必ずmainパッケージが存在し、その中にmain()が必要です。このmian()がエントリーポイントになるからです。
- **パッケージはディレクトリの中に入れる**
    - Goのコードはパッケージで纏められており、同一のパッケージはディレクトリで纏めて管理します。
- **パッケージ名とファイル名は同一のものにする**
    - 理由は後述します。
- **外部で使用する識別子の頭は大文字**
    - Goは頭文字が大文字であることをキーにエクスポートします。
```main.go
package main

import {
    "fmt"
    "/test/print"
}

func main(){
    fmt.Println(hello.PrintHello("World"))
}

```
```print.go
package hello

func PrintHello(name string) string{
    return fmt.Sprintf("Hello, %s!!",name)
}

```
```bash
% go run main.go
Hello, World!!
```

　`import　{path}`で他パッケージを読み込むことができます。しかし、実際に使用する際はファイル名ではなくパッケージ名が使用されるため、ファイル名とパッケージ名は同一のものにすることが推奨されています。よって上記のコードは推奨されていない書き方と言えます。
<br>

　ここまでパッケージの話をしてきましたが、多くのソースコードで以下のような記述を見かけます。
```
import (
  "fmt"

  "github.com/{}/{}"
)
```
　私は思いました。なぜ、ここでgithub?

### 疑問３ ： packageに現れるgithub
　Go言語ではgithubといったリモートからパッケージをインポート使用することが、しばしばあります。他者が作成したパッケージを気軽に使用できるのは利点かもしれません。しかし、疑問が残ります。
 　**別にローカルにコピーして使っても良いのではなだろうか？**
　特に、開発中に自分で作ったパッケージをgithubからインポートする意味がわかりません。「同じものローカルにあるじゃん！」叫びました。
 　しかし、物事には理由があるわけで、これにもしっかり理由がありました。
- **理由１：常に最新版のパッケージを使用できる**
    - チーム開発を行う際、誰かがgithubのパッケージを更新したとします。そのことに自分が気がついていなくてもビルドした際に最新版がgithubからインポートされるため、問題なく開発を行えます。
- **理由２：パッケージをバージョン管理できる**
    - githubで管理するということはパッケージもバージョン管理の恩恵を受けることができます。つまりバージョンを指定して過去のパッケージを扱うことができます。

　他にもありそうですが、私が納得できたのは以上二つです。
## チュートリアル　Get started with Go(2)
　だいぶ話が逸れてしましたが、チュートリアルに話を戻します。
　次は先程触れた外部パッケージの活用に挑戦します。
 　外部パッケージは[ここ](https://pkg.go.dev/)で検索でき、今回はrsc.io/quoteモジュールに含まれるquote パッケージを使用します。

　main.goを作成したら、整合性を確認します。ここで使用する`% go mod tidy`とは、ソースファイルを解析して必要なライブラリをダウンロードしたり不要になったファイルを削除してくれたりするコマンドです。
```bash
% 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.mod`を見ると内容が更新されています。
```go.mod
module example/hello

go 1.22.2

require rsc.io/quote v1.5.2

require (
	golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect
	rsc.io/sampler v1.3.0 // indirect
)
```bash

```
しかし、ここで一つ知らないファイルが増えていることに気がつきます。
```bash
% ls
go.mod  go.sum  main.go
```
　確かに、[チュートリアル](https://go.dev/doc/tutorial/getting-started)には"Goはquoteモジュールを要件として追加し、モジュールを認証するために使用する go.sum ファイルも追加します。"という記述がありました。ですが、よくわかりません。
 
### 疑問４ ： go.sumとは？
　go.sumとは**外部モジュールの不正な改竄を検出するためのハッシュ値が入ったファイル**です。このファイルのおかげで、もし外部モジュールが不正に改竄されても`go mod tidy`時効時に異常を検出することができます。
　※詳しくは[こちら](https://blog.serverworks.co.jp/go-module)

## チュートリアル　Create a Go module

### [Start a module that others can use](https://go.dev/doc/tutorial/create-module)
　Goのコードはパッケージにグループ化され、パッケージはモジュールにグループ化されます。この章ではモジュールを作成して活用する練習を行います。
 ```bash
% mkdir greetings
% cd greetings
% go mod init example.com/greetings
go: creating new go.mod: module example.com/greetings
% touch greetings.go
 ```
　チュートリアルではexample.comとなっていますが、`example.com`の箇所は自分のgituhubリポジトリ(モジュールがダウンロード可能なリポジトリ)に変えました。
　greetings.goではgreetingsパッケージで"Hi," +name+"Welcome!"を出力するHell関数を定義しています。変数messageは:=を使用することで型を宣言していませんが、型を宣言する場合は以下のように記述します。
 ```greetings.go
 var message string
 message = fmt.Sprintf("Hi, %v. Welcome!", name)
 ```
 　関数は`func 関数名(引数 型) 戻りの型{return 戻り値}`で定義されます。
### [Call your code from another module](https://go.dev/doc/tutorial/call-module-code)
 "Create a Go module"では呼び出すgreetings.goを作成しました。
 次はそれを呼び出すためのhellパッケージを作っていきます。
 ```
% mkdir hello
% cd hello
% go mod init example.com/hello
go: creating new go.mod: module example.com/hello
 ```
　しかし、これらのモジュールはまだgithubにpushしていないので、アクセスすることができません。そこで以下のコマンドを使用して、モジュールが見つからない際にローカルへリダイレクトするように設定します。
 ```bash
 % go mod edit -replace example.com/greetings=../greetings
 ```
　さらに、まだ存在しませんが、main.goにgreetingsが必要である依存関係を記述します。この時、go.mod内の`require`に`v0.0.0-00010101000000-000000000000`という記述が追加されますが、これはバージョンがない際に使われる疑似番号です。
```bash
% go mod tidy 
```
問題なく実行することもできました。
```bash
% go run main.go
Hi, Gladys. Welcome!
 ```
### [Return and handle an error](https://go.dev/doc/tutorial/handle-errors)
　エラーの扱いについてです。コードを`greetings.go`にコピーします。コードには`name`が空である際にエラーを返す機能が追加されています。
　実際、実行してみたところ、エラーが出力されました。
```
% go run .
greetings: empty name
exit status 1
```

　ちなみに、ここで`errors.New("empty name")`の代わりに使われている`nil`は`null`のことです。

 ## [Return a random greeting](https://go.dev/doc/tutorial/random-greeting)
　ここではGoのランダム関数とフォーマットに触れます。
　まず、`greetings.go`を以下のように書き換えます。
　Goはpythonでいうformat関数のようなものを使わなくても変数を代入できるのですね。驚きです。
　コードを実行してみると、毎回異なる出力が得られます。
```bash
% go run main.go
Great to see you, world!
% go run main.go
Hi, world. Welcome!
```
### Return greetings for multiple people

　ここではマップとスライスを活用して複数の要素に対してHello関数の処理を実行します。
　実行すると、挨拶文のマップを出力として得られました。
```bash
% go run main.go
map[Darrin:Hail, Darrin! Well met! Gladys:Hail, Gladys! Well met! Samantha:Hail, Samantha! Well met!]
```
### mapとslice
　ここで登場した**map**、**slice**について少し触れます。
　**mapとは**
　　mapとはPythonでいう辞書にあたる要素です。
  　　`m := make(map[string]string, 100)`
　　のようにmakeを使って型とサイズを指定して初期化できます。
　**sliceとは**
　　sliceとはPythonでいうリストにあたる要素です。
  　　`s := make([]int, 5, 10)`
　　のようにmakeを使って型とサイズを指定して初期化できます。
### [Add a test](https://go.dev/doc/tutorial/add-a-test)
　次はテストの実装を行います。Goでは、ファイル名を`_test.go`で終わらせるとことで、`go test`コマンドがそのファイルをテスト関数が含まれるファイルとして認識してくれます。
　まず、greetings関数のtestを作成します。テスト関数はTestから始まる名前を持ち、`testing.T`型のポインタをパラメータとして持ちます。
　一つ目のテスト、`TestHelloName`はHello関数が有効な応答を返すことができるかのテストを行います。二つ目のテスト、`TestHelloEmpty`はHello関数にからの文字列が渡された際にエラーハンドリングが正しく機能するかを検証します。
 　実際に`go test`コマンドを実行してみます。
```
% go test
PASS
ok      github.com/{}/greetings    0.379s
% go test -v
=== RUN   TestHelloName
--- PASS: TestHelloName (0.00s)
=== RUN   TestHelloEmpty
--- PASS: TestHelloEmpty (0.00s)
PASS
ok      github.com/{}/greetings    0.352s
```
　test関数は異常を検出しませんでした。