ポインタレシーバのメソッドしか無い型は値ではinterfaceを満たさない
ポインタレシーバで定義したメソッドは、その型の値そのもの(非ポインタ)のメソッドセットには含まれません。値をそのままinterface型の変数に代入しようとするとコンパイルエラーになります。
エラーメッセージの読み方
./main.go:15:18: cannot use p (variable of type Person) as Greeter value in variable declaration: Person does not implement Greeter (method Greet has pointer receiver)
./main.go- ファイル名
15- 行番号
18- 列番号
cannot use p (variable of type Person) as Greeter value in variable declaration: Person does not implement Greeter (method Greet has pointer receiver)- 詳細メッセージ — 何が問題だったか
このエラーが出る典型パターン
パターン1
1 package main 2 3 import "fmt" 4 5 type Greeter interface { 6 Greet() string 7 } 8 9 type Person struct{ Name string } 10 11 func (p *Person) Greet() string { return "hi " + p.Name } 12 13 func main() { 14 p := Person{Name: "go"} 15 var g Greeter = p ^ 16 fmt.Println(g.Greet()) 17 }
./main.go:15:18: cannot use p (variable of type Person) as Greeter value in variable declaration: Person does not implement Greeter (method Greet has pointer receiver)
GreetはPersonではなく*Personにしか定義されていないメソッドです。値pをそのままinterfaceに代入することはできず、&pでアドレスを渡す必要があります。
直し方: p を &p にします。
広告
広告スロット(未設定)
パターン2
1 package main 2 3 import "fmt" 4 5 type Writer interface { 6 Write() string 7 } 8 9 type Logger struct{ Prefix string } 10 11 func (l *Logger) Write() string { return l.Prefix + ": ok" } 12 13 func main() { 14 l := Logger{Prefix: "info"} 15 var w Writer = l ^ 16 fmt.Println(w.Write()) 17 }
./main.go:15:17: cannot use l (variable of type Logger) as Writer value in variable declaration: Logger does not implement Writer (method Write has pointer receiver)
ポインタレシーバのメソッドしか無い型は、値そのもののメソッドセットには含まれません。interfaceへの代入時にアドレスを取れないと満たせません。
直し方: l を &l にします。
パターン3
1 package main 2 3 import "fmt" 4 5 type Runner interface { 6 Run() string 7 } 8 9 type Task struct{ Name string } 10 11 func (t *Task) Run() string { return t.Name + " done" } 12 13 func main() { 14 t := Task{Name: "build"} 15 var r Runner = t ^ 16 fmt.Println(r.Run()) 17 }
./main.go:15:17: cannot use t (variable of type Task) as Runner value in variable declaration: Task does not implement Runner (method Run has pointer receiver)
アドレス指定可能な変数への代入なら&は自動で補われますが、interfaceへの代入では自動補完されません。
直し方: t を &t にします。
パターン4
1 package main 2 3 import "fmt" 4 5 type Closer interface { 6 Close() string 7 } 8 9 type File struct{ Path string } 10 11 func (f *File) Close() string { return f.Path + " closed" } 12 13 func main() { 14 f := File{Path: "a.txt"} 15 var c Closer = f ^ 16 fmt.Println(c.Close()) 17 }
./main.go:15:17: cannot use f (variable of type File) as Closer value in variable declaration: File does not implement Closer (method Close has pointer receiver)
値のメソッドセットにポインタレシーバのメソッドは含まれないため、interfaceを満たすには&で明示的にポインタを渡す必要があります。
直し方: f を &f にします。
パターン5
1 package main 2 3 import "fmt" 4 5 type Painter interface { 6 Paint() string 7 } 8 9 type Wall struct{ Color string } 10 11 func (w *Wall) Paint() string { return "painted " + w.Color } 12 13 func main() { 14 w := Wall{Color: "blue"} 15 var p Painter = w ^ 16 fmt.Println(p.Paint()) 17 }
./main.go:15:18: cannot use w (variable of type Wall) as Painter value in variable declaration: Wall does not implement Painter (method Paint has pointer receiver)
この制約はコンパイル時に静的に検査されるため、実行前に気づくことができます。
直し方: w を &w にします。
よくある誤解
「ポインタレシーバのメソッドでも、値をそのまま渡せば自動的にアドレスを取ってくれるはず」という思い込みは誤りです。アドレス指定可能な変数への代入時は自動的に&が補われますが、interfaceへの代入のようにアドレスを取れない文脈では、値のメソッドセットにポインタレシーバのメソッドは含まれません。
まとめ
ポインタレシーバのメソッドしか無い型は値ではinterfaceを満たさないは上級でつまずきやすい項目です。上の5パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。