Go | nocopy机制



runtime checking


func main() {
    var a strings.Builder
    b := a
// 运行报错:panic: strings: illegal use of non-zero Builder copied by value


type Builder struct {
	addr *Builder // of receiver, to detect copies by value
	buf  []byte

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")

// Write appends the contents of p to b's buffer.
// Write always returns len(p), nil.
func (b *Builder) Write(p []byte) (int, error) {
	b.buf = append(b.buf, p...)
	return len(p), nil



type Cond struct {
	noCopy noCopy

	// L is held while observing or changing the condition
	L Locker

	notify  notifyList
	checker copyChecker

type copyChecker uintptr

func (c *copyChecker) check() {
	// Check if c has been copied in three steps:
	// 1. The first comparison is the fast-path. If c has been initialized and not copied, this will return immediately. Otherwise, c is either not initialized, or has been copied.
	// 2. Ensure c is initialized. If the CAS succeeds, we're done. If it fails, c was either initialized concurrently and we simply lost the race, or c has been copied.
	// 3. Do step 1 again. Now that c is definitely initialized, if this fails, c was copied.
	if uintptr(*c) != uintptr(unsafe.Pointer(c)) &&
		!atomic.CompareAndSwapUintptr((*uintptr)(c), 0, uintptr(unsafe.Pointer(c))) &&
		uintptr(*c) != uintptr(unsafe.Pointer(c)) {
		panic("sync.Cond is copied")


type cond struct {
    checker copyChecker

type copyChecker uintptr

func (c *copyChecker) check() {
    fmt.Printf("Before: c: %12v, *c: %12v, uintptr(*c): %12v, uintptr(unsafe.Pointer(c)): %12v\n", c, *c, uintptr(*c), uintptr(unsafe.Pointer(c)))
    swapped := atomic.CompareAndSwapUintptr((*uintptr)(c), 0, uintptr(unsafe.Pointer(c)))
    fmt.Printf("After : c: %12v, *c: %12v, uintptr(*c): %12v, uintptr(unsafe.Pointer(c)): %12v, swapped: %12v\n", c, *c, uintptr(*c), uintptr(unsafe.Pointer(c)), swapped)

func main() {
    var a cond
    b := a

// 输出
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


go vet checking

// noCopy may be added to structs which must not be copied
// after the first use.
// See
// 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 (*noCopy) Lock()   {}
func (*noCopy) Unlock() {}


对于其他需要nocopy对象类型来说,使用go vet工具来做静态编译检查。

具体实施来说,就是该对象,或对象中存在filed,它拥有Lock()Unlock()方法,即实现sync.Locker接口。之后,可以通过go vet功能,来检查代码中该对象是否有被copy


// wg.go
package main

import "sync"

func main() {
	var sm sync.Mutex
	sm2 := sm


$ go vet wg.go
 # command-line-arguments
./wg.go:9:9: assignment copies lock value to sm2: sync.Mutex

因此,定义某对象不能被copy,就可以嵌入noCopy结构体,最终通过go vet进行copy检查。

type noCopy struct{}
func (*noCopy) Lock() {}
func (*noCopy) Unlock() {}
type MyType struct {
   noCopy noCopy