[Go] 关于禁止结构体被复制的一点想法

陪她去流浪 桃子 2018年10月08日 编辑 阅读次数:3370

几个月前刚学Go语言不久的时候,我问群里的人怎么禁止Go的结构体被复制。

一些人回答说没有办法。另一些人则说干嘛要禁止,你不复制不就好了吗?

不想做过多无用的争辩,因为有时候真的需要禁止复制啊!

在C++里面,可以通过使拷贝构造函数私有化等方式来使用语法提供的支持来禁止复制:

1
2
3
4
5
6
private:
    Value(const Value&);

// C++11及以后也可以:

Value(const Value&) = delete;

然而在Go里面,没有构造函数,也没有析构函数。所以,没有办法!

当然,一些tricks也不是没有的。

比如,strings.Builder中是这样来做的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
func (b *Builder) copyCheck() {
	if b.addr == nil {
		// This hack works around a failing of Go's escape analysis
		// that was causing b to escape and be heap allocated.
		// See issue 23382.
		// TODO: once issue 7921 is fixed, this should be reverted to
		// just "b.addr = b".
		b.addr = (*Builder)(noescape(unsafe.Pointer(b)))
	} else if b.addr != b {
		panic("strings: illegal use of non-zero Builder copied by value")
	}
}

这个函数会在几乎每次导出函数被调用的时候进行一次调用。 你可以复制该结构体,但是,它如果发现addr保存的地址不是本身的话,就会在运行时panic,智障。

再比如sync.Mutex中的一句话

1
2
3
4
5
6
7
8
// A Mutex is a mutual exclusion lock.
// The zero value for a Mutex is an unlocked mutex.
//
// A Mutex must not be copied after first use.
type Mutex struct {
	state int32
	sema  uint32
}

你没看错,must not be copied,就一句话告诉使用者:一定不能被复制。你要复制?可以,你尽管复制,运行时不崩溃算我输。

当然,还有更狠的,如Go101所言:

bytes.Buffer根本不会检测是否被复制。你唯一能做的,就是:小心!不要复制!

Go语言棒棒哒。

标签:Go