Code Fix

上級

ポインタレシーバのメソッドしか無い型は値では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パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)