Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nocopy minimal example?

Tags:

I've been trying to get a noCopy directive to work for one of my own structs, but I cannot get go vet to detect it.

I can get it to detect copying of sync.WaitGroup, and sync.Mutex, but not my own struct. This test file in the vet source doesn't even trigger with my go vet.

Or well, it finds some of the errors:

# command-line-arguments
./govet.go:56:6: no new variables on left side of :=
./govet.go:110:17: unsafe.Sizeof(mu) evaluated but not used
./govet.go:111:18: unsafe.Sizeof(mu) evaluated but not used
./govet.go:112:10: unsafe.Sizeof(mu) evaluated but not used

but the copylock detection doesn't find anything.

Did something change in go vet since this discussion at 1.4? I'm running go version go1.11 darwin/amd64.

like image 359
Filip Haglund Avatar asked Sep 25 '18 09:09

Filip Haglund


1 Answers

First, copying locks is properly detected by go vet. Example:

type My struct {
    l sync.Mutex
}

Usage:

func main() {
    m := My{}
    m2 := m
    fmt.Println(m2)
}

Running go vet, the output is:

./play.go:25: assignment copies lock value to m2: main.My contains sync.Mutex
./play.go:26: call of fmt.Println copies lock value: main.My contains sync.Mutex

So both cases (assigning and passing to fmt.Println()) were detected.

This also means that the easiest way to make your struct a target of vet when copied, simply add a field of type sync.Mutex. This is a ready solution, although it consumes memory (sync.Mutex is not a zero-size struct). It doesn't matter whether you use this mutex or not (we didn't use it in the example above).

In the discussion you referenced Rob Pike suggests to create a type:

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

And use a field of this type (either regular or embedded) to mark a struct non-copiable (and so make go vet scream if that happens).

I don't know if this has ever worked, but currently it doesn't, because go vet checks for the sync.Locker interface, which also has an Unlock() method:

type Locker interface {
        Lock()
        Unlock()
}

So if we create a noCopy type that implements sync.Locker (more precisely its pointer type), that will work:

type noCopy struct{}

func (*noCopy) Lock()   {}
func (*noCopy) Unlock() {}

type By struct {
    noCopy noCopy
}

Testing it:

func main() {
    b := By{}
    b2 := b
    fmt.Println(b2)
}

Running go vet:

./play.go:29: assignment copies lock value to b2: main.By contains main.noCopy
./play.go:30: call of fmt.Println copies lock value: main.By contains main.noCopy

Here are some changes related to go vet and noCopy:

Go 1.7

Go 1.8

like image 159
icza Avatar answered Nov 02 '22 23:11

icza