Goの静的解析で型関係の処理を提供するgo/types パッケージには、*Interface 型を引数にとるよく似たシグネチャを持つメソッドが2つある。
https://pkg.go.dev/go/types#Satisfies と https://pkg.go.dev/go/types#Implementsのことだ。
func Implements(V Type, T *Interface) bool は、Goにジェネリクスが導入される前から存在していたメソッドでインターフェイスを実装(Implements)しているかどうかを判定する。
一方でシグネチャ的にはインターフェイスで同じだが func Satisfies(V Type, T *Interface) boolは、型VがインターフェイスTを型制約として満たす(satisfies)かどうかを判定する。
satisfiesは、implementsよりも若干広い。 型制約に関する仕様 https://go.dev/ref/spec#:~:text=Satisfying%20a%20type%20constraintによれば型Tが型制約としてのインターフェイスIを満たすのは次の場合だ。
- TがIを実装している場合
- Iが
interface{ comparable; E }(Eは basic interface、comparableは事前宣言された型制約のこと)の形で書けて、Tが言語仕様として==で比較可能、かつTがEを実装している場合。basic interface とは、float64 | float32のような unions を含まないインターフェイスのこととする。
型制約はインターフェイスで表現されるため、型制約CをインターフェイスIとして言い換えた。
つまり、大雑把に言えば「“Iからcomparableを除いた部分集合”を関数、マップ、スライスではないTが実装していれば、TはIをImpementsしていないがsatisfiesはしている」ということ。 なぜimplementsとsatisfiesが異なる概念になったかの解説は、Go言語のBasic Interfaceはcomparableを満たすようになる(でも実装するようにはならない)に詳しい解説があるので気になるなら読んでみるとよい。
ジェネリクスが導入される以前は、Implements と satisfies は同等で「実装する」と「満たす」を使い分けせずに用いられてきたので未だに混乱する。 自分の書く文章の中では意識して使い分けていきたい。