値の同一性が重要なときは、値を(うっかり)コピーすることができないようにしたいことがある。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のアトミック変数を使って効率よく同じことを行うこともできる。