Go | nocopy机制
Mar 9, 2024
如何保证nocopy
runtime checking
strings.Builder中copy检查
func main()
// 运行报错:panic: strings: illegal use of non-zero Builder copied by value
源码
type Builder struct
func ()
// Write appends the contents of p to b's buffer.
// Write always returns len(p), nil.
func (p []byte) (int, error)
addr是一个指向自身的指针。当a复制给b时,a和b本身是不同的对象。因此,b.addr实际还是指向a的指针,这就会触发条件b.addr!=b,造成panic。
sync.Cond中copy检查
type Cond struct
type copyChecker uintptr
func ()
定义一个相似的结构体对象,来探究这里的check函数究竟是如何做copy检查的。
type cond struct
type copyChecker uintptr
func ()
func main()
// 输出
Before: c: 0xc0000b4008, *c: 0, uintptr(*c): 0, uintptr(unsafe.Pointer(c)): 824634458120
After : c: 0xc0000b4008, *c: 824634458120, uintptr(*c): 824634458120, uintptr(unsafe.Pointer(c)): 824634458120, swapped: true
Before: c: 0xc0000b4040, *c: 824634458120, uintptr(*c): 824634458120, uintptr(unsafe.Pointer(c)): 824634458176
After : c: 0xc0000b4040, *c: 824634458120, uintptr(*c): 824634458120, uintptr(unsafe.Pointer(c)): 824634458176, swapped: false
当a被bcopy之后,uintptr(*c)和uintptr(unsafe.Pointer(c))的值是不同的,通过uint对象的原子比较方法CompareAndSwapUintptr将返回false,它证明了对象a被copy过,从而调用panic保护sync.Cond不被复制。
go vet checking
// noCopy may be added to structs which must not be copied
// after the first use.
//
// See https://golang.org/issues/8005#issuecomment-190753527
// for details.
//
// Note that it must not be embedded, due to the Lock and Unlock methods.
type noCopy struct
// Lock is a no-op used by -copylocks checker from `go vet`.
func ()
func ()
runtime时的copy检查虽然很重要,但是,该操作会影响程序的执行性能。
对于其他需要nocopy对象类型来说,使用go vet工具来做静态编译检查。
具体实施来说,就是该对象,或对象中存在filed,它拥有Lock()和Unlock()方法,即实现sync.Locker接口。之后,可以通过go vet功能,来检查代码中该对象是否有被copy。
静态检查
// wg.go
package main
import "sync"
func main()
该代码运行时,不会报错,但是却存在安全隐患。
# command-line-arguments
因此,定义某对象不能被copy,就可以嵌入noCopy结构体,最终通过go vet进行copy检查。
type noCopy struct
func ()
func ()
type MyType struct