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
的空檔案。
我僅見過一次這種技術的使用,是在編譯器測試套件中。還有其他可以發揮想象力的使用,但我還沒見到過。
可能就是這樣吧。 :)
via: http://commaok.xyz/post/compile-time-assertions
作者:Josh Bleecher Snyder 譯者:geekpi 校對:wxy
相關文章
- go語言編譯過程概述Go編譯
- Go語言交叉編譯工具goxGo編譯
- Go語言內幕(2):深入 Go 編譯器Go編譯
- [譯] Go 語言實戰: 編寫可維護 Go 語言程式碼建議Go
- 【譯】Go語言宣告語法Go
- 編譯型語言與解釋型語言編譯
- C語言編譯工具C語言編譯
- 幽默:Go語言的編譯器 - programmerjoke9Go編譯
- badamczewski/PowerUp:Rust/Go語言的反編譯工具RustGo編譯
- [翻譯] Go 語言入門Go
- 源語言、目標語言、翻譯器、編譯器、直譯器編譯
- 解釋型語言、編譯型語言 區別編譯
- Go語言————1、初識GO語言Go
- 使用Go語言從零編寫PoS區塊鏈(譯)Go區塊鏈
- C語言 - 條件編譯C語言編譯
- Go 語言實戰: 編寫可維護 Go 語言程式碼建議Go
- java編譯、編碼、語言設定Java編譯
- Uber Go 語言編碼規範Go
- Go語言變數的生命週期Go變數
- GO語言Go
- Go語言專案編譯之後找不到配置檔案Go編譯
- Python 既是解釋型語言,也是編譯型語言Python編譯
- 解釋型語言與編譯型語言的區別?編譯
- Python 語言特性:編譯+解釋、動態型別語言、動態語言Python編譯型別
- c語言多檔案編譯C語言編譯
- C語言編譯全過程C語言編譯
- GO語言————2、GO語言環境安裝Go
- [譯]Go語言記憶體佈局Go記憶體
- GO型別斷言Go型別
- Go語言序列化(Gob編碼)Go
- 【Go語言入門系列】(八)Go語言是不是面嚮物件語言?Go物件
- 計算機語言:編譯型/解釋型、動態語言/靜態語言、強型別語言/弱型別語言計算機編譯型別
- Go語言封裝、繼承、介面、多型和斷言的案例Go封裝繼承多型
- 編譯語言、解釋語言與指令碼語言之間的區別編譯指令碼
- go 語言常量Go
- Go語言mapGo
- go 語言切片Go
- go語言使用Go