🛡️
[Go小ネタ] type listを含むinterfaceのinterface guard
Goには型がインターフェイスを満たしていることを明示的に示すjsのimplements
のようなものがないため、代用としてしばしばinterface guardと呼ばれる記法が使われることがあります。
type Hoge interface{
Hello()
}
// HogeImplはHogeインターフェイスを実装している
type HogeImpl struct{}
func (h *HogeImpl) Hello() {
fmt.Println("Hello, 世界")
}
// interface guard
// 左辺はここ以外で使わないためアンダースコアにする
// 右辺の値は何でもよいがゼロ値を入れておくと記述が楽
var _ Hoge = (*HogeImpl)(nil)
interface guardを書いておくことで、例えば*HogeImpl
型にHello()
が実装されていない場合、以下のようなコンパイルエラーを出力してくれます。
cannot use (*HogeImpl)(nil) (value of type *HogeImpl) as type Hoge in variable declaration:
*HogeImpl does not implement Hoge (missing Hello method)
しかし、Go1.18から追加されたジェネリクスが絡んでくると、こう簡単にはいかない場合があります。具体的にはインターフェイスが内部のプロパティにtype list(その型に代入可能な型のリスト)を持っていると不具合が生じます。
type Hoge interface {
~int // int型に代入可能な型のリスト
}
// HogeImplはint型に代入可能なのでHogeインターフェイスを実装している
type HogeImpl int
// interface guard
var _ Hoge = (HogeImpl)(0)
これは先ほど同様うまくいくように思われますが、実際は以下のコンパイルエラーが発生します。
cannot use type Hoge outside a type constraint: interface contains type constraints
この問題は、インターフェイスを実装している型をgeneric typeに含めた空の構造体(以下のHogeIsImplementedBy
)を定義することで解決することができます。
type Hoge interface {
~int
}
type HogeIsImplementedBy[T Hoge] struct{}
type HogeImpl int
// interface guard
var _ HogeIsImplementedBy[HogeImpl]
これでコンパイルエラーを消すことができました!
さらに、Hoge
がHogeImpl
によって実装されていることも命名から明らかです。
定義する型が1つ増えてしまうため少し冗長ではありますが、type listを含んだインターフェイスのinterface guardを書きたい場合には有効です。
また、将来的にはtype listを含めることも検討されているようです。
Discussion