Go言語入門 vol.4 – Goの型アサーションについて –

※Go言語入門のindexページはこちら

こんにちは。オガリア開発チームの粂です。

Go言語の勉強のためにGoで書かれたOSSライブラリをぼちぼち読んでいるのですが、以下のような記述にたびたび遭遇します。

例えば、

hoge.(bool)

hoge.(string)

など。

当初どういう意味か全くわからずあれこれ調べた結果、上記は型アサーションと呼ばれるものだということがわかり、
さらに、それがどういった役割を持つのかもある程度理解できたので、現時点で理解できている内容を残しておこうと思います。

Goのバージョン:1.1.2

具体的なソースコードを示しながら書いていこうと思うのですが、

package main

import (
	"fmt"
)

func main() {
	check("Hello")
}

func check(i interface{}) {
	a, b := i.(string)
	fmt.Println(a)
	fmt.Println(b)
}

上記のようなソースコードを実行すると出力は以下のようになります。

Hello
true

checkの冒頭で型アサーションが使用されています。
ここでは引数として受け取ったインターフェース型のiがstring型かどうかをアサーションしています。

アサーションした結果、2つの戻り値が返ってくるのですが、上記でいうとaにはstring型に変換されたiが格納され、bにはアサーションに成功したかどうかがbool型の値で格納されます。
mainからはstring型の文字列「Hello」を渡していますので、fmt.Printlnによる出力結果がHelloとtrueになっています。

この型アサーションを使えば、引数として受け取ったインターフェース型のメソッド呼び出しの前に、そのメソッドが実装された型かどうかをチェックすることができます。

また、GitHubでhttps://github.com/mattn/gomを読んでいて型アサーションの使い方で参考になったのが以下のような書き方です。
gomfile.goのソースコードの一部を抜粋します。

func matchOS(any interface{}) bool {
	var envs []string
	if as, ok := any.([]string); ok {
		envs = as
	} else if s, ok := any.(string); ok {
		envs = []string{s}
	} else {
		return false
	}

	if has(envs, runtime.GOOS) {
		return true
	}
	return false
}

引数として受け取ったインターフェース型のanyがstringの配列型であればそのままenvsに代入、string型であればstringの配列型に変換してenvsに代入しています。

さらに、https://github.com/heroku/hkも参考になりました。
ssh.goのエラーハンドリングしている箇所です。

if err != nil {
	if _, ok := err.(privKeyError); ok {
		log.Println("refusing to upload")
	}
	log.Fatal(err)
}

errの型アサーションをすることでerrの型チェックを行いロギングの処理を振り分けています。

もっとソースコードリーディングを進めて理解を深めていきたいと思います。

最後までお読みいただきありがとうございました!

Pocket