【Go】構造体のフィールドに定義してある_ struct{}
はどういう意味?
ライブラリのコードを読んでいると_ struct{}
というフィールドが定義してある構造体をみかけることがありますが、これはどういう意味なのか?というお話です。
結論としては、これは構造体を初期化する際に、フィールド名の指定を強制する意図で宣言されています。
blank identifierを用いた構造体の定義とComposite literalsによる構造体の初期化
type SomeType struct {
Field1 string
Field2 bool
_ struct{}
}
_
はblank identifierと呼ばれるものです。
これは、dev/null
に似ていて、blank identifierに割当てられた値や宣言は、無害な方法で、虚無のブラックホールへと捨て去られます。(_
がブラックホールの穴に見えてくる…)
ここでは詳しく説明しませんが、Effective Goに詳しい使用用途などが紹介されています。
また、Composite literalsで構造体を初期化した場合以下の2つの方法があります。
- フィールド名を指定して初期化する
- フィールド名を省略して初期化する
// フィールド名を指定して初期化:
_ = SomeType{Field1: "hello", Field2: true}
// フィールド名を省略して初期化:
_ = SomeType{"hello", true} // too few values in SomeType literal
フィールド名を指定した場合
フィールド名を指定した場合、指定をしなかったフィールドは、ゼロ値になります。
また、フィールドの順序が入れ替わったり、フィールドが追加されても正しく動作します。
フィールド名を省略した場合
フィールド名を省略した場合、構造体の宣言と同じ順序ですべてのフィールドを宣言して初期化する必要があります。
この場合、フィールド名を省略すると、_ struct{}
に対する初期化を行っていないので、すべてのフィールドを初期化できず、コンパイルエラーになります。
なので結果として、構造体の初期化の際にフィールド名の指定を強制することができるのですね。
パッケージ外からの初期化
また、以下のように同じパッケージ内でstruct{}{}
で初期化した場合は、コンパイルエラーは発生せず、初期化できてしまいます。(struct{}{}
で初期化するのは少々、無理矢理感がありますが…)
SomeType{"hello", true, struct{}{}}
しかしながら、外部のパッケージから構造体を初期化しようとした場合、エラーになります。これは、非公開のフィールドは外部パッケージから初期化できないためです。
// implicit assignment of unexported field '_' in hoge.SomeType literal
_ = hoge.SomeType{"hello", true, struct{}{}}
所感
_ struct{}
という宣言を初めて見たときは、一見なんのこっちゃ感があるので、この手法はメリットとデメリットの両方がありそうだなと個人的には感じました。
意図しない動作を防ぐことができるため、ライブラリを作成したりする場合は特に使う場面があるのかもしれないですね。
このようなテクニックを使用しない場合でも、明示的でわかりやすく、予期しない動作をしないよう、構造体の初期化時にはフィールド名は指定していきたいおきもちです。