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億背後的“霸下-七層流量清洗”係統