Goクイズアドベントカレンダー19日目

December 19, 2020

この記事は、Goクイズ Advent Calendar 2020の19日目の記事です。

問題

以下のコードを実行するとどうなるか?

package main
import _ "fmt"
var x T; type T int; type _ T; type _ = T; const _ = 0
var _ = func(_ T) (_ T) { x++; return }
func (_ T) _(_ T) (_, _, _ T) { return }
var _ = func(T,_ T) (_ T) { x++; return }(x,x)
func main() { fmt.Println(x) }
func (_ T) _(_ T) (_, _, _ T) { return }
func _() {}
  1. コンパイルエラー
  2. 0と表示される
  3. 1と表示される
  4. 2と表示される

解答と解説

解答

答えは「1. コンパイルエラー」でした!

これは、以下のように_がどこまで使えるのかということを問う問題です。

  • インポート文で使えるか
  • 型定義で使えるか
  • 型エイリアスで使えるか
  • 定数定義で使えるか
  • 変数定義で使えるか
  • 関数の引数や戻り値に使えるか
  • 関数名に使えるか

解説

_はブランク識別子と呼ばれ、変数への代入などを省略したい場合に使用します。主に代入文の左辺(参照)に使われ、右辺(値)として使用することはできません。

代入文の他にも、何かしらの定義において識別子として使えます。例えば、型定義、型エイリアス、定数定義、変数定義、関数定義などです。また、関数定義については引数や戻り値だけではなく関数名にも使えます。もちろん、メソッドにも使えます。

なお、ブランク識別子は何度使用しても定義済み識別子のコンパイルエラーにはなりません。そのため、_関数や_メソッドを複数定義することも可能です。

では、なぜコンパイルエラーになるのか?

さて、答えはコンパイルエラーになります。ブランク識別子を使ってパッケージをインポートした場合、そのパッケージは別途インポートしない限り(ブランク識別子を使用せず)はファイル内で使用することはできません。

問題のfmtパッケージは、ブランク識別子を使ってインポートされているため、fmt.Println関数は使用できません。つまり、コンパイルエラーになります。

なお、ブランク識別子を使ったパッケージのインポートは、init関数における副作用を期待する場合に用います。database/sqlパッケージやimageパッケージを用いる場合に、ドライバーや画像形式を登録する場合に用いることが多いでしょう。