trueにfalseを代入する!? 〜 Goクイズの解説 その1 〜

Goクイズ

Goクイズとは、プログラミング言語Goに関するクイズです。プログラムを見て実行結果を予想します。文法などのGoに関する知識が問われます。4択などの選択式であることが多いでしょう。

@mercaridevjpのTwitterアカウントでも以下のような問題をポストしました!

問題

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

package main
func main() {
    true := false
    println(true == false)
}
  1. コンパイルエラー
  2. trueと表示される
  3. falseと表示される
  4. パニックが発生する

Twitterでのみなさんの回答

Twitterの投票機能を使って、みなさんの回答を集めてみました。結果は以下のようになりました。

「コンパイルエラー」と「trueと表示される」が多くて、後者が若干上回っています。

回答と解説

答えは「trueと表示される」です!

この問題はtrue := falseがコンパイルエラーになるのか、ならない場合はどうなるのか?ということが論点になります。

Goでは変数名(識別子)には、iftypeなどの予約語は使えません。そのため、trueが予約語である場合はコンパイルエラーになります。しかし、true言語仕様によるとユニバースブロックで事前定義された名前付き定数となっています。

Goでは以下のように外のブロックで定義された変数名と同じ名前の変数を内側のブロックで定義することが可能です。main関数の最初のprintln関数はパッケージ変数xの値である100を表示します。しかし、2つめのprintln関数は、直前で定義されたローカル変数xの値である"hello"を表示します。

なお、ここでは変数について説明しましたが、基本的には名前付き定数など識別子(名前)を伴うものは同じような挙動になります。

package main

var x int = 100

func main() {
    println(x) // 100と表示される
    var x string = "hello"
    println(x) // helloと表示される
}

さて、trueの場合を考えてみましょう。trueはユニバースブロックで定義された名前付き定数でした。ユニバースブロックとは、すべてのスコープの親(ルート)となるようなスコープです。そのため、そこで定義された識別子(名前)と同じ名前で子スコープ(それ以下も含む)となる任意のスコープで定義することが可能です。

つまり、問題にあるtrue := falseは、main関数のスコープで新しい変数trueを作り、そこにユニバースブロックで定義された名前付き定数falseの値を代入するという事になります。ちなみに、このときtrue = falseと書くと変数定義ではなく、ただの代入になるので、定数への代入はできないためコンパイルエラーになります。

そして、println(true == false)は、値が真偽値のfalse(偽)である変数trueと、同じく値が真偽値のfalse(偽)であるユニバースブロックで定義された名前付き定数falseの比較であるため、真偽値のtrue(真)になります。そのため、最終的にはtrueが表示されます!

ユニバースブロックで定義された識別子と同じ識別子が定義できることが分かると以下のようなコードも書けます。面白いですね!!

package main

func main() {
    type string int
    var s string
    println(s + 10) // 10と表示される
}

もっとクイズをやってみたい方へ

今回のGoクイズを解いたり回答を見て、「へぇー!こんな風に書けるんだ!」と感じた方もいるのではないでしょうか。そんな方には、「開発ライブ実況 #7 あるあるバグを仕込んだ渾身のGoクイズを持ち寄って出し合います!」というイベントがオススメです!

Goの言語仕様に詳しい社内外のGopher4人が集まり、渾身のGoクイズを出し合います。もちろん、出題者からの解説とともに日頃の開発で活きるハマリポイントなども聞けるでしょう。

練習問題も用意してますので、ぜひウォーミングアップも兼ねてチャレンジしてみてください!

@mercaridevjpのTwitterアカウントでも引き続き問題を投稿していきますので、ぜひフォローしてお待ち下さい!

問題を解くだけじゃ足りない!!自分も作ってみたいぞ!って方は、ぜひGoクイズ アドベントカレンダーにご参加ください!