[Go]interfaceを引数にとるときはポインタ渡しする必要はない
TL;DR
ポインタ渡ししなくても参照渡しされる。
- func Delete(item *model.ItemInterface) error
+ func Delete(item model.ItemInterface) error
先に挙動を確認
▼やったこと
interfaceを引数に取る関数 func Delete(item model.ItemInterface) error
に対して、
interfaceを実装した変数を渡して呼び出す Delete(c, concleteItem)
。
ここで、Delete関数内でのitemの挙動をいくつか確認する。
▼結果
(1)Delete関数の中でitemの値を更新すると、呼び出し元のconcleteItemの値も更新される
- ここから参照渡しされていることが分かる
(2)一方で、itemとconcleteItemは違うポインタを示す。また、*item のようにして値にアクセスすることはできない。
- これらはポインタ渡しした場合の挙動とは異なる。
値渡しした場合ともポインタ渡しした場合とも挙動が異なる。それはなぜかを説明する。
Interface型の引数には何が入っているのか?
Interface values are represented as a two-word pair giving a pointer to information about the type stored in the interface and a pointer to the associated data.
https://research.swtch.com/interfaces
すなわち、interfaceを引数に取る関数の中で、引数は以下の値を持つ値となっている。
(1) 値へのポインタ
(2) 値の型(=実装の型)
これで、上記の挙動(1)(2)の説明が付くことになる。
よって、ポインタ渡しをしなくても参照渡し出来るので、ポインタ渡しをする必要がない。
補足: 型アサーションについて
上記のinterface型の引数(という言い方が正しいのか分からないが)の仕様は、型アサーションにも関わる。
以下のような典型的な型アサーションコードを考える。
func Delete(item model.ItemInterface) error {
switch v := item.(type) {
case ConcleteItem:
...
case ConcleteItem2:
...
default:
...
}
}
このコードが何故動作するのか(どうやってitemの実装型を知るのか)というと、何のことはなく、item
変数がその実装の型情報をそのまま持っているからである。
Discussion