ある構造体型の変数の値をもう一方の変数にコピーするライブラリを書いた
書いたもの 👇
使用例
type Foo struct { Hoge string Fuga string piyo string } v1 := Foo{Hoge: "dummy", Fuga: "FUGA", piyo: "dummy"} v2 := Foo{Hoge: "HOGE"} ires, _ := mapstruct.Map(v1, v2) res := ires.(Foo) fmt.Println("%#v", res) // main.Foo{Hoge:"HOGE", Fuga:"FUGA", piyo:""}
こんな感じで、第 2 引数の構造体の各値を第 1 引数の構造体の対応するフィールドへ適用したものが interface{}
型として返ってくる。
以下のような条件の場合、そのフィールドは第 1 引数へ適用されない
- 非公開なフィールド
- 値がゼロ値
なので、上記の例のように、第 1 引数の Fuga
フィールドに非ゼロ値が入っていて、第 2 引数の Fuga
フィールドにはゼロ値が入っている場合、第 2 引数の値は適用されずに、そのまま第 1 引数の値が使われる。
どちらのフィールドも非ゼロ値の場合、第 2 引数の値が使われる。
何に使うか?
このライブラリの使用例としては、なにか設定値を表す構造体があり、アプリケーションが Git のようにローカルとグローバルの設定値を持てる場合などに使える。
例えば、以下のような設定値を表す構造体があるとする。
type Core struct { Editor string } type User struct { Name string EMail string } type Config struct { Core *Core User *User }
ここで、ローカル値を優先しつつ、ローカルで何も指定していない項目はグローバルで設定している値を使いたい。
この時に mapstruct
を使う。
localConfig := &Config{ User: &User{ Name: "ktr", EMail: "ktr@example.com", }, } globalConfig := &Config{ User: &User{ Name: "dummy", EMail: "dummy@example.com", }, Core: &Core{ Editor: "/usr/bin/vim", }, } ic, _ := mapstruct.Map(globalConfig, localConfig) config := ic.(*Config) pp.Println(config)
すると以下のような構造体が返ってくることがわかる。
&main.Config{ Core: &main.Core{ Editor: "/usr/bin/vim", }, User: &main.User{ Name: "ktr", EMail: "ktr@example.com", }, }
ちゃんと両方の値が適用されてて便利。
実際にこのライブラリは Evans でも使っている。(というかそのためにライブラリを書いた)
このライブラリがやっていることを手動でやろうとすると、どうしても設定値が増えたときなどにバグを生みやすいのでこれを使うことでそういったことを防ぐことができると思う。