emahiro/b.log

Drastically Repeat Yourself !!!!

参照型変換メソッドを func で表現する

Overview

タイトルの通りなんですが、Go の struct をするときにその struct のあるフィールドを参照型にすることがあると思います。
この参照型のフィールドに値を代入するために、プリミティブな型を参照型に変換するだけの関数を作成することがあるかと思いますが、そのために unexported な toIntPtr みたいな関数を作成するのではなく func を使って代入することができるなーと思ったのでそのことについて書いてみます。

どう言うときに使うの?

主にテストを書くときのことを想定しています。

例えば、時刻を扱う表現で MySQL の DATETIME 型の初期値を NULL にしたいケースではフィールドの型を *time.Time にするケースは多くあります。こういった struct を使ったテストをするときにシンプルに時刻を生成してフィールドにアサインしようとするとコンパイルエラーが発生してしまいます(※)

time.Time*time.Time は別の型なので。

こう言うときにわざわざ参照型の変数をフィールドに代入するためだけに toDatetimePtr みたいなメソッドを定義することがありますが、実装で使うならまだしもテストの時だけに使うためにこういった参照型変換関数を用意するのは微妙です。

Example

type User struct {
    BirthDay *time.Time
}

みたいな User エンティティを使ったテストを書くときに

func TestUserXXXX(t *testing.T) {
    tests := [] struct {
        name string
        src  User
    }{
        {name: "xxxxx....",  src: User{ BirthDay: toDatetimePtr(time.Now())} },
    }
}

func toDatetimePtr(t time.Time) *time.Time {
    return &t
}

わざわざ time.Time の参照型である Birthday のためだけに toDatetimePtr を定義するのはちょっと微妙なので以下のようにしたらいいのではないかなと思いました。

func TestUserXXXX(t *testing.T) {
    tests := [] struct {
        name string
        src     User
    }{
        {
            name: "xxxxx....",  
            src: User{ 
                BirthDay: func(t time.Time) *time.Time {
                    return &t
                }(time.Now()),
            }, 
        },
    }
}

まとめ

これを書いてる最中にじゃあ参照型の型を新しく定義すれば?と思いましたが、いずれにしても利用用途が限定的なケースにおいてあんまり書きたいコードではないなと思いました。

Go では通常の実装においても func をそのまま使うと有用なケースが多いですね。

追伸

func の方が func を呼び出すコストがかかるので使いどころは選ばないといけません。
教えてもらった以下の Compier Explore によると確かに func を持っている時の方がコストが高いです。

https://go.godbolt.org/z/P9rWPP