Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

タイトルそのままです。

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を使うようにしたほうが良さそうです。

t10471
mercari
フリマアプリ「メルカリ」を、グローバルで開発しています。
https://tech.mercari.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away