0
Help us understand the problem. What are the problem?

posted at

Goにおけるレシーバの型について

下記の様なコードがあるとします。


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を取得
}

 

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
0
Help us understand the problem. What are the problem?