Go | nocopy机制
2024-03-09
如何保证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