Goでxxxのポインタを取っているプログラムはだいたい全部間違っている

  • 157
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

Goで、文字列、インターフェイス、チャネル、マップ、スライスのポインタを取っているプログラムは、書いた本人がきちんと自分がなにをしているのか理解しているのでなければ、ほぼ確実に間違っているといっていい。

Goではある種の型の値はそもそもポインタのようなものである。上記の型はどれも任意の大きさになり得るが、大きくなりうる実体のデータはヒープに確保されていて、値そのものが持っているのはそのヒープ上への値へのただのポインタ+多少の付随的なデータにすぎない。こういった値を値渡しではなくポインタ渡しする必要はない。ポインタのデリファレンスのほうがポインタのコピーより高くつくし、余計な混乱を引き起こすだけだからだ。もしこういう値をポインタ渡ししているとしたら、そのコードはなにか深い意味があるのではなく、それを書いた人が大きな値がコピーされると勘違いしていて書いた可能性のほうがずっと高い。

文字列は2ワードの値で、文字列実体の先頭へのポインタと文字列の長さ(バイト長)からできている。どちらのワードも書き換えることはできない。Goでは文字列は不変だから、コピーを渡そうがそれへのポインタを渡そうができることに違いはなく、文字列を渡すときはポインタのポインタを渡す(文字列へのポインタを渡す)より素直に文字列(実体へのポインタ)を値渡しするほうがよい。

インターフェイスも2ワードの値で、実際の値の型へのポインタと実際の値へのポインタからできている。インターフェイスを書き換えることも普通はできない(インターフェイス型の値が指している先をすげ替えたりインターフェイスが指している値の型を変更したりすることは普通はありえない)。従って値渡しするほうが素直だ。

チャネル、マップも同じだ。

スライスもやはり同じである。スライスは3ワードの値で、実体のデータへのポインタと、配列のキャパシティ、それから長さの3つの値からできている。スライスは要は配列へのポインタなので、これもわざわざポインタへのポインタを渡すのではなく値渡しするほうが普通だ。

というわけで、こういう型の値は正当な理由なくポインタをとったりしないことに注意されたい。