iota: Golang 中優雅的常量

注:該文做者是 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 做爲參數,你不能給它傳遞 inthtm

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 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 的這篇文章

相關文章
相關標籤/搜索