下記の様なコードがあるとします。
package main
import "fmt"
type Greet interface {
sayGreet() string
}
type Hello struct {
Message string
}
func (h *Hello) sayGreet() string {
return h.Message
}
func createGreet() Greet {
return Hello{Message: "こんにちは"}//ここでコンパイルエラー
}
func main() {
g := createGreet()
fmt.Println(g.sayGreet())
}
今このコードは、createGreet関数のreturnの箇所でコンパイルエラーを起こしています。
なぜなのか。
createGreet関数の返り値はGreetインターフェイスです。
ということは
return Hello{Message: "こんにちは"}
上記で返される返り値がGreetインターフェイスを実装している型であれば問題ないはずです。
func (h *Hello) sayGreet() string {
return h.Message
}
上記の箇所で、Hello構造体はstring型を返すsayGreet()メソッドを実装しているため、HelloはGreetインターフェイスを実装しているように見えます。
しかし、よく見るとレシーバは 「(h *Hello)」 となっており、「Hello構造体を参照しているポインタ型」となっています。
あくまで、sayGreet()をメソッドとして保持している型は「Hello型」ではなく、「Hello型を参照しているポインタ型」となります。
ですので、createGreet()の返り値は「Hello型」ではなく、「Hello型を参照しているポインタ型」にしなければいけません。
下記のように修正します。
func createGreet() Greet {
return Hello{Message: "こんにちは"}//Hello型を返している。
}
⬇️ 修正
func createGreet() Greet {
return &Hello{Message: "こんにちは"}//Hello型を参照しているポインタ型を返している。
}
これでコンパイルエラーが解消され、プログラムが動くようになりました。
なお、新たな疑問として
func (h *Hello) sayGreet() string {
return h.Message
}
hはポインタ型(アドレスが格納されている。)なのに、なぜMessageをとってこれるのか?
これはGoが自動的にポインタの参照先の構造体からMessageの内容を取得してきてくれるからとなります。
もちろん下記のように、愚直に記載してもプログラムは正しく動作します。
func (h *Hello) sayGreet() string {
return (*h).Message //hが参照している構造体そのものからMessageを取得
}