這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
註:該文作者是 Katrina Owen,原文地址是 iota: Elegant Constants in Golang
有些概念有名字,並且有時候我們關注這些名字,甚至(特別)是在我們代碼中。
const ( CCVisa = "Visa" CCMasterCard = "MasterCard" CCAmericanExpress = "American Express")
在其他時候,我們僅僅關注能把一個東西與其他的做區分。有些時候,有些時候一件事沒有本質上的意義。比如,我們在一個資料庫表中儲存產品,我們可能不想以 string 儲存他們的分類。我們不關注這個分類是怎樣命名的,此外,該名字在市場上一直在變化。
我們僅僅關注它們是怎麼彼此區分的。
const ( CategoryBooks = 0 CategoryHealth = 1 CategoryClothing = 2)
使用 0, 1, 和 2 代替,我們也可以選擇 17, 43, 和 61。這些值是任意的。
常量是重要的,但是它們很難推斷,並且難以維護。在一些語言中像 Ruby 開發人員通常只是避免它們。在 Go,常量有許多微妙之處。當用好了,可以使得代碼非常優雅且易維護的。
自增長
在 golang 中,一個方便的習慣就是使用 iota
標示符,它簡化了常量用於增長數位定義,給以上相同的值以準確的分類。
const ( CategoryBooks = iota // 0 CategoryHealth // 1 CategoryClothing // 2)
自訂類型
自增長常量經常包含一個自訂類型,允許你依靠編譯器。
type Stereotype intconst ( TypicalNoob Stereotype = iota // 0 TypicalHipster // 1 TypicalUnixWizard // 2 TypicalStartupFounder // 3)
如果一個函數以 int
作為它的參數而不是 Stereotype
,如果你給它傳遞一個 Stereotype
,它將在編譯器期出現問題。
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
。
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 中是弱類型直到它使用在一個嚴格的上下文環境中。
Skipping Values
設想你在處理消費者的音訊輸出。音頻可能無論什麼都沒有任何輸出,或者它可能是單聲道,立體聲,或是環繞立體聲的。
這可能有些潛在的邏輯定義沒有任何輸出為 0,單聲道為 1,立體聲為 2,值是由通道的數量提供。
所以你給 Dolby 5.1 環繞立體聲什麼值。
一方面,它有6個通道輸出,但是另一方面,僅僅 5 個通道是全頻寬通道(因此 5.1 稱號 - 其中 .1
表示的是低頻效果通道)。
不管怎樣,我們不想簡單的增加到 3。
我們可以使用底線跳過不想要的值。
type AudioOutput intconst ( OutMute AudioOutput = iota // 0 OutMono // 1 OutStereo // 2 _ _ OutSurround // 5)
運算式
iota
可以做更多事情,而不僅僅是 increment。更精確地說,iota
總是用於 increment,但是它可以用於運算式,在常量中的儲存結果值。
這裡我們建立一個常量用於位元遮罩。
type Allergen intconst ( 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 float64const ( _ = 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 的這篇文章。