Go Language Specification 輪読会の感想と学んだこと(型アサーション、スライス式)

先日、初めてGo Language Specification 輪読会 (#14) 2021/1/27開催に参加した感想と、型アサーションスライス式に関して学んだことを書いてみたいと思います。

Go Language Specification 輪読会に参加した感想

The Go Programming Language Specification を読んでいる時に、この英文どう解釈すれば良いんだろう。。と困るようなところで

  • Go言語強い方がいて、たぶんこういう意味だろうという推測が付いたり
  • 英語強い方がいて、表現に関して教えてもらえたり

1人で読んでいても分からない点がクリアになって凄くためになり、これが集まって輪読会をする利点なんだなぁと実感しました。

もちろん、輪読会の中で知った言語仕様に関しては勉強になりましたが、特に参加して良かったと感じたのは、Go Language Specificationに出てくる用語について、他の参加者の方が解説してくれていたことです。

具体的には

  • primary expression という表現が良くでてきますが、ここのことだよーと教えてくれた
  • addressable というのはこういうことだよー、とサンプルコードを書いてに関して教えてもらえた

という感じです。

そこで思ったのは「魚を与えるのでなく、釣り方を教えよ」という言葉がありますが、よく出てくる用語が分かり、自分で読むための力がつくのでこういうことを教えてもらえると凄く役に立つなと思いました。

あと、アットホームな感じでとても参加しやすく感じました。

学んだこと

言語仕様なのであまり実用性がない箇所もありますが、学んだ点に関しても4点共有してみたいと思います。

アサーション(Type Assertion)

①インタフェースで型アサーションするとき、動的型変数はインタフェースを実装していることを検証する

言語仕様の文章では

If T is an interface type, x.(T) asserts that the dynamic type of x implements the interface T.

という箇所で、具体的なコードを示すと

type Animal string
type Human string

type Thinkable interface {
    think()
}

func (Human) think() {}

func main() {
    // var thinkable interface{} = Human("安倍晋三") // こっちはOK
    var thinkable interface{} = Animal("My Cat") // こっちはNG

    var human = thinkable.(Thinkable)
    fmt.Println(human)
    // => panic: interface conversion:
    //    main.Animal is not main.Thinkable: missing method think
}

となって、型アサーションはインタフェースを実装しているかどうかもチェックしてくれる。へぇ。

簡易スライス式(Simple slice expressions)

nilスライスをスライスするとnilになる

言語仕様の文章では

If the sliced operand of a valid slice expression is a nil slice, the result is a nil slice.

という箇所。コードとしては

var slice []int
fmt.Println(slice[:] == nil)
=> true

という感じで確かに nil になる

ちなみに、要素がないスライスはどうなるんだろうと思って試してみたけど、この時は nil スライスではなく空のスライスなので nil にはならなかった。

slice := [5]int{1, 2, 3, 4, 5}
emptySlice := slice[0:0]
fmt.Println(emptySlice[:] == nil)
=> false

// 一応、容量が0になる場合も試してみたけど、関係なかった
emptySlice := slice[0:0:0]
fmt.Println(emptySlice[:] == nil)
=> false

③スライスされる配列はアドレス化可能(addressable) でなければならない

言語仕様の文章では

If the sliced operand is an array, it must be addressable.

という箇所。Addressableという用語は

The operand must be addressable, that is, either a variable, pointer indirection, or slice indexing operation; or a field selector of an addressable struct operand; or an array indexing operation of an addressable array.

で、変数、ポインタ間接参照、スライスのインデックス操作、アドレス指定可能な構造体オペランドのフィールド・セレクタ、アドレス指定可能な配列の配列インデックス操作のいずれかということらしいので、試しに変数に入れずに配列をスライスしてみると

fmt.Println([3]string{"apple","banana","strawberry"}[:])
fmt.Println([]string{"apple","banana","strawberry"}[:]) 
=> ./prog.go:8:54: invalid operation [3]string literal[:]
   (slice of unaddressable value)

というように、たしかにビルド時のエラーになった。

完全スライス式(Full slice expressions)

④定数インデックスは負の数ではなく、int型の値で表現されなければならない

言語仕様の文章では

A constant index must be non-negative and representable by a value of type int;

という箇所。

例えばruneはint32のエイリアスでintタイプに該当するので、'🍣'みたいなものでもOK!

func main() {
    // intの要素の値が0で、長さ・容量が300,000のスライスを作る
    s3 := make([]int, 300_000)
    const c3 = '🍣'
    fmt.Println(c3)
    // => 127843

    // 長さが127,843のスライスができる    
    fmt.Println(s3[0:c3]) 
    fmt.Println(len(s3[0:c3])) 
    // => 127843
}