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への代入のようにアドレスを取れない文脈では、値のメソッドセットにポインタレシーバのメソッドは含まれません。

実務での勘所

Goの仕様は、値の型Tとポインタ型*Tとで、持てるメソッドセットの範囲が非対称であると定めています。値レシーバで定義したメソッドは、TのメソッドセットにもTポインタのメソッドセットにも含まれます。しかしポインタレシーバで定義したメソッドは、Tのポインタである*Tのメソッドセットにしか含まれません。あるinterfaceが要求するメソッドの中に1つでもポインタレシーバのものが含まれていれば、それを満たせるのは*T型の値だけです。

演習をはじめる

関連するエラー

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