Go 语言编译期断言
这篇文章是关于一个鲜为人知的让 Go 在编译期断言的方法。你可能不会使用它,但是了解一下也很有趣。
作为一个热身,来看一个在 Go 中熟知的编译期断言:接口满意度检查。
在这段代码(playground)中,var _ =
行确保类型 W
是一个 stringWriter
,其由 io.WriteString
检查。
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'为性能回归编写一个脆弱的测试。
好的,让我们低调一点!
接口满意检查是很棒的。但是如果你想检查一个简单的布尔表达式,如 1 + 1 == 2
?
考虑这个代码(playground):
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 一起工作。如果你改变 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
是一个没有函数体的声明。编译器假定别人将提供一个实现,也许是一个汇编程序。
当编译器可以证明 len(Hash {})< md5.Size
时,它消除了 if 语句中的代码。结果,没有人使用函数hashIsTooSmall
,所以链接器会消除它。没有其他损害。一旦断言失败,if 语句中的代码将被保留。不会消除hashIsTooSmall
。链接器然后注意到没有人提供了函数的实现然后链接失败,并出现错误,这是我们的目标。
最后一个奇怪的点:为什么是 import "C"
? go 工具知道在正常的 Go 代码中,所有函数都必须有主体,并指示编译器强制执行。通过切换到 cgo,我们删除该检查。(如果你在上面的代码中运行 go build -x
,而没有添加 import "C"
这行,你会看到编译器是用 -complete
标志调用的。)另一种方法是添加 import "C"
来向包中添加一个名为 foo.s
的空文件。
我仅见过一次这种技术的使用,是在编译器测试套件中。还有其他可以发挥想象力的使用,但我还没见到过。
原文发布时间为:2017-03-28
本文来自云栖社区合作伙伴“Linux中国”
最后更新:2017-05-23 15:36:12
上一篇:
如何在 Linux 桌面上使用 Gifine 录制 GIF 动画?
下一篇:
机器学习初学者入门实践:怎样轻松创造高精度分类网络
CreateThread,_beginthread,_beginthreadx的区别
程序装载及执行说明
阿里云ECS通过docker配置MySQL--MGR
阿里云安全吴翰清:这次勒索病毒被低估,不重视安全的企业会“突然死亡”
利用Android 2.2新特性完成缩略图
Oracle中查看建立索引和使用索引的注意点
Ticket Lock的Relaxed Atomics优化
Andorid 关于PTRACE ptrace(PTRACE_TRACEME,0 ,0 ,0);
CentOS6.5 Redis3.x集群安装配置
【云周刊】第147期:解密天猫双11 1682亿背后的“霸下-七层流量清洗”系统