エラーメッセージ
Goを書き始めて1週間も経てば、イライラさせるコンパイラエラーに遭遇したことがあるでしょう。通常、構造体を関数に渡そうとしたときに発生し、コンパイラは次のようなメッセージを表示して処理を停止させます。
cannot use val (type *MyStruct) as type MyInterface in argument to Foo: *MyStruct does not implement MyInterface (missing Method method)
メソッドが存在すると言っているのに、「レシーバ」が間違っているために、さらに混乱を招くエラーが出ることもあります。
cannot use MyStruct{...} (type MyStruct) as type MyInterface in argument to Foo: MyStruct does not implement MyInterface (Method method has pointer receiver)
何が原因なのか?
Goの型システムは明示的であることで有名です。推論しようとする一部の言語とは異なり、Goは構造体のメソッドとインターフェースの定義が完全に一致することを要求します。このエラーは通常、次の3つの特定の問題のいずれかに起因します。
- ポインタレシーバの罠: メソッドを
*MyStruct(ポインタ)に対して定義しているのに、MyStruct(値)を渡している。 - シグネチャの不一致: 引数や戻り値の型にタイポがある可能性があります。例えば、インターフェースが
int64を要求しているのにintを使用している場合などです。 - 可視性の問題: インターフェースがエクスポートされた(大文字で始まる)メソッドを期待しているのに対し、メソッドがエクスポートされていない(小文字で始まる)場合。
ステップバイステップの修正方法
1. ポインタレシーバの不一致を解決する
これは9割方の原因です。Goでは、値型のメソッドセットには、ポインタレシーバで定義されたメソッドは含まれません。これにより、元のデータではなくデータのコピーを誤って変更してしまうメソッドの呼び出しを防いでいます。
type MyInterface interface {
Update(name string)
}
type MyStruct struct {
Name string
}
// このメソッドは MyStruct ではなく *MyStruct に属します
func (s *MyStruct) Update(name string) {
s.Name = name
}
リテラルの MyStruct{} を渡そうとすると、コンパイルに失敗します。修正は簡単で、アドレス演算子(&)を使用して代わりにポインタを渡します。
val := MyStruct{}
// Foo(&val) は動作します! Foo(val) は失敗します。
2. シグネチャを完全に一致させる
Goはインターフェースにおける共変戻り値型や自動型キャストをサポートしていません。インターフェースが error の戻り値を期待している場合、メソッドのシグネチャで明示的に error を返すと宣言していない限り、直接 nil を返すことはできません。
誤り:
type Closer interface {
Close() error
}
// 戻り値の型 'error' が欠落しているため、これは失敗します
func (s *MyStruct) Close() {
fmt.Println("終了中...")
}
正しい例: シグネチャのすべての文字がインターフェースの定義と一致していることを確認してください。
func (s *MyStruct) Close() error {
return nil
}
3. パッケージのプライバシーを確認する
他のパッケージで定義されたインターフェースは、エクスポートされたメソッドしか「見る」ことができません。io.Writer を実装しようとしている場合、メソッド名は write ではなく Write である必要があります。小文字のメソッドはパッケージ内でプライベートであり、他の場所で定義されたインターフェースからは見えません。
修正を確認する「プロ」の方法
構造体がインターフェースを満たしているかどうかを確認するために、プロジェクト全体のビルドを待つ必要はありません。Goの開発者は、これらのエラーを即座にキャッチするために「静的インターフェースチェック」を使用します。構造体の定義のすぐ下に次の行を配置してください。
var _ MyInterface = (*MyStruct)(nil)
この行はメモリを割り当てません。単にコンパイラに「*MyStruct へのポインタが MyInterface に代入可能であることを確認せよ」と伝えます。後で実装を壊してしまった場合、IDEでこの行が即座にエラーとしてハイライトされます。
インターフェースのベストプラクティス
- 一貫性を持たせる: 構造体の一つのメソッドにポインタレシーバが必要な場合は、その構造体のすべてのメソッドでポインタレシーバを使用するようにしてください。これにより、メソッドセットの予測可能性が保たれます。
- インターフェースを小さく保つ: Goの格言「インターフェースが大きくなるほど、抽象化は弱くなる」に従いましょう。1つのインターフェースにつき1〜3個のメソッドを目指します。
- ツールを活用する: VS Code や GoLand を使用している場合は、構造体を右クリックして「Implement Interface(インターフェースを実装)」を選択します。これにより正しいシグネチャが自動生成され、タイポを防ぐことができます。

