Go初学者が学んだことまとめ〜その２〜（参照型）

#はじめに
こちらの続きです。
[Go初学者が学んだことまとめ〜その１〜（構成と実行、構文）](https://qiita.com/haru_iwamoto/items/87d9cf6ff5219b5c3a5a)

今回は参照型についてです。

#参照型
##スライス
###定義
可変長配列を表現する型。
下記のように定義できる。

```Go
var s []int
```
```Go
s := make([]int, 5) //[0, 0, 0, 0, 0]
```
```Go
s := []int{1, 2, 3} //[1, 2, 3]
```

###要素数と容量
スライスには要素数と容量がある。
要素数はlen,容量はcapで取得できる。

```Go
//makeの第2引数が要素数、第３引数が容量
s := make([]int, 5, 10)
fmt.Println(len(s)) //5
fmt.Println(cap(s)) //10
```
容量とは、メモリ上に確保する領域のこと。
要素数を拡張していく際に、新たな領域の確保が不要になるというメリットがある。

###append
配列の拡張ができる。

```Go
s := []int{1, 2, 3} 
s = append(s, 4) // s == [1, 2, 3, 4]
s = append(s, 5, 6, 7) // s == [1, 2, 3, 4, 5, 6, 7]
```
拡張される容量は、要素数と同じではない。
ランタイムが自動で拡張している。
どの程度拡張されるかは、Goのランタイムに依存する。

```Go
s := []int{1, 2, 3}
fmt.Println(cap(s)) //3
s = append(s, 4)
fmt.Println(cap(s)) //6
s = append(s, 5, 6, 7)
fmt.Println(cap(s)) //12
```

appendした時は、自動拡張するかどうかわからないということになる。
つまりこういうこと。

```Go
s1 := append(s0, x) //s0とs1が同じメモリ領域であるかは不明
```
なので、自動拡張の有無を意識しないといけないコードは基本NG。

###簡易スライス式
配列やスライスから、新たなスライスを生成する。
インデックスの範囲を[m:n]の形式で指定する。

```Go
a := [5]int{1, 2, 3 ,4, 5}
s := a[0:2] //s == [1, 2] sは[]int型
```


###完全スライス式
3つのパラメータを取る記法。

```Go
a[low:high:max] //0 <= low <= high <= max <= cap(a) という関係を満たす必要がある
```
maxという値で容量をコントロールできる。

```Go
a := [10]int{1, 2, 3 ,4, 5, 6, 7, 8, 9, 10}
s1 := a[2:4]
fmt.Println(cap(s1)) // 8 //len(a)-low が容量になる

s2 := a[2:4:4]
fmt.Println(cap(s2)) // 2 //max-low が容量になる

s3 := a[2:4:6]
fmt.Println(cap(s3)) // 4 //max-low が容量になる
```

「容量」という概念が非常にややこしいので注意。

###配列とスライスの引数
スライスは「参照型」であるので、スライスの配列要素の参照（ポインタ）を渡すことになる（詳細は後述）。
それに対して配列は、関数側に配列のコピーされたものが渡される。
下記は動作の違いを示したもの。

```Go:Go
func powArray(a [3]int) {
	for i, v := range a {
		a[i] = v * v
	}
}

func powSlice(s []int) {
	for i, v := range s {
		s[i] = v * v
	}
}

func main() {
	a := [3]int{1, 2, 3} 
	powArray(a) //コピーされたものが関数に渡るのでaは変更されない。
	fmt.Println(a) //[1, 2, 3]

	s := []int{1, 2, 3}
	powSlice(s) //関数はsの配列要素と同一のメモリ領域を使用するためsは変更される。
	fmt.Println(s) //[1, 4, 9]
}
```

ちなみに、JavaScript(TypeScript)の配列は参照渡し。
プリミティブ型(number,string,boolean)は値渡し、それ以外は参照渡しになる。

```typescript:TypeScript
function pow(a: number[]): void {
    for(let i = 0; i < a.length; i++) {
        a[i] = a[i] * a[i];
    }
}
const ary = [1, 2, 3];
pow(ary);
console.log(ary); //[1, 4, 9];
```

####さらに深堀すると・・・
スライスの内部構造は 

* 配列へのポインタ
* 要素数 (length)
* 容量 (capacity)

の３つからなる。
関数の引数にスライスを渡した場合、上記の３つが値渡しされている。
下記はそれを示す例。

```Go
func add(s []int) {
	s = append(s, 1)       // s => ptr = 0xc0000180a0, len = 1, cap = 10
	fmt.Printf("%p\n", s)  // 0xc0000180a0 配列のアドレスは呼び出し元と同じ
	fmt.Printf("%p\n", &s) // 0xc00000a0a0 スライス自体のアドレスは呼び出し元と違う
	fmt.Println(s) //[1]
}

func main() {
	s := make([]int, 0, 10) // s => ptr = 0xc0000180a0, len = 0, cap = 10
	fmt.Printf("%p\n", s)   // 0xc0000180a0 配列のアドレス
	fmt.Printf("%p\n", &s)  // 0xc00000a060 スライス自体のアドレス
	add(s)
	fmt.Println(s) // [] len = 0なので出力は[]になる
}
```
appendすると参照元のスライスは書き換わるように見えるが、実際の出力は書き換わっていないように見える。
これは、スライスの要素数は値渡しのため、引き渡した関数側でappendして要素数を書き換えても、参照元には反映されないためである。

##マップ
###定義
連想配列を表現する型。
下記のように定義できる。

```Go
var m map[int]string
```
```Go
m := make(map[int]string)
```
```Go
m := map[int]string{1: "Taro", 2: "Hanako", 3: "Jiro"}
```

特別な使い方はないので、省略。

##チャネル
ゴルーチンとゴルーチンの間でデータを受け渡しするためにデザインされたGo特有のデータ構造。
###定義
```Go
var ch chan int     //双方向
var ch1 <-chan int  //受信専用
var ch2 chan<- int  //送信専用
```
```Go
ch := make(chan int, 8) //バッファサイズ8
```
チャネルはFIFO(先入先出し)のキュー。
バッファサイズとはこのキューのサイズ。

###使い方
チャネルは必ずゴルーチンと一緒に使う。
下記は0〜10000をゴルーチン間で受け渡し、出力するプログラム。

```Go
package main

import "fmt"

func receiver(ch <-chan int) {
	for {
		i, ok := <-ch
		if ok == false {
			//受信できなくなったら終了
			break
		}
		fmt.Println(i)
	}
}

func main() {
	ch := make(chan int)
	go receiver(ch)

	i := 0
	for i <= 10000 {
		ch <- i
		i++
	}
	//closeするとi, ok := <-chのokがfalseになる
	close(ch)
}
```

###select
複数のチャネルをコントロールする構文。
例えば

```Go
e1 := <- ch
e2 := <- ch
```
というコードで、変数ch1のチャネルからデータが受信できない場合、このゴルーチンは停止したままになり、ch2はいつまでたっても受信できない。
このように、1つの処理で複数のチャネルを処理できない問題に対処できるのがselect。
ゴルーチンを停止させることなく、全ての処理を継続させることができる。
このように書く。

```Go
package main

import "fmt"

func main() {
	ch1 := make(chan int)
	ch2 := make(chan int)
	ch3 := make(chan int)

	go func() {
		for {
			i := <- ch1
			ch2 <- (i * 2)
		}
	}()

	go func() {
		for {
			i := <- ch2
			ch3 <- (i - 1)
		}
	}()

	n := 1
LOOP:
	for {
		//処理が継続できるcaseの中からランダムに選択される
		//全て処理が継続できない場合はdefaultが選択される
		select {
		case ch1 <- n:
			n++
		case i := <- ch3:
			fmt.Println("received", i)
		default:
			if n > 100 {
				break LOOP
			}
		}
	}
}
```
非同期処理のデータ送受信がここまで簡単にできるのがGoの特徴。

##Goは全て値渡し
[When are function parameters passed by value?](https://golang.org/doc/faq#pass_by_value)
> everything in Go is passed by value

公式がおっしゃる通り、Goは全て値渡しとのことです。
Goで参照渡しという言葉を使うのは誤解を招くのでやめましょう。
（この記事も当初は参照渡しという言葉を使っていましたが、ご指摘により変更しました。）

#まとめ
参照型についてまとめました。
スライスや、チャネルを使ったselectなどは少し難易度が上がりました。
selectをきちんと理解できれば、Go初心者は卒業できるのかなと思います。
[その３](https://qiita.com/haru_iwamoto/items/cd36429b3c4fb1627fdf)に続きます。

#参考
[スターティングGo言語](https://www.amazon.co.jp/%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0Go%E8%A8%80%E8%AA%9E-%E6%9D%BE%E5%B0%BE%E6%84%9B%E8%B3%80-ebook/dp/B01FH3KRTI)

#関連
[Go初学者が学んだことまとめ〜その１〜（構成と実行、構文）](https://qiita.com/haru_iwamoto/items/87d9cf6ff5219b5c3a5a)
[Go初学者が学んだことまとめ〜その３〜（構造体、インターフェース）](https://qiita.com/haru_iwamoto/items/cd36429b3c4fb1627fdf)
[Go初学者が学んだことまとめ〜その４〜（コマンド、パッケージ）](https://qiita.com/haru_iwamoto/items/fc248d18c709b2642ad7)