基礎からわかるGo言語」を読んだ. 2012年に発行された本なので,Goのバージョンがちょっと古い(Go 1.0.3)が,構文や考え方は大きく変わらないと思う. 今後,Go言語を使うことになるかもしれないので,気になったところをメモしておく.

  • インスタンスの作成と初期化: インスタンスの作成方法には,下のコード例のように,いくつかの選択肢がある. 特に気を付けないといけないのは,Go言語にもポインタの概念があるので, ポインタと実体のどちらが生成されるのかという点である. また,インスタンス作成時に,構造体の要素に対する初期値を明示しなかった場合には, その要素にはゼロ値が割り当てられる点にも注意する. 例外として,スライス,チャネルおよびマップは参照型なので,newの代わりに,makeを使う.
1
2
3
4
var x T     // T型
y := T{ }   // T型
z := &T{ }  // *T型
w := new(T) // *T型
  • ブランクフィールド: 構造体の定義において,_ byteのように名前のない要素を定義すると,パディングを実現できる.
  • インターフェースとnil: インターフェースに,T型の値xを代入すると,内部では<T,x>のように型と値が組になって格納される. これに起因して,インターフェース型の値pnilかどうかをチェックするときは, 単純にnilと比較するのではなく,reflect.ValueOf(p).IsNil()を使う.
1
2
3
4
5
var p1 *int
var p2 interface{} = p1
fmt.Println(p1 == nil) // true
fmt.Println(p2 == nil) // false
fmt.Println(reflect.ValueOf(p2).IsNil()) // true
  • リカバリ: 配列の範囲を超えたインデクス参照などによって致命的なエラーが発生したとき,あるいはpanic関数を呼び出したとき,プログラムはパニック状態になる. パニック状態になると,関数実行は中断され,main関数までコールスタックを遡っていく. ただし,その途中で,defer文によって遅延指定された関数は実行される. deferにより遅延して呼び出しされた関数の中で,そのパニックに対処できた場合は, recovery組み込み関数によって,パニックによるコールスタックの連鎖を中断できる.

  • lenとcap: スライスsは,配列へのポインタ,長さlen,および容量capを持つ. ここで,スライスsの部分要素を参照するためにv := s[2:4]とすると, vsは同じ配列を参照することになる. コピーが欲しければ,明示的に,copy関数を使わなければならない. この辺りは,内部構造を考慮しながら,コーディングしたほうが良い.

  • チャネルの利用例: Goでは,スレッド間のデータ共有を実現するにあたって, 共有メモリではなく,チャネルという概念を導入している. チャネルは,スレッド間で任意のデータをアトミックに送受信するFIFO構造の通信機構である. チャネルを上手に使うと,同期,ミューテックス(セマフォ),およびデータ共有を実現できる. 例えば,同期であれば,ワーカスレッドがタスクの終了と同時にチャネルに何らかのデータを書き込み, マスタスレッドがチャネルを監視するという実装になる. ミューテックスであれば,大きさ1のチャネルを作り, 複数のスレッドからそのデータを取り合う,といった実装になる. データ共有であれば,同じく大きさ1のチャネルを作り, データの取り出しに成功したスレッドが,そのデータを更新し, 再度チャネルに書き込むという実装になる.

  • select文 複数のチャネルから値を受信するとき,switch文に似た構文をもつselect文を使うことで, 受信可能なチャネルを選択できる.