Go を一緒に学んでいるときに質問されやすいところ3セット

こんにちは。
Women Who Go Tokyo の [micchie](https://twitter.com/micchiebear) と申します。
[Go4 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/go4) の22日目の記事を担当します。

Women Who Go Tokyo は、毎月1回 Go についての勉強会を行っています。
Go にはじめて触れる方にも安心して楽しく参加していただけるよう、少しずつ改良を重ねながら運営しています。

最近は「Go のことを学びながらやっていたら、いつのまにか家計簿アプリができていた！すごい！」を体験していただけるように「家計簿アプリを作ろう！」というタイトルやっています。
教材を提供してくださっている @tenntenn さん、いつもありがとうございます。

今日のアドベントカレンダーでは、質問をもらうことが多い箇所について紹介したいと思います。
今後の勉強会の助けになりますように。

説明が怪しいところは是非、正していただけますと幸いです。

※ ちなみに、すでに大変良い形でまとめてくださっている素敵なブログが、たくさんあります！

## 1. ポインタがわからない
ポインタとは、データ本体のアドレスを指すものです。
あまり良い例ではありませんが「上野動物園のクマ舎にいるマレーグマ」を想像してみてください。

	- データ本体: マレーグマ
	- アドレス: 上野動物園のクマ舎

bear という変数に “マレーグマ” を格納します。

```
var bear string
bear = "マレーグマ"
```

bear は “マレーグマ” の本体です。

```
fmt.Printf("%s\n", bear)
fmt.Printf("%p\n", &bear)
```

上記を実行すると、データ本体とそのアドレスを出力できます。

```
bearのデータ本体: マレーグマ
bearのアドレス: 0x40c138
```

`0x40c138` が「上野動物園のクマ舎」を表す「アドレス」です。
`&` をつかってデータ本体からそのアドレスを取り出しています。

次に、b に bear を代入します。

```
b := bear
```

代入は値のコピーです。

```
fmt.Printf("bのデータ本体: %s\n", b)
fmt.Printf("bのアドレス: %p\n", &b)
```

上記を実行すると、

```
bのデータ本体: マレーグマ
bのアドレス: 0x40c150
```

このようになります。
データ本体は同じに見えますが、アドレスが bear と b は異なります。
マレーグマが `0x40c150` という「上野動物園のクマ舎 (2)」にコピーされたということになります。(しかしクマがコピーされるなんて…物理的にはありえない…)

今度は p というポインタ変数を作って、bear のポインタを代入します。

```
var p *string
p = &bear
```

`*string` は string のポインタ型です。 

```
fmt.Printf("pのデータ本体: %s\n", *p)
fmt.Printf("pのアドレス: %p\n", p)
```

上記を実行すると、

```
pのデータ本体: マレーグマ
pのアドレス: 0x40c138
```

このようになります。
ポインタ変数に `*` を付与することで、そのデータ本体を取り出すことができます。
また、bear のアドレスを代入しているので、同じアドレスとなります。

[The Go Playground](https://play.golang.org/p/YaYk7CtMD5W)


## 2. 配列とスライスがむずかしい
Go には配列とスライスがあります。この時点で「スライスとは？」みたいな気持ちになる人も少なくはないようです。

### 配列を作る

配列は、同じ型のデータを集めたもの、スライスは配列の一部となります。

まずは、長さが 3 の string 型の配列を作成します。

```
array := [3]string{"シロクマ", "ツキノワグマ", "アメリカクロクマ"}
```

配列は、長さも含めて一つの型となります。
そのため、要素を追加したい場合は、その長さにあった配列を作り直さなければなりません。

```
array[0] = "メガネグマ"
array[1] = "メガネグマ"
array[2] = "メガネグマ"
```

このように値の書き換えはできますが、長さを超えると、

```
array[3] = "メガネグマ"
```

エラーとなります。

```
invalid array index 3 (out of bounds for 3-element array)
```


### スライスを作る 
スライスを作ります。

```
slice := []string{"シロクマ", "ツキノワグマ", "アメリカクロクマ"}
```

一見、上の配列と同じように見えますが、実態は下記のようになっています。

	1. 長さが 3 の string 型の配列が作られる。
	2. 1. を参照したスライスが作られる。

このようにスライスを作ることもできます。
この `make` の段階では、各要素はスライスの型のゼロ値 (この場合は string なので空文字) が格納されます。

```
slice := make([]string, 3)
slice[0] = "シロクマ"
slice[1] = "ツキノワグマ"
slice[2] = "アメリカクロクマ"
```


### スライスから取り出す

スライスの値は範囲を指定して取り出すことができます。

```
// slice全体: [シロクマ ツキノワグマ アメリカクロクマ]
fmt.Printf("slice全体: %v\n", slice)

// 最初〜最後まで: [シロクマ ツキノワグマ アメリカクロクマ]
fmt.Printf("最初〜最後まで: %v\n", slice[:])

// インデックス0〜最後まで: [シロクマ ツキノワグマ アメリカクロクマ]
fmt.Printf("インデックス0〜最後まで: %v\n", slice[0:])

// インデックス1〜最後まで: [ツキノワグマ アメリカクロクマ]
fmt.Printf("インデックス1〜最後まで: %v\n", slice[1:])

// インデックス2〜最後まで: [アメリカクロクマ]
fmt.Printf("インデックス2〜最後まで: %v\n", slice[2:])

// 最初〜インデックス1-1=0まで: [シロクマ]
fmt.Printf("最初〜インデックス1-1=0まで: %v\n", slice[:1])

// インデックス1〜インデックス1-1=0まで: []
fmt.Printf("インデックス1〜インデックス1-1=0まで: %v\n", slice[1:1])

// インデックス1〜インデックス2-1=1まで: [ツキノワグマ]
fmt.Printf("インデックス1〜インデックス2-1=1まで: %v\n", slice[1:2])

// インデックス1〜インデックス3-1=2まで: [ツキノワグマ アメリカクロクマ]
fmt.Printf("インデックス1〜インデックス3-1=2まで: %v\n", slice[1:3])

// panic: runtime error: slice bounds out of range [5:3]
fmt.Printf("インデックス5〜最後まで: %v\n", slice[5:])
```

### スライスに追加する

スライスは値の追加ができますが、長さ以上の要素を直接入れようとすると、エラーになります。

```
slice[3] = "メガネグマ"
.
.
.
panic: runtime error: index out of range [3] with length 3
```

しかしスライスは、配列と違って長さを持たないため、`append` を利用して要素の追加ができます。

```
slice = append(slice, "メガネグマ")
```


### スライスの cap と len

スライスには `cap` という容量を表すものと、`len` という長さを表すものがあります。
下記は、cap と len を指定しながらスライスを作った例です。

```
slice := make([]string, 3, 3)
fmt.Println(slice, len(slice), cap(slice))
```

	1. 長さが 3 の string 型の配列が作られる。
	2. 1. を参照したスライスが作られる。

このときの 1. のサイズが `cap`、配列に入っている要素の数が `len` です。
要素は string のゼロ値が入っています。

```
[  ] 3 3
```

ここで、要素をいくつか足します。

```
slice = append(slice, "アメリカクロクマ")
fmt.Println(slice, len(slice), cap(slice))

slice = append(slice, "ツキノワグマ")
fmt.Println(slice, len(slice), cap(slice))

slice = append(slice, "ホッキョクグマ")
fmt.Println(slice, len(slice), cap(slice))

slice = append(slice, "マレーグマ")
fmt.Println(slice, len(slice), cap(slice))
```

最初に3つ、string のゼロ値である空文字が格納されたあとに 1 つ追加したため、`len` は 4 です。

```
[   アメリカクロクマ] 4 6
[   アメリカクロクマ ツキノワグマ] 5 6
[   アメリカクロクマ ツキノワグマ ホッキョクグマ] 6 6
[   アメリカクロクマ ツキノワグマ ホッキョクグマ マレーグマ] 7 12
```

`cap` に注目してください。
`append` を行ったときに、`cap` よりも大きくなってしまう場合 ` cap` のメモリを拡張するのですが、拡張前の `cap` の倍を確保しに行こうとしています。

	- アメリカクロクマを追加する前は、cap が  3 で、 6 に拡張。
	- マレーグマを追加する前は、cap が 6 で、12 に拡張。

ある程度の値が増えることがわかっている場合は、最初からスライスの `cap` を指定することで、メモリの効率を考慮した書き方ができます。


## 3. Range が期待をうらぎる
Go ではスライスのループを利用することがよくあります。
たとえば、下記のようなスライスを作成して、順番にクマの名前とアドレスを表示するプログラムを書きます。

```
bears := []string{"シロクマ", "ツキノワグマ", "アメリカクロクマ"}

for _, bear := range bears {
	fmt.Printf("range bearのデータ本体: %s\n", bear)
	fmt.Printf("range bearのアドレス: %p\n\n", &bear)
}
```

上記を実行すると、

```
range bearのデータ本体: シロクマ
range bearのアドレス: 0x40c138

range bearのデータ本体: ツキノワグマ
range bearのアドレス: 0x40c138

range bearのデータ本体: アメリカクロクマ
range bearのアドレス: 0x40c138
```

このようになります。何か気づきませんか…?
同じアドレスです。値のコピーは作成されないのです。

これに気づかない状態で、サクッと並列化します。

```
for _, bear := range bears {
	go func() {
		fmt.Printf("bearのデータ本体: %s\n", bear)
		fmt.Printf("bearのアドレス: %p\n\n", &bear)
	}()
}
time.Sleep(1 * time.Second)
```

上記を実行すると、

```
./prog.go:12:47: loop variable bear captured by func literal
./prog.go:13:47: loop variable bear captured by func literal
Go vet exited.

bearのデータ本体: アメリカクロクマ
bearのアドレス: 0x40c138

bearのデータ本体: アメリカクロクマ
bearのアドレス: 0x40c138

bearのデータ本体: アメリカクロクマ
bearのアドレス: 0x40c138
```

期待した結果になりますでしょうか。期待から外れています。
ループ変数である bear は各ループで変更されるため、goroutine は bears の最後の値を利用します。

これを回避するには、下記のようにパラメータで渡したり、値をコピーすると良いでしょう。

```
for _, bear := range bears {
	go func(bear string) {
		fmt.Printf("bearのデータ本体: %s\n", bear)
		fmt.Printf("bearのアドレス: %p\n\n", &bear)
	}(bear)
}
```


```
for _, bear := range bears {
	bear := bear
	go func() {
		fmt.Printf("bearのデータ本体: %s\n", bear)
		fmt.Printf("bearのアドレス: %p\n\n", &bear)
	}()
}
```

[The Go Playground](https://play.golang.org/p/zyCR6mJVpA2)

## 最後に
毎月開催されている Women Who Go Tokyo ですが、色んな人に Go を楽しんでもらいたいと思っています。
こちらの記事 [Women Who Go Tokyo という取り組みでわたしたちは。｜micchie｜note](https://note.com/micchiebear/n/naab2adc0124c) でも紹介しているので、興味のある方はぜひ、覗いてみてください。

※ 本当はレシーバの話も書きたかった…間に合わなかった…