一些编译时刻断言技巧

2019-07-13 11:41:52 +08:00
 liulaomo

有时候,我们希望在编译时刻就能够判断某些条件是否成立,而不是等到运行时刻报错。但是 Go 并没有提供在编译时刻直接判断某个条件是否成立的功能。那么该如何做到这一点呢?

事实上,我们可以利用容器类型的组合字面值中的常量键值和下标不能重复这一规则来实现编译时刻断言。比如,下面这两个映射组合字面是编译通不过的:

// error: 键值 true 重复了
var _ = map[bool]int{true: 1, true: 2}
// error: 键值 false 重复了
var _ = map[bool]int{false: -1, false: 0}

而下面这个是没问题的:

var _ = map[bool]int{true: 1, false: 0}

利用这一规则,我们可以在编译时刻断言任意常量表达式条件。

用例一:有时候一个程序明确不支持 32 架构,则可以在代码中加入下面这行来防止此程序在 32 位的架构上被编译出可执行程序。

var _ = map[bool]int{true: 1, ^uint(0) >> 63 == 0: 0}

其中的^uint(0) >> 63为另外一个编译时刻的小技巧。它的估值结果在 32 位的架构上为 0,而在 64 位的架构上为 1。

用例二:保证某个整数设置N(一个常量)必须大于等于另一个整数设置M(也是一个常量)。

var _ = map[bool]int{true: 1, N >= M: 0}

用例三:保证某个常量字符串S不为空。

var _ = map[bool]int{true: 1, len(S) == 0: 0}

上面几个用例都比较简单,在实践中,被断言的条件可以很复杂,只要此条件可以在编译时刻被估值即可。

其实,对于上面第二个和第三个用例,它们各自还有若干编译时刻断言方法。比如,对于第二个用例,我们可以使用下面的各个方法来断言整数设置N大于等于另一个整数设置M

func _(x []int) {_ = x[N-M]}
func _(){_ = []int{N-M: 0}}
func _([N-M]int){}
var _ [N-M]int
const _ uint = N-M
type _ [N-M]int
var _ uint = N/M - 1

对于第三个用例,我们也可以使用下面的各个方法来断言一个字符串常量不为空。

var _ = map[bool]int{false: 0, S != "": 1}
type _ [len(S)-1]int
var _ = S[:1]
var _ = S[0]
const _ = 1/len(S)

本文首发在微信 Go 101 公众号,欢迎各位转载本文。Go 101 公众号将尽量在每周发表一篇原创短文,有意关注者请扫描下面的二维码。

关于更多 Go 语言编程中的事实、细节和技巧,请访问《 Go 语言 101 》官方网站:https://gfw.go101.org。如果官网被墙,请访问《 Go 语言 101 》 github 项目:https://github.com/golang101/golang101

685 次点击
所在节点    Go 编程语言
2 条回复
secondwtq
2019-07-13 13:19:42 +08:00
C++11 有了 static_assert,方便很多
Go 其实也可以加入类似的东西,文中方法挺 hack 的
不过我猜 Go 设计团队应该不会认为这是一个有效的需求…
slanternsw
2019-07-13 20:58:53 +08:00
ugly hack....

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/582600

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX