文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Golang如何实现不被复制的结构体

2023-07-05 19:53

关注

这篇文章主要介绍“Golang如何实现不被复制的结构体”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Golang如何实现不被复制的结构体”文章能帮助大家解决问题。

不允许复制的结构体

sync包中的许多结构都是不允许拷贝的,比如sync.Cond,sync.WaitGroup,sync.Pool, 以及sync包中的各种锁,因为它们自身存储了一些状态(比如等待者的数量),如果你尝试复制这些结构体:

var wg1 sync.WaitGroupwg2 := wg1 // 将 wg1 复制一份,命名为 wg2// ...

那么你将在你的 IDE 中看到一个醒目的警告:

assignment copies lock value to wg2: sync.WaitGroup contains sync.noCopy

IDE是如何实现这一点的呢?我们自己又能否利用这一机制来告诉别人,不要拷贝某个结构体呢?

(懒得看原理,只想知道怎么用,可以直接下划至结论部分)

实现原理

大部分编辑器/IDE都会在你的代码上运行go vet,vet是Go官方提供的静态分析工具,我们刚刚得到的提示信息就是vet分析代码后告诉我们的。vet的实现在Go源码的cmd/vet中,里面注册了很多不同类型的分析器,其中copylock这个分析器会检查实现了LockUnlock方法的结构体是否被复制。

copylock Analysercmd/vet中注册,具体实现代码在golang.org/x/tools/go/analysis/passes/copylock/copylock.go中, 这里只摘抄部分核心代码进行解释:

var lockerType *types.Interfacefunc init() {    //...    methods := []*types.Func{        types.NewFunc(token.NoPos, nil, "Lock", nullary),        types.NewFunc(token.NoPos, nil, "Unlock", nullary),    }    // Locker 结构包括了 Lock 和 Unlock 两个方法    lockerType = types.NewInterface(methods, nil).Complete()}

init函数中把包级别的全局变量lockerType进行了初始化,lockerType内包含了两个方法: LockUnlock, 只有实现了这两个方法的结构体才是copylock Analyzer要处理的对象。

// lockPath 省略了参数部分,只保留了最核心的逻辑,// 用来检测某个类型是否实现了Locker接口(Lock和Unlock方法)func lockPath(...) typePath {    // ...    // 如果传进来的指针类型实现了Locker接口, 就返回这个类型的信息    if types.Implements(types.NewPointer(typ), lockerType) && !types.Implements(typ, lockerType) {        return []string{typ.String()}    }    // ...}

lockPath会检测传入的参数是否实现了LockUnlock方法,如果是则返回类型的信息。而vet会在AST上每个需要检查的节点上调用lockPath函数(如赋值、函数调用等场景)。如果在这些会导致复制的场景中,发现了锁结构体的复制,则会报告给用户:

func run(pass *analysis.Pass) (interface{}, error) {    // ...    // 需要检查的节点    switch node := node.(type) {    // range语句    case *ast.RangeStmt:        checkCopyLocksRange(pass, node)    // 函数声明    case *ast.FuncDecl:        checkCopyLocksFunc(pass, node.Name.Name, node.Recv, node.Type)    // 函数字面量(匿名函数)    case *ast.FuncLit:        checkCopyLocksFunc(pass, "func", nil, node.Type)    // 调用表达式(Foo(xxx))    case *ast.CallExpr:        checkCopyLocksCallExpr(pass, node)    // 赋值语句    case *ast.AssignStmt:        checkCopyLocksAssign(pass, node)    // 通用声明(import/const/type/var)    case *ast.GenDecl:        checkCopyLocksGenDecl(pass, node)    // 复合常量({a,b,c})    case *ast.CompositeLit:        checkCopyLocksCompositeLit(pass, node)    // return语句    case *ast.ReturnStmt:        checkCopyLocksReturnStmt(pass, node)    // ...}// checkCopyLocksAssign 检查赋值操作是否复制了一个锁func checkCopyLocksAssign(pass *analysis.Pass, as *ast.AssignStmt) {    for i, x := range as.Rhs {        // 如果等号右边的结构体里有字段实现了Lock/Unlock的话,就输出警告信息        if path := lockPathRhs(pass, x); path != nil {            pass.ReportRangef(x, "assignment copies lock value to %v: %v", analysisutil.Format(pass.Fset, as.Lhs[i]), path)        }    }}

上面只列出了赋值操作的实现代码,其它类型的检查这里就不一一解释了,感兴趣的同学可以自行查看源码。

结论

只要你的IDE会帮你运行go vet(目前主流的VSCode和GoLand都会自动帮你运行),你就能通过这个机制来提醒他人,尽量避免复制结构体。

如果你的结构体也因为某些原因,不希望使用者复制,你也可以使用该机制来警告使用者:

定义一个实现了LockUnlock的结构体

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

将其放入你的结构体中:

// Foo 代表你不希望别人复制的结构体type Foo struct {    noCopy noCopy    // ...}

或直接让你的结构体实现LockUnlock方法:

type Foo struct {    // ...}func (*Foo) Lock()   {}func (*Foo) Unlock() {}

这样别人在尝试复制Foo的时候,就会得到IDE的警告信息了。

关于“Golang如何实现不被复制的结构体”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程网行业资讯频道,小编每天都会为大家更新不同的知识点。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯