有时候,我们希望在编译时刻就能够判断某些条件是否成立,而不是等到运行时刻报错。但是 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。
1
secondwtq 2019-07-13 13:19:42 +08:00 via iPad
C++11 有了 static_assert,方便很多
Go 其实也可以加入类似的东西,文中方法挺 hack 的 不过我猜 Go 设计团队应该不会认为这是一个有效的需求… |
2
slanternsw 2019-07-13 20:58:53 +08:00 via Android
ugly hack....
|