does not satisfy(ジェネリクスの制約違反)の原因と直し方
ジェネリック関数の型パラメータには制約(constraint)を指定できます。制約が要求する演算(比較や算術演算など)をサポートしない型を渡すと、コンパイルエラーになります。
エラーメッセージの読み方
./main.go:18:17: string does not satisfy Number (string missing in ~int | ~float64)
./main.go- ファイル名
18- 行番号
17- 列番号
string does not satisfy Number (string missing in ~int | ~float64)- 詳細メッセージ — 何が問題だったか
このエラーが出る典型パターン
パターン1
1 package main 2 3 import "fmt" 4 5 type Number interface { 6 ~int | ~float64 7 } 8 9 func sum[T Number](vals []T) T { 10 var total T 11 for _, v := range vals { 12 total += v 13 } 14 return total 15 } 16 17 func main() { 18 fmt.Println(sum([]string{"a", "b"} )) ^ 19 }
Number制約はintかfloat64系の型しか許しません。stringは加算演算子+=に対応していないため、コンパイルエラーになります。
直し方: []string{"a", "b"} を []int{1, 2} にします。
パターン2
1 package main 2 3 import "fmt" 4 5 type Number interface { 6 ~int | ~float64 7 } 8 9 func sum[T Number](vals []T) T { 10 var total T 11 for _, v := range vals { 12 total += v 13 } 14 return total 15 } 16 17 func main() { 18 fmt.Println(sum([]bool{true, false})) ^ 19 }
boolは+=のような算術演算をサポートしないため、Number制約を満たせずコンパイルエラーになります。
直し方: []bool{true, false} を []float64{1.5, 2.5} にします。
パターン3
1 package main 2 3 import "fmt" 4 5 type Number interface { 6 ~int | ~float64 7 } 8 9 func sum[T Number](vals []T) T { 10 var total T 11 for _, v := range vals { 12 total += v 13 } 14 return total 15 } 16 17 func main() { 18 fmt.Println(sum([]byte{1, 2} )) ^ 19 }
byteはuint8のエイリアスで、Number制約の~int | ~float64のどちらの基底型にも一致しないため弾かれます。
直し方: []byte{1, 2} を []int{3, 4} にします。
パターン4
1 package main 2 3 import "fmt" 4 5 type Number interface { 6 ~int | ~float64 7 } 8 9 func sum[T Number](vals []T) T { 10 var total T 11 for _, v := range vals { 12 total += v 13 } 14 return total 15 } 16 17 func main() { 18 fmt.Println(sum([]rune{'a', 'b'} )) ^ 19 }
runeはint32のエイリアスですが、制約が~intのように書かれていてもruneという別名の型自体は基底型がint32でありintではないため一致しません。
直し方: []rune{'a', 'b'} を []int{5, 6} にします。
パターン5
1 package main 2 3 import "fmt" 4 5 type Number interface { 6 ~int | ~float64 7 } 8 9 func sum[T Number](vals []T) T { 10 var total T 11 for _, v := range vals { 12 total += v 13 } 14 return total 15 } 16 17 func main() { 18 fmt.Println(sum([]complex64{1, 2} )) ^ 19 }
複素数型complex64も~int | ~float64のどちらにも含まれないため、Number制約を満たせません。
直し方: []complex64{1, 2} を []int{7, 8} にします。
よくある誤解
「anyを制約にしておけば、どんな型でも渡せてどんな演算もできるはず」という思い込みは誤りです。any(interface{})を制約にすると比較以外のほとんどの演算ができなくなるため、算術演算が必要な場合はconstraints.OrderedのようなAsc要求を満たす制約を指定する必要があります。
まとめ
does not satisfy(ジェネリクスの制約違反)の原因と直し方は上級でつまずきやすい項目です。上の5パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。