https://logmi.jp/tech/articles/326450
小野輝也氏:「Goと定数」について話します。よろしくお願いします。始めに自己紹介をさせてください。小野輝也と申します。2021年の新卒入社で、ITインフラ本部のSRE部に所属しています。先週まではずっと技術系の研修を受けていて、まさに今週から配属になりました。
ふだんはGoでWebアプリケーションを書いたり、Rustでネットワークプログラミングをする同人誌を書いたりしています。Twitterもやっているので、よければフォローよろしくお願いします。
今回話す内容についてです。私はまだ配属されてから間もなくて、実際のDMMのプロダクション環境下で動くGoを書いたことはないので、Goの言語仕様に関わるような簡単な定数の話をしようと思います。内容は、Goにおける定数の基本と、その定数の上手な扱い方についてです。
Goの定数について、まず基礎をさらっと話そうと思います。(スライドを示して)左のコードのように定数がたくさん定義してあって、その型と値を表示するプログラムを実行してみます。
こちらが結果です。定数になれるのはboolean、rune、integer、floating-point、complex、stringのみの型になっています。Go言語では、マップや配列、スライスのようなものはベースにできません。
定数の定義の部分を空にすると、それより上の空ではない値をコピーするというものがあります。左のコードのcの部分です。「=右辺」が存在していないのですが、このようなものを書いた場合、それより上の空ではない、この場合でいうとbの「Welcome to DMM.go」は、cに同じ定義が書かれたものとみなされます。そのため、実行した結果は、bとまったく同じものが表示されるようになっています。
Goの定数はコンパイル時に値が決定されます。たくさんの型があるのですが、aに関してはfloatで、eに関しては複素数のかたちをしているのでcomplexになります。
(スライドを示して)最後のこれは、Goにおいてはtrueとfalseは予約語ではなくて、ビルトインパッケージで定義されている定数です。こんな感じで定義されているため「true=false」と書くと、右側のfalseはビルドインパッケージの定数であるfalseで、trueはパッケージで定義される定数です。このため、実行してみるとtrueの値はfalseという結果が得られます。
(スライドを示して)dに出てきたiotaに関してです。iotaはあまり目にする機会がないのですが、これは定数の生成器です。const(……)のカッコの部分に含まれる、定数定義の0から始まるインデックスを返します。同じ行に含まれるiotaは、同じ値を返してきます。
下のコードサンプルの例を見てみると、定数の定義a、b、cと並んでいますが、1つ目のaに関して、この行のiotaは0から始まるインデックスなので、0を返してきます。0プラス0で、aは0になります。2行目に関して、インデックスは2個目なので、1になり、bはiota掛ける10で10になります。
cに関しては定義が書いていないのですが、先ほど「定義が空の部分はその上にあるものをコピーする」というルールがありました。式をまるまるコピーしてくるので、cもiota掛ける10をまるまるコピーします。この時、iotaは定義の3つ目になるので、0から始まるインデックスだと2で、cの値は20になります。今のところはGoにもEnumはないので、このiotaは疑似的にEnumを作る時に利用したりします。
次に、この定数について今回話そうと思った経緯です。自分でGoのプログラムを書いていて、「あれ?」と思ったことがあったので、それを元にいろいろ調べて、ここで発表しようと思っています。
(スライドを示して)どんなプログラムかというと、任意の時間停止させられるtime.Sleep関数で、左のようにtime.Sleepで3掛けるtime.Secondと書くと、普通にコンパイルが通って、プログラムが3秒待って始動するような挙動をします。
一方で、右側のように変数aに対して3を割り当て、それに対してtime.Secondを掛けようとすると、「invalid operation、mismatched types」intとtime.Durationを掛けることはできませんよ、といったコンパイルエラーになり、このプログラムは通せません。
一見同じことをしているように見えるのに、一方はよくて、一方はダメな理由が気になったので、それについて後々話していこうと思います。