一些编译时刻断言技巧

以下文章来源于Go 101 ,作者老貘

有时候,我们希望在编译时刻就能够判断某些条件是否成立,而不是等到运行时刻报错。但是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) 
3