Tin tức

Marker noCopy của Go: Cách go vet Phát hiện Sao chép Struct Không An toàn

Marker noCopy trong gói sync của Go không phải là quy tắc biên dịch—mà là một móc khéo léo cho trình kiểm tra copylocks của go vet. Đây là cách nó hoạt động và cách sử dụng nó trong các kiểu của riêng bạn.

August 17, 2026· 2 min read· Nguồn: Phuong Le
Marker noCopy của Go: Cách go vet Phát hiện Sao chép Struct Không An toàn

Các struct trong gói sync như sync.Mutex, sync.Oncesync.Map đều chứa một trường kiểu noCopy. Marker này là một struct rỗng với hai phương thức rỗng, LockUnlock. Nó không thêm bất kỳ quy tắc đặc biệt nào vào trình biên dịch Go—bạn vẫn có thể sao chép sync.Map sau khi sử dụng và go build vẫn sẽ vượt qua. Cảnh báo đến từ go vet, cụ thể là trình kiểm tra copylocks của nó.

Quy tắc của trình kiểm tra rất đơn giản: nó tìm kiếm một kiểu mà con trỏ của nó triển khai sync.Locker nhưng giá trị của nó thì không. Với noCopy, các phương thức nhận con trỏ làm cho *noCopy triển khai sync.Locker trong khi kiểu giá trị thì không. Trình kiểm tra đệ quy kiểm tra các trường struct, vì vậy nó tìm thấy noCopy bên trong sync.Map và báo cáo assignment copies lock value to b: sync.Map contains sync.noCopy.

Tại sao sync.Map cần marker tường minh khi nó đã chứa sync.Mutex? Ba lý do: nó làm cho cảnh báo rõ ràng hơn, nó không phụ thuộc vào triển khai nội bộ, và nó sửa một lỗi âm tính giả. Một kiểu như type LocalMutex sync.Mutex mất các phương thức Lock/Unlock, vì vậy trình kiểm tra sẽ bỏ sót nó nếu không có noCopy trong struct cơ bản.

Bạn có thể định nghĩa kiểu noCopy của riêng mình cho các gói của bạn. Chỉ cần sao chép mẫu:

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

type Session struct {
    _      noCopy
    id     string
    closed bool
}

Lưu ý rằng go test không chạy copylocks theo mặc định. Bạn cần chạy go vet ./... hoặc go test -vet=copylocks ./... để phát hiện các bản sao này.

Điều gì hỏng khi bạn bỏ qua cảnh báo? Tùy thuộc. Một sync.WaitGroup bị sao chép có thể khiến Wait() không bao giờ trả về, vì bộ đếm bị sao chép và Done() chỉ giảm bản sao. Sao chép một mutex có thể dẫn đến race dữ liệu và deadlock.

Marker noCopy được thêm vào năm 2016 bởi Aliaksandr Valialkin (CTO của VictoriaMetrics) dựa trên một mẫu của Russ Cox. Đây là một móc phân tích tĩnh khéo léo, không phải là lưới an toàn thời gian chạy.

Marker noCopy không thêm bất kỳ quy tắc đặc biệt nào vào trình biên dịch Go. Cảnh báo đến từ trình kiểm tra copylocks của go vet, tìm kiếm các kiểu mà con trỏ triển khai sync.Locker nhưng giá trị thì không.
Ban biên tập Manul X