「基礎からわかるGo言語」を読んだ. 2012年に発行された本なので,Goのバージョンがちょっと古い(Go 1.0.3)が,構文や考え方は大きく変わらないと思う. 今後,Go言語を使うことになるかもしれないので,気になったところをメモしておく.
- インスタンスの作成と初期化:
インスタンスの作成方法には,下のコード例のように,いくつかの選択肢がある.
特に気を付けないといけないのは,Go言語にもポインタの概念があるので,
ポインタと実体のどちらが生成されるのかという点である.
また,インスタンス作成時に,構造体の要素に対する初期値を明示しなかった場合には,
その要素にはゼロ値が割り当てられる点にも注意する.
例外として,スライス,チャネルおよびマップは参照型なので,
new
の代わりに,make
を使う.
var x T // T型
y := T{ } // T型
z := &T{ } // *T型
w := new(T) // *T型
- ブランクフィールド:
構造体の定義において,
_ byte
のように名前のない要素を定義すると,パディングを実現できる. - インターフェースとnil:
インターフェースに,
T
型の値x
を代入すると,内部では<T,x>
のように型と値が組になって格納される. これに起因して,インターフェース型の値p
がnil
かどうかをチェックするときは, 単純にnil
と比較するのではなく,reflect.ValueOf(p).IsNil()
を使う.
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]
とすると,v
とs
は同じ配列を参照することになる. コピーが欲しければ,明示的に,copy
関数を使わなければならない. この辺りは,内部構造を考慮しながら,コーディングしたほうが良い. -
チャネルの利用例: Goでは,スレッド間のデータ共有を実現するにあたって, 共有メモリではなく,チャネルという概念を導入している. チャネルは,スレッド間で任意のデータをアトミックに送受信するFIFO構造の通信機構である. チャネルを上手に使うと,同期,ミューテックス(セマフォ),およびデータ共有を実現できる. 例えば,同期であれば,ワーカスレッドがタスクの終了と同時にチャネルに何らかのデータを書き込み, マスタスレッドがチャネルを監視するという実装になる. ミューテックスであれば,大きさ1のチャネルを作り, 複数のスレッドからそのデータを取り合う,といった実装になる. データ共有であれば,同じく大きさ1のチャネルを作り, データの取り出しに成功したスレッドが,そのデータを更新し, 再度チャネルに書き込むという実装になる.
-
select文 複数のチャネルから値を受信するとき,
switch
文に似た構文をもつselect
文を使うことで, 受信可能なチャネルを選択できる.