Go

Goでコピーされたくない値がコピーされたことを検出する方法

More than 3 years have passed since last update.

値の同一性が重要なときは、値を(うっかり)コピーすることができないようにしたいことがある。C++プログラマならデフォルトのコピーコンストラクタをprivateにして(あるいはC++11の=deleteを使って)、値をコピーしようとするコードをコンパイルできないようにするところだが、Goでは言語レベルである型の値のコピーを禁止する方法はない。従ってコンパイル時にそのようなコードを検出する方法はない。

代わりにと言ってはなんだが、実行時に検出することはできる。

たまに見かけるやり方は、構造体のメンバにポインタを用意しておいて、そこにその値自身のポインタを入れるという方法だ。あとはメソッドが呼ばれるたびにその値をチェックして、レシーバのアドレスとポインタの値が異なっていたらpanicなどでバグを通知すればよい。

コードで書くと次のような感じだ。

type nonCopyable struct {

mu sync.Mutex
p *nonCopyable
}

func (n *nonCopyable) check() {
// n.pのアクセスを排他制御
n.mu.Lock()
defer n.mu.Unlock()
if n.p == nil {
// checkが初回に呼ばれたときは初期化
n.p = n
} else if n != n.p {
panic("nonCopyable is copied")
}
}

排他制御のコストが気になる場合はsync.atomicのアトミック変数を使って効率よく同じことを行うこともできる。