注:該文做者是 Katrina Owen,原文地址是 iota: Elegant Constants in Golanghtml
有些概念有名字,而且有時候咱們關注這些名字,甚至(特別)是在咱們代碼中。golang
const ( CCVisa = "Visa" CCMasterCard = "MasterCard" CCAmericanExpress = "American Express" )
在其餘時候,咱們僅僅關注能把一個東西與其餘的作區分。有些時候,有些時候一件事沒有本質上的意義。好比,咱們在一個數據庫表中存儲產品,咱們可能不想以 string 存儲他們的分類。咱們不關注這個分類是怎樣命名的,此外,該名字在市場上一直在變化。數據庫
咱們僅僅關注它們是怎麼彼此區分的。c#
const ( CategoryBooks = 0 CategoryHealth = 1 CategoryClothing = 2 )
使用 0, 1, 和 2 代替,咱們也能夠選擇 17, 43, 和 61。這些值是任意的。ide
常量是重要的,可是它們很難推斷,而且難以維護。在一些語言中像 Ruby 開發者一般只是避免它們。在 Go,常量有許多微妙之處。當用好了,能夠使得代碼很是優雅且易維護的。函數
在 golang 中,一個方便的習慣就是使用 iota
標示符,它簡化了常量用於增加數字的定義,給以上相同的值以準確的分類。學習
const ( CategoryBooks = iota // 0 CategoryHealth // 1 CategoryClothing // 2 )
自增加常量常常包含一個自定義類型,容許你依靠編譯器。spa
type Stereotype int const ( TypicalNoob Stereotype = iota // 0 TypicalHipster // 1 TypicalUnixWizard // 2 TypicalStartupFounder // 3 )
若是一個函數以 int
做爲它的參數而不是 Stereotype
,若是你給它傳遞一個 Stereotype
,它將在編譯器期出現問題。code
func CountAllTheThings(i int) string { return fmt.Sprintf("there are %d things", i) } func main() { n := TypicalHipster fmt.Println(CountAllTheThings(n)) } // output: // cannot use TypicalHipster (type Stereotype) as type int in argument to CountAllTheThings
相反亦是成立的。給一個函數以 Stereotype
做爲參數,你不能給它傳遞 int
。htm
func SoSayethThe(character Stereotype) string { var s string switch character { case TypicalNoob: s = "I'm a confused ninja rockstar." case TypicalHipster: s = "Everything was better we programmed uphill and barefoot in the snow on the SUTX 5918" case TypicalUnixWizard: s = "sudo grep awk sed %!#?!!1!" case TypicalStartupFounder: s = "exploit compelling convergence to syndicate geo-targeted solutions" } return s } func main() { i := 2 fmt.Println(SoSayethThe(i)) } // output: // cannot use i (type int) as type Stereotype in argument to SoSayethThe
這是一個戲劇性的轉折,儘管如此。你能夠傳遞一個數值常量,而後它能工做。
func main() { fmt.Println(SoSayethThe(0)) } // output: // I'm a confused ninja rockstar.
這是由於常量在 Go 中是弱類型直到它使用在一個嚴格的上下文環境中。
設想你在處理消費者的音頻輸出。音頻可能不管什麼都沒有任何輸出,或者它多是單聲道,立體聲,或是環繞立體聲的。
這可能有些潛在的邏輯定義沒有任何輸出爲 0,單聲道爲 1,立體聲爲 2,值是由通道的數量提供。
因此你給 Dolby 5.1 環繞立體聲什麼值。
一方面,它有6個通道輸出,可是另外一方面,僅僅 5 個通道是全帶寬通道(所以 5.1 稱號 - 其中 .1
表示的是低頻效果通道)。
無論怎樣,咱們不想簡單的增長到 3。
咱們能夠使用下劃線跳過不想要的值。
type AudioOutput int const ( OutMute AudioOutput = iota // 0 OutMono // 1 OutStereo // 2 _ _ OutSurround // 5 )
iota
能夠作更多事情,而不單單是 increment。更精確地說,iota
老是用於 increment,可是它能夠用於表達式,在常量中的存儲結果值。
這裏咱們建立一個常量用於位掩碼。
type Allergen int const ( IgEggs Allergen = 1 << iota // 1 << 0 which is 00000001 IgChocolate // 1 << 1 which is 00000010 IgNuts // 1 << 2 which is 00000100 IgStrawberries // 1 << 3 which is 00001000 IgShellfish // 1 << 4 which is 00010000 )
這個工做是由於當你在一個 const
組中僅僅有一個標示符在一行的時候,它將使用增加的 iota
取得前面的表達式而且再運用它,。在 Go 語言的 spec 中, 這就是所謂的隱性重複最後一個非空的表達式列表。
若是你對雞蛋,巧克力和海鮮過敏,把這些 bits 翻轉到 「on」 的位置(從左到右映射 bits)。而後你將獲得一個 bit 值 00010011
,它對應十進制的 19。
fmt.Println(IgEggs | IgChocolate | IgShellfish) // output: // 19
這是在 Effective Go 中一個很是好定義數量級的示例:
type ByteSize float64 const ( _ = iota // ignore first value by assigning to blank identifier KB ByteSize = 1 << (10 * iota) // 1 << (10*1) MB // 1 << (10*2) GB // 1 << (10*3) TB // 1 << (10*4) PB // 1 << (10*5) EB // 1 << (10*6) ZB // 1 << (10*7) YB // 1 << (10*8) )
今天我學習到了在 zettabyte 以後是 yottabyte。
當你在把兩個常量定義在一行的時候會發生什麼?
Banana 的值是什麼? 2 仍是 3? Durian 的值又是?
const ( Apple, Banana = iota + 1, iota + 2 Cherimoya, Durian Elderberry, Fig )
iota
在下一行增加,而不是當即取得它的引用。
// Apple: 1 // Banana: 2 // Cherimoya: 2 // Durian: 3 // Elderberry: 3 // Fig: 4
這搞砸了,由於如今你的常量有相同的值。
所以,對的
在 Go 中,關於常量有不少東西能夠說,你應該在 golang 博客讀讀 Rob Pike 的這篇文章。