Go言語 チートシート

# 概要

Go言語をおさらいする必要があったので、[公式](https://go-tour-jp.appspot.com/list) で勉強しながらチートシートを作りました。
間違いや情報が古い等ありましたらコメント頂けると幸いです。

# 内容

A Tour of Go の順番で作った。

- パッケージ
- 変数
- 関数
- 基本制御構文
- 遅延処理
- ポインタ
- 構造体
- 配列
- スライス
- マップ
- メソッド
- インタフェース
- エラー
- その他標準ライブラリ
- 非同期処理

# パッケージ

Go のプログラムは package で構成される。
プログラムは main パッケージから開始される。
`fmt` 等は標準のパッケージ、import で読み込む。

# 変数

```golang
// 通常の宣言
var x int

// 型名省略可能
var x, y = 1, "true"

// 暗黙的な型宣言
x := 1

// 定数
const X = 1
```

# 関数

一般的な言語の関数と変わらない。
ただし、Goの関数はクロージャの為、それ自信の外部から変数を参照する。

```golang
// 構文
func 関数名(変数名 型名, 変数名 型名) 返り値型名 { }

// 大文字で始まる関数はパッケージ外からアクセス可能
func Add(x int, y int) int { }

// 小文字で始まる関数はパッケージ外からアクセス不可
func add(x int, y int) int { }

// 引数の型が同じ場合はまとめる
func add(x, y int) int { }

// 関数値
f := func(x, y int) int { }
f(1, 2)
```

# 基本制御構文

```golang
// 繰り返し
for i := 0 ; i < 10 ; i++ { }

// while
for i < 10 { }

// 無限ループ
for { }

// 分岐
if x < 0 { }

// ifのショートステートメント
if v := add(x, y); v < 10 { }

// switch 
switch v := add(x, y); v {
case 1:
  // 処理 (breakは必要無い)
default:
  // 処理
}

// 条件の無いswitch
switch {
case v < 10:
  // 処理
}
```

# 遅延処理

`defer` を使用。
defer が付いている関数呼び出しは関数の return 時に行われる。
ただし、引数の評価はすぐに行われる。

```golang
defer add(x, y)
```

# ポインタ

Goはポインタを扱い、ポインタは値のアドレスを指す。
ゼロ値のポインタは nil になる。

```golang
// ポインタ
var p *int

// &演算子 (ポインタを引き出す)
i := 42
p = &i

// *演算子 (ポインタの指す変数を示す)
fmt.Println(*p) // 42
*p = 21 // ポインタpを通して変数iに代入
```

# 構造体

構造体はフィールドの集合である。
先頭大文字、小文字のルールは関数と同じ。

```golang
// 構造体の定義
type Point struct {
  X int
  Y int
}

// 構造体の初期化
p := Point{1, 2}

// フィールドを指定して初期化
p := Point{X: 1, Y: 2}

// 省略して初期化 (ゼロ値が入る)
p := Point{X: 1}
p := Point{}

// ポインタを返して初期化
v := &Point{1, 2}

// フィールドへアクセス
p.X = 10

// ポインタを通してアクセス
v := &p
(*v).X = 15 
v.X = 5 // *演算子は省略可能
```

# 配列

配列の長さは型の一部分であるため、サイズを変えることは出来ない。

```golang
// 宣言
var a [3]int

// 初期化
a := [3]int{1,2,3}

// アクセス
a[0] = 1
```

# スライス

配列は固定長だが、スライスは可変長。
Goでは配列よりもスライスの方が一般的。

スライスは配列への参照のようなもので、実際にデータを持っていない。
スライスは配列の部分列を指し示し、スライスの変更は配列にも影響する。

```golang
// 宣言
var s []int

// 既存の配列からスライスを作る
var s []int = a[1:4] // 1番目から3番目の要素で作る

// スライスの省略
var s[]int = a[:4] // 0番目から3番目
var s[]int = a[1:] // 1番目から最後尾まで
var s[]int = a[:] // 0番目から最後尾まで

// 新規の配列からスライスのみを取得する
s := []int{1, 2, 3}

// 長さ (スライスの要素数) の取得
len(s)

// 容量 (スライスの最初の要素から数えて、元配列の要素数) の取得
cap(s)

// 動的サイズの配列の作成
s := make([]int, 長さ, 容量)

// 多次元スライス
board := [][]string{
  []string{"_", "_", "_"},
  []string{"_", "_", "_"},
  []string{"_", "_", "_"},
}
board[0][0] = "X"

// 要素の追加 (長さは追加した数だけ増え、容量は足りない時に多めに自動確保する)
append(s, 追加したい値, 追加したい値...)

// リストの走査 (i はインデックス, v は値のコピー)
for i, v := range s { }
for _, v := range s { } // インデックスの破棄
for i := range s { } // 値の破棄
```

# マップ

map はキーと値を関連付ける。

```golang
// 宣言
m := make(map[string]int)

// リテラル
var m = map[string]int{
  "one": 1,
  "two": 2,
}

// アクセス
m["one"] = 1

// 要素の削除
delete(m, key)

// キーの存在確認
elem, ok := m[key] // 存在すれば ok は true, しなければ false
```

# メソッド

Go にはクラスが無いが、型にメソッドを定義出来る。
メソッドはレシーバ引数を関数に取る。

```golang
// Point型を定義
type Point struct {
  X, Y int
}

// Point型にメソッドを定義
func (p Point) Add() int {
  return p.X + p.Y
}

// メソッド呼び出し
p := Point{1, 2}
p.Add()

// ポインタレシーバ (レシーバ自身を更新出来る)
func (p *Point) Up() {
  p.X += 1
}
```

# インタフェース

interface型はメソッドのシグネチャの集まりで定義される。
これを実装した値を、interface型の変数へ持たせることが出来る。
JavaとかのInterfaceと雰囲気同じ。

```golang
// 型定義
type Point struct {
  X, Y int
}

// インタフェース定義
type Calculator interface {
  Add(int, int) int
}

// インタフェース実装 (implementsキーワードは必要無い)
func (p Point) Add(x, y int) int {
  return p.X + p.Y
}

// 代入
var c Calculator
p := Point{1, 2}
c = &p
```

# エラー

Go言語では、エラー状態を error 値で表現する。
error 型は組み込みインタフェースである。

```golang
// error型の定義
type error interface {
  Error()  string
}

// エラーチェック (nilなら成功, nilでなければ失敗)
i, err := f()
if err != nil {
  fmt.Printf("%v¥n", err)
  return
}
```

# その他標準ライブラリ

- データストリームの読み込み
- インタフェース
- ファイル
- ネットワーク接続
- 圧縮
- 暗号化
- etc...

詳細は[公式](https://golang.org/search?q=Read#Global)を参照。

# 非同期処理

goroutine を利用する、これはGoのラインタイムに管理される軽量スレッドである。
`go f(x)` と書くと新しい goroutine が実行される。
引数の評価は呼び出し元の goroutine で行われ、関数の実行は新しい goroutine が作られる。

goroutine は同じアドレス空間を利用するので、共有メモリアクセスは必ず同期する。
同期には channel を利用すると良い。
詳しくは[公式](https://go-tour-jp.appspot.com/concurrency/2)参照。

# まとめ