51学通信论坛2017新版

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 1526|回复: 0
打印 上一主题 下一主题

Go 语言编译期断言

[复制链接]

 成长值: 15625

  • TA的每日心情
    开心
    2022-7-17 17:50
  • 2444

    主题

    2544

    帖子

    7万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    74104
    跳转到指定楼层
    楼主
    发表于 2017-11-15 16:13:05 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式


    这篇文章是关于一个鲜为人知的让 Go 在编译期断言的方法。你可能不会使用它,但是了解一下也很有趣。 -- Josh Bleecher Snyder
    编译自: http://commaok.xyz/post/compile-time-assertions
    作者: Josh Bleecher Snyder
    译者: geekpi

    这篇文章是关于一个鲜为人知的让 Go 在编译期断言的方法。你可能不会使用它,但是了解一下也很有趣。
    作为一个热身,来看一个在 Go 中熟知的编译期断言:接口满意度检查。
    在这段代码(playground[1])中,var _ = 行确保类型 W 是一个 stringWriter,其由 io.WriteString[2] 检查。
    package main
    import "io"
    type W struct{}
    func (w W) Write(b []byte) (int, error) { return len(b), nil }
    func (w W) WriteString(s string) (int, error) { return len(s), nil }
    type stringWriter interface {
    WriteString(string) (int, error)
    }
    var _ stringWriter = W{}
    func main() {
    var w W
    io.WriteString(w, "very long string")
    }

    如果你注释掉了 W 的 WriteString 方法,代码将无法编译:
    main.go:14: cannot use W literal (type W) as type stringWriter in assignment:
    W does not implement stringWriter (missing WriteString method)

    这是很有用的。对于大多数同时满足 io.Writer 和 stringWriter 的类型,如果你删除 WriteString 方法,一切都会像以前一样继续工作,但性能较差。
    你可以使用编译期断言保护你的代码,而不是试图使用`testing.T.AllocsPerRun'[3]为性能回归编写一个脆弱的测试。
    这是一个实际的 io 包中的技术例子[4]。
    好的,让我们低调一点!
    接口满意检查是很棒的。但是如果你想检查一个简单的布尔表达式,如 1 + 1 == 2 ?
    考虑这个代码(playground[5]):
    package main
    import "crypto/md5"
    type Hash [16]byte
    func init() {
    if len(Hash{}) < md5.Size {
    panic("Hash is too small")
    }
    }
    func main() {
    // ...
    }

    Hash 可能是某种抽象的哈希结果。init 函数确保它将与 crypto/md5[6] 一起工作。如果你改变 Hash 为(比如说)[8]byte,它会在进程启动时发生崩溃。但是,这是一个运行时检查。如果我们想要早点发现怎么办?
    如下。(没有 playground 链接,因为这在 playground 上不起作用。)
    package main
    import "C"
    import "crypto/md5"
    type Hash [16]byte
    func hashIsTooSmall()
    func init() {
    if len(Hash{}) < md5.Size {
    hashIsTooSmall()
    }
    }
    func main() {
    // ...
    }

    现在如果你改变 Hash 为 [8]byte,它将在编译过程中失败。(实际上,它在链接过程中失败。足够接近我们的目标了。)
    $ go build .
    # demo
    main.hashIsTooSmall: call to external function
    main.init.1: relocation target main.hashIsTooSmall not defined
    main.init.1: undefined: "main.hashIsTooSmall"

    这里发生了什么?
    hashIsTooSmall 是一个没有函数体的声明[7]。编译器假定别人将提供一个实现,也许是一个汇编程序。
    当编译器可以证明 len(Hash {})< md5.Size 时,它消除了 if 语句中的代码。结果,没有人使用函数 hashIsTooSmall,所以链接器会消除它。没有其他损害。一旦断言失败,if 语句中的代码将被保留。不会消除 hashIsTooSmall。链接器然后注意到没有人提供了函数的实现然后链接失败,并出现错误,这是我们的目标。
    最后一个奇怪的点:为什么是 import "C"? go 工具知道在正常的 Go 代码中,所有函数都必须有主体,并指示编译器强制执行。通过切换到 cgo,我们删除该检查。(如果你在上面的代码中运行 go build -x,而没有添加 import "C" 这行,你会看到编译器是用 -complete 标志调用的。)另一种方法是添加 import "C" 来向包中添加一个名为 foo.s 的空文件[8]。
    我仅见过一次这种技术的使用,是在编译器测试套件[9]中。还有其他可以发挥想象力的使用[10],但我还没见到过。
    可能就是这样吧。 :)
    via: http://commaok.xyz/post/compile-time-assertions
    作者:Josh Bleecher Snyder[11] 译者:geekpi 校对:wxy
    本文由 LCTT 原创编译,Linux中国 荣誉推出

      [1]: playground - https://play.golang.org/p/MJ6zF1oNsX
      [2]: io.WriteString - https://golang.org/pkg/io/#WriteString
      [3]: `testing.T.AllocsPerRun' - https://golang.org/pkg/testing/#AllocsPerRun
      [4]: 一个实际的 io 包中的技术例子 - https://github.com/golang/go/blob/go1.8rc2/src/io/multi.go#L72
      [5]: playground - https://play.golang.org/p/mjIMWsWu4V
      [6]: crypto/md5 - https://golang.org/pkg/crypto/md5/
      [7]: 一个没有函数体的声明 - https://golang.org/ref/spec#Function_declarations
      [8]: 向包中添加一个名为 foo.s 的空文件 - https://github.com/golang/go/blob/go1.8rc2/src/os/signal/sig.s
      [9]: 编译器测试套件 - https://github.com/golang/go/blob/go1.8rc2/test/fixedbugs/issue9608.dir/issue9608.go
      [10]: 可以发挥想象力的使用 - https://github.com/golang/go/blob/go1.8rc2/src/runtime/hashmap.go#L261
      [11]: Josh Bleecher Snyder - https://twitter.com/commaok


    声明:本文转载自网络。版权归原作者所有,如有侵权请联系删除。
    扫描并关注51学通信微信公众号,获取更多精彩通信课程分享。

    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?立即注册

    x
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    Archiver|手机版|小黑屋|51学通信技术论坛

    GMT+8, 2025-2-2 01:44 , Processed in 0.084291 second(s), 33 queries .

    Powered by Discuz! X3

    © 2001-2013 Comsenz Inc.

    快速回复 返回顶部 返回列表