👋

GO言語のinterfaceとstructでハマったのでまとめてみる

2023/05/16に公開

今までPHPメインだったけどGO言語の勉強を始めてinterfacestruct
これで動くだろって適当に書いたらエラーで止まることが何度かあったのでまとめてみる

GO言語初心者なので当たり前のことを書いてると思うけど温かい目で見守ってほしい
こうしたほうがいいよというアドバイスなどあればコメントもらえると嬉しいです

実装環境など

  • go version go1.20.3

structでinterfaceの使用

エラーで動かいけどこんなコードを書いてみる

main.go
type hoge interface {
    SetNum(num int)
    GetNum() int
}

type fuga struct {
    num int
}

func (f *fuga) SetNum(num int) {
    f.num = num
}

func (f fuga) GetNum() int {
    return f.num
}

func Add(h hoge, num int) hoge {
    num += h.GetNum()
    h.SetNum(num)
    return h
}

func main() {
    f := fuga{10}
    h := Add(f, 30)
    fmt.Println(h.GetNum())
}

hogeというinterfaceを作りfugaというstructで使えるようにして
Addの引数をhogeにして加算するだけのもの

実行しようとするとこのようなエラーが

どうやらfugaのSetNumメソッドがポインタレシーバーなのがダメらしい

でもmainのAddで渡すのをポインタにすると実行できる

main.go
func main() {
    f := fuga{10}
    Add(&f, 30)
    fmt.Println(f.GetNum())
}
実行結果
$ go run main.go
40

SetNumのポインタレシーバーをやめれば実行はできるけどfugaの値を変更できないので想定と違う動きになってしまう

メソッドなどでインターフェスにしない場合は普通に実行できる

main.go
30 func main() {
    f := fuga{10}
    num := f.GetNum() + 30
    f.SetNum(num)
    fmt.Println(f.GetNum())
}
実行結果
$ go run main.go
40

まとめ

interfaceのメソッドでポインタレシーバーを使用する場合はポインタ変数を使用する

interface型のメンバー変数

structにinreface型を定義して用途別でそこにstructを設定してみてまたもやエラーに

main.go
type hoge struct {
    data interface{}
}

type fuga struct {
    num int
}

func newHoge(fn func() interface{}) hoge {
    return hoge{fn()}
}

func main() {
    fn := func() interface{} {
        return fuga{10}
    }
    h := newHoge(fn)
    fmt.Println(h.data.num)
}

hogeのdataはinterface型なのでnumは無いとのこと
それはそうだよねPHPのmixed的なものかと思ったら全然違うものだった

型アサーションで型変換してあげればいいだけの話だけどPHPになれすぎてあのような書き方をしてしまった

main.go
func main() {
    fn := func() interface{} {
        return fuga{10}
    }
    h := newHoge(fn)
    f := h.data.(fuga)
    fmt.Println(f.num)
}
実行結果
$ go run ./main.go
10

まとめ

当たり前だけどinterface型は勝手に別の型になったりしない
使用するときは型アサーションで変換するなどする

その他

これはなれの問題だけど最初のコードでAddfugaのポインタを渡す場合
Addの引数のinterfaceであるhogeをポインタ指定してしまう

こんな感じのエラーになるものをついつい書いてしまう

main.go
func Add(h *hoge, num int) hoge {
    num += h.GetNum()
    h.SetNum(num)
    return h
}

func main() {
    f := fuga{10}
    h := Add(&f, 30)
    fmt.Println(h.GetNum())
}

一部翻訳すると「h.GetNum undefined (型 hoge はインタフェースへのポインタであり,インタフェースではない)」とのこと
インタフェースへのポインタ
ポインタ*なのでインタフェースではないとすごく当たり前のことが書かれてる
なるほどね

Discussion