はじめに
ありし日の自分は以下のコードが正常に動作することに疑問を感じてました。
type Dog struct {
Name string
}
func (d *Dog) Speak() {
fmt.Printf("Woof, I am %s\n", d.Name)
}
func main() {
dog1 := &Dog{Name: "hoge"}
dog1.Speak() // => Woof, I am hoge
dog2 := Dog{Name: "fuga"}
dog2.Speak() // => Woof, I am fuga
}
「*Dog
というポインタ型に対してSpeak()
は定義されているけど、Dog
という値型にはSpeak()
定義されてないのだから、dog2.Speak()
はpanicするはずでは?」と脳内にはてなマークを浮かべてました。
疑問を先輩にぶつけてみたところ、腑に落ちる説明を頂いたので備忘録としてまとめておきます。
結論
「コンパイラによる暗黙的変換が行われているから」です。
ポインタ型に対して定義してあるメソッドを値型の変数から呼び出そうすると、コンパイラが自動でポインタ型のメソッド呼び出しに変換してくれます。
コンパイラ君さては頭いいです。
func main() {
dog2 := Dog{Name: "fuga"}
dog2.Speak() // => (&dog2).Speak()にコンパイラが変換してくれる
}
また、この変換は値型に対して定義してあるメソッドをポインタ型の変数から呼び出そうとした場合も行われます。
func (d Dog) Speak() {
fmt.Printf("Woof, I am %s\n", d.Name)
}
func main() {
dog1 := &Dog{Name: "hoge"}
dog1.Speak() // => (*dog1).Speak()にコンパイラが変換してくれる
}
終わりに
コンパイラ君頭ヨシヨシですね。
GO言語を学び始めてまだ1ヶ月も経っていないので、簡単なものになるかもしれませんがこれからも疑問点を記事にしていきたいと思っています。これ以降の記事も読んでもらえたら嬉しいです。