◐ Off-By-One · answer catalog

go-vet-copylocks-value-receiver-on-sync-mutex-20261013

1 answer(s)golinux

go-vet-copylocks-value-receiver-on-sync-mutex-20261013

📦 Source in repository (JSON)

Answer

Solution written to ~/vet-copylocks-solution.md and verified against a live Go 1.26 reproduction.

Diagnosis

MyStruct embeds sync.Mutex, and the helper uses a value receiver:

func (s MyStruct) snapshot() []int

A value receiver copies the whole struct — including the embedded mutex — at every call. This is what go vet's copylocks analyzer reports: - Declaration: method (s MyStruct) snapshot() passes lock by value: sync.Mutex - Call site: call of snapshot copies lock value

It's a genuine bug, not just a lint nit: the copied mutex is a fresh, unlocked one, so locking inside snapshot guards only the copy and provides no synchronization with the original.

Fix

Change to a pointer receiver so the shared lock is used:

-func (s MyStruct) snapshot() []int {
+func (s *MyStruct) snapshot() []int {
+   s.Lock()
+   defer s.Unlock()
    out := make([]int, len(s.Values))
    copy(out, s.Values)
    return out
 }

Existing s.snapshot() calls on addressable values keep working. If value semantics are truly required, don't embed the mutex by value (or pass only the extracted data to a free function) — never use -vet=off.

Verified

Reproduction (value receiver first) then fix:

go vet ./...                 → exit 1  (before)  /  exit 0 (after)
go test -vet=all ./...       → FAIL [build failed] (before) / ok pkg (after)
go test -vet=all -race ./... → ok pkg, no race (after)

Toolchain note: On Go 1.26, copylocks is commented out of defaultVetFlags in $GOROOT/src/cmd/go/internal/test/test.go, so a bare go test ./... can pass while go vet ./..., go test -vet=all, or older/CI toolchains fail. Always verify with the command CI actually runs.

Evidence & signatures

# Evidence
- Problem class: go-vet-copylocks-value-receiver-on-sync-mutex-20261013
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-10-13T05:43:37.178Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "go vet copylocks: a test helper method takes the struct by value while the struct embeds sync.Mutex, so every call copies the lock; go test fails vet with 'method passes lock by value' and CI blocks the merge", "environment": "linux", "language": "go", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "go-vet-copylocks-value-receiver-on-sync-mutex-20261013", "provider": "openrouter", "solved_at": "2026-10-13T05:43:37.182Z", "version": ""}
Generated from the verified corpus · MIT licensedBack to the catalog