LoginSignup
1
2

More than 3 years have passed since last update.

Goでオブジェクト生成の返り値にinterfaceを使うとnil Pointer Receiverができないという話

Posted at

タイトルそのままです。

package main

import (
    "fmt"
    "reflect"
)

type Fooer interface {
    Foo()
}

type Fooo struct{}

func (f *Fooo) Foo() {
    if f == nil {
        fmt.Println("nil!!")
    } else {
        fmt.Println("not nil!!")
    }
}

func newFooer() Fooer {
    return &Fooo{}
}

func bar(f Fooer) {
    f.Foo()
}

func main() {
    // 型を代入したあとnilを入れても型情報は消えない
    z := &Fooo{}
    fmt.Println(reflect.TypeOf(z)) // *main.Fooo
    z = nil
    fmt.Println(reflect.TypeOf(z)) // *main.Fooo
    z.Foo()                        // nil!!
    bar(z)                         // nil!!

    // 型情報を持ったnilを入れて、そのあとただのnilを入れても型情報は消えない
    y := (*Fooo)(nil)
    fmt.Println(reflect.TypeOf(y)) // *main.Fooo
    y = nil
    fmt.Println(reflect.TypeOf(y)) // *main.Fooo
    y.Foo()                        // nil!!
    bar(y)                         // nil!!

    // interfaceを返す関数から変数を代入したあとにnilを入れると型情報が消える
    x := newFooer()
    fmt.Println(reflect.TypeOf(x)) // *main.Fooo
    x = nil
    fmt.Println(reflect.TypeOf(x)) // <nil>
    // panicになる x.Foo()
    // panicになる bar(x)
}

発端はクリーンアーキテクチャをやろうと思いサンプルを探すと、この例のように生成系でinterfaceを返しているものが多いです。
依存関係の逆転をしたいので、接合部分をinterfaceにするというのは素直だと思いますが、Goでは微妙なところもありそうです。

Go言語のInterfaceの考え方、Accept interfaces,return structsに従ったほうが、大体は良さそう。

クリーンアーキテクチャをするならAccept interfaces,return structs に従って生成してからDIで渡すところでinterfaceを使うようにしたほうが良さそうです。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2