receiver には2つの種類があり、1つは value, そして pointer です。
package main import ( "fmt" ) type document struct { title string author string } func (d document) display() { fmt.Printf("Title: %s, Author: %s\n", d.title, d.author, ) } func (d *document) setTitle(title string) { d.title = title } func main() { d1 := &document{"A project report", "John"} d1.display() d2 := document{"A sales report", "Paul"} d2.setTitle("A business report") d2.display() }上記の場合、display() は value receiver, setTitle() は pointer receiver を持ちます。
ここで document の instance が value / pointer に関わらず、2つの method が使えているように見えるのは、Go が method の receiver に合うようにしてくれるからです。
例えば d1.display() は (*d1).display() のように呼び出してくれます。
また、d2.setTitle() は (&d2).setTitle() といった具合です。
しかし、interface を使用する場合には注意が必要です。
package main import ( "fmt" ) type displayer interface { display() } type document struct { title string author string } func (d *document) display() { fmt.Printf("Title: %s, Author: %s\n", d.title, d.author, ) } func displayDocument(d displayer) { d.display() } func main() { d := document{"A project report", "John"} displayDocument(d) }
$ go build main.go ./main.go:29: cannot use d (type document) as type displayer in argument to displayDocument: document does not implement displayer (display method has pointer receiver)この場合、コンパイルがエラーになってしまいます。
d := document{"A project report", "John"}display() が pointer receiver なのに、d が value instance だからです。
Go のドキュメントには以下のようにあります。
The method set of any other type T consists of all methods declared with receiver type T. The method set of the corresponding pointer type *T is the set of all methods declared with receiver *T or T分かりづらいですが、value の method set は value receiver のみという制限があるんですね。pointer の method set には value receiver, pointer receiver どちらも使用できます。
どの type T の method set も receiver type T で宣言された method で構成されます。 pointer type *T に対応する method set は *T もしくは T で宣言された method set です。
d := &document{"A project report", "John"}このように修正すると、コンパイルが通るようになります。
なぜ、このような制限があるのかというと、アドレスが常に取得できるわけではないからです。
package main import ( "fmt" ) type price int func (p *price) appendYen() string { return fmt.Sprintf("\u00A5%d", *p) } func main() { fmt.Println(price(1000).appendYen()) } $ go build main.go ./main.go:14: cannot call pointer method on price(1000) ./main.go:14: cannot take the address of price(1000)