存在一些情况,GoLang 中的一些结构体不应被复制。例如,有时全局配置应该只有一个版本在整个应用程序中传递,而不应该被复制和修改。
在 GoLang 中,没有直观的解决方案来防止结构体的复制。但是仍然有一些方法可以用来帮助防止这种情况在开发代码时发生。技巧是定义一些实现sync.Locker
接口的结构体,并将此结构体嵌入到任何不应被复制的结构体中。
// noCopy 可以嵌入到必须在第一次使用后不被复制的结构体中。
//
// 详情请参见 https://golang.org/issues/8005#issuecomment-190753527
// 。
type noCopy struct{}
// Lock 是 `go vet` 中 -copylocks 检查器使用的空操作。
func (*noCopy) Lock() {}
func (*noCopy) UnLock() {}
下面是一个解释上述内容的示例代码片段。
package main
import (
"fmt"
)
type noCopy struct{}
func (*noCopy) Lock() {}
func (*noCopy) Unlock() {}
type Demo struct {
noCopy noCopy
}
func Copy(d Demo) {
}
func main() {
d := Demo{}
fmt.Printf("%+v", d)
Copy(d)
fmt.Printf("%+v", d)
}
现在,如果您在 main.go 文件上运行go vet
,您将看到一些错误,告诉您某些地方复制了结构体(Copy()
)。
> go vet main.go
# command-line-arguments
.\main.go:16: Copy 按值传递锁:main.Demo 包含 main.noCopy
.\main.go:21: fmt.Printf 的调用复制锁值:main.Demo 包含 main.noCopy
.\main.go:23: Copy 的调用复制锁值:main.Demo 包含 main.noCopy
.\main.go:25: fmt.Printf 的调用复制锁值:main.Demo 包含 main.noCopy
这里需要注意的一点是,上述方法不会告诉编译器编译失败。如果您继续编译并运行它,它仍然可以工作。这只是为go vet
提供了另一个提示,通常我们在构建项目之前运行它来检查我们的源代码中可能存在的问题。
> go run main.go
{noCopy:{}}{noCopy:{}}
参考: