Goクイズ Advent Calendar Day4

# はじめに
本記事は, [Goクイズ Advent Calender 2020](https://qiita.com/advent-calendar/2020/goquiz)の12/4用に書かれました。昨日は, @syumai さんの[Go Quiz Advent Calendar【3日目】](https://syumai.hateblo.jp/entry/go-quiz-2020-3)でした。

これから紹介するクイズは, 実際にアルバイト先で見つけたコードを一部改変したものです。

# 問題
以下のコードを実行すると, どのように出力されるでしょうか？
[Playground](https://play.golang.org/p/K4xfdTOZxVX)

```go
package main

import (
	"fmt"
)

type character string

func sayHello(name *character) {
	fmt.Printf("Hello, %s! ", *name)
}

func main() {
	names := []character{"Alice", "Bob", "Carol", "Dave", "Eve"}
	for _, name := range names {
		go sayHello(&name)
	}
}
```

1. (何も出力されない)
2. Hello, Alice! Hello, Bob! Hello, Carol! Hello, Dave! Hello, Eve!
3. Hello, Alice! Hello, Bob! Hello, Carol! Hello, Dave! Hello, Eve!(順不同)
4. Hello, Eve! Hello, Eve! Hello, Eve! Hello, Eve! Hello, Eve!

# 解答と解説
<details><summary>解答と解説</summary>
<div>
`全て起きる可能性がある`(申し訳ないです)

※2020/12/04追記: 
こちらのクイズには不備があり, 全ての選択肢が発生する可能性があります。作問ミスです。申し訳ありませんでした......
@kyoh86 さん, ご指摘ありがとうございます。

# TL;DR
 - 呼び出し元の関数が終了すると, 呼び出し元によって生成されたgoroutineは終了する
 - 対処法は, channelかsync.WaitGroupを用いること

# 解説
`go sayHello(name)`とあるので, goroutineに関する並行処理が絡んでいると気づいた方が多かったのではないのでしょうか？

解答でも書いた通り, 全ての答えが発生し得ます。
なぜなら, main関数の終了によって, 新しく生成されたgoroutineが終了しますが, そのタイミングや順番は規定されていないためです。

[Specification](https://golang.org/ref/spec#Go_statements)には下記のように書かれています。

> The function value and parameters are evaluated as usual in the calling goroutine, but unlike with a regular call, program execution does not wait for the invoked function to complete. Instead, the function begins executing independently in a new goroutine. When the function terminates, its goroutine also terminates. If the function has any return values, they are discarded when the function completes.

> 関数の値とパラメータは通常どおり呼び出し元のgoroutineで評価されますが, 通常の呼び出しとは異なり, プログラムの実行は呼び出された関数が完了することを待ちません。その代わり呼び出された関数は新しいgoroutineで独立して実行を始めます。もし, 呼び出し元の関数が終了すると呼び出されたgoroutineも終了します。その関数がいかなる返り値を持とうと関数の終了時に破棄されます。

図で書くと, 以下の通りです。
![flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238364/57dd49d1-eefb-6ea1-ef8e-8fafa508c0af.png)

これを起こさないために, 下記の通り`sync.WaitGroup()`や`channel`を用いて非同期処理が終わるまである程度待つなどの対策が出来ます。

[sync.WaitGroupを使う例](https://play.golang.org/p/Aki32evMXR2)

```go
package main

import (
	"fmt"
	"sync"
)

type character string

func sayHello(name *character, wg *sync.WaitGroup) {
	defer func() { wg.Done() }()
	fmt.Printf("Hello, %s! ", *name)
}

func main() {
	names := []character{"Alice", "Bob", "Carol", "Dave", "Eve"}

	wg := &sync.WaitGroup{}
	for _, name := range names {
		wg.Add(1)
		go sayHello(&name, wg)
	}
	wg.Wait()
}
```

[channelを使う例](https://play.golang.org/p/ubT439nRpVq)

```go
package main

import (
	"fmt"
)

type character string

func sayHello(name *character, quit chan bool) {
	fmt.Printf("Hello, %s! ", *name)
	quit <- true
}

func main() {
	names := []character{"Alice", "Bob", "Carol", "Dave", "Eve"}

	quit := make(chan bool)
	for _, name := range names {
		go sayHello(&name, quit)
		// こちらにquitを書くと, 必ずAlice, Bob, Carol, Dave, Eveの順で出力される
		// <-quit
	}
	for _, _ = range names {
		<-quit
	}
}

```

</div>
</details>

# おわりに
こちらのコードはスクレイピング用のコードに潜んでいたバグでした。何度実行しても想定通りに動かないので, 何度か実行していた時に問題に気づけました。

以上で, Goクイズは終わりです。
ここまでお付き合いいただきありがとうございました！

明日は, @hajimehoshi さんのGo Quizです。

ありがとうございました！

[^1]: 外部のGUI操作を必要とするハンドラのテストコードが書けなくて困っているのは別の話......