1. Goに触れる
- Goの特徴
- シンプルで協力な記述
- 機能を増やすことで言語を拡張していくことはしない
- 並行プログラミング
- マルチコア前提,並行処理とガーベッジコレクション
- goroutine: 軽量スレッド的なもの,
go
キーワードをつけるだけ,e.g.go f()
で関数を並行呼び出し
- 必要なライブラリやツールが標準で同梱されてる
- シングルバイナリ・クロスコンパイル
- コマンドラインツールの開発に向いてる
- 組み込み・IoTにも (TinyGoという組み込み向けのサブセットもある)
- フロントエンド向け,wasm以外にもGopherJSというのもある
- 静的解析しやすい言語
- シンプルで協力な記述
- Goのバージョン
- Go1の間は後方互換
- Go2=Go1.xを拡張していって,後方互換が保てなくなる場合にGo2にするというポリシ
- 重要そうな技術
- gRPC
- gVisor
- 学習ソース
- go.dev
- Go Codereview Comments
- Goらしい書き方
2. 基本構文
- 数値をアンダースコアで区切れる
- e.g.
5_000_000
- e.g.
- 連続整数を
iota
で定義できる1const ( 2 a = iota 3 b 4 c 5 ) 6 fmt.Printf("%d, %d, %d", a, b, c) // -> 1, 2, 3
3. 関数と型
- 少し特殊な配列定義方法
1 array1 := [...]int{1, 2, 3}
2 fmt.Println(array1) // [1 2 3]
3
4 array2 := [...]int{5: 50, 10: 100}
5 fmt.Println(array2) // [0 0 0 0 0 50 0 0 0 0 10]
arrayとslice
- arrayは固定長,静的
- sliceは参照型,可変長,動的
- sliceの容量はメモリ確保済みのサイズなので拡張可能
- 容量オーバーしたら基本的に2倍の値を確保
slice trick
1 // slice tricks: cut
2 aa := []int{1, 2, 3, 4, 5}
3 fmt.Println(aa) // [1, 2, 3, 4, 5]
4 aa = append(aa[:2], aa[3:]...)
5 fmt.Println(aa) // [1, 2, 4, 5]
4. パッケージ
- go get => dep => modules (vgo)
- modulesを使う
go mod init
でgo.modを生成- main作成
1package main 2 3import ( 4 "fmt" 5 6 "github.com/tenntenn/greeting" 7) 8 9func main() { 10 fmt.Println(greeting.Do()) 11}
go run xxx.go
するとgo.modにパッケージ追記されて実行できる- runする前に
go mod tidy
してもOK - vendorディレクトリ配下にパッケージダウンロードするときは
go mod vendor
- runする前に
- 後方互換のないバージョン使う場合はimport時の記述を変える
1package main 2 3import ( 4 "fmt" 5 "time" 6 7 "github.com/tenntenn/greeting/v2" 8) 9 10func main() { 11 fmt.Println(greeting.Do(time.Now())) 12}
5. コマンドラインツール
- forでファイル処理したいとき→forの中でdeferせず処理を関数化して関数の方でdeferする
- NG
1for _, fn := range fileNames { 2 f, err := os.Open(fn) 3 if err != nil { 4 // エラー処理 5 } 6 defer f.Close() // fを使った処理 7}
- OK
1for _, fn := range fileNames { 2 err := readFile(fn) 3 if err != nil { /* エラー処理 */ } 4}
1func readFile(fn string) error { 2 f, err := os.Open(fn) 3 if err != nil { return err } 4 // fを使った処理 5 defer f.Close() 6}
6. 抽象化
- 型スイッチ
1 // type switch
2 var i interface{}
3 i = 100
4 switch v := i.(type) {
5 case int:
6 fmt.Println(v * 2)
7 case string:
8 fmt.Println(v + "hoge")
9 default:
10 fmt.Println("default")
11 }
8. テストとテスタビリティ
Example
で始まる関数名のテストを書くとGoDocにサンプルとして出力されるテーブル駆動テスト + サブテスト
1func TestIsOdd(t *testing.T) {
2 cases := []struct {name string; input int; expected bool}{
3 {name: "+odd", input: 5, expected: true},
4 {name: "+even", input: 6, expected: false},
5 {name: "-odd", input: -5, expected: true},
6 {name: "-even", input: -6, expected: false},
7 {name: "zero", input: 0, expected: false},
8 }
9
10 for _, c := range cases {
11 c := c
12 t.Run(c.name, func(t *testing.T) {
13 if actual := IsOdd(c.input); c.expected != actual {
14 t.Errorf(
15 "want IsOdd(%d) = %v, got %v",
16 c.input, c.expected, actual)
17 }
18 })
19 }
20}
- テストデータは
testdata
というディレクトリに入れる- パッケージとして認識されなくなる
9. ゴールーチンとチャネル
- goroutineが切り替わるタイミング
- (ブロックされる)チャネルへの読み書き
- 待ちが発生するシステムコール
time.Sleep
- メモリ割り当て
runtime.Gosched
- チャネルをcloseすると受信場所にゼロ値が送られる=ブロードキャスト,処理終了通知として使われる
Tagged: #Go