iota 在 Go 中的使用

文章持續更新,微信搜一搜「  吳親強的深夜食堂 」

介紹

Go 語言實際上沒有直接支持枚舉的關鍵字。通常咱們都是經過 const + iota 實現枚舉的能力。微信

有人要問了,爲何必定要使用枚舉呢?stackoverflow 上有一個高讚的回答,以下:app

You should always use enums when a variable (especially a method parameter) can only take one out of a small set of possible values. Examples would be things like type constants (contract status: "permanent", "temp", "apprentice"), or flags ("execute now", "defer execution"). If you use enums instead of integers (or String codes), you increase compile-time checking and avoid errors from passing in invalid constants, and you document which values are legal to use.BTW, overuse of enums might mean that your methods do too much (it's often better to have several separate methods, rather than one method that takes several flags which modify what it does), but if you have to use flags or type codes, enums are the way to go.

簡單翻譯一下, 兩點很重要。函數

  • 當一個變量(尤爲是方法參數) 只能從一小部分可能的值中取出一個時,理應使用枚舉。
    例如類型常量(合同狀態:永久、臨時工、學徒), 或者在作任務程序時,是當即執行仍是延遲執行的標記。
  • 若是使用枚舉而不是整形,則會增長編譯時的檢查,避免錯誤無效值的傳入,記錄哪些值是合法使用的。

如何實現枚舉

iota 是 Go 中預聲明的一個特殊常量。它會被預聲明爲0,可是它的值在編譯階段並不是是固定的,當預聲明的 iota 出如今一個常量聲明中的時候,它的值在第n個常量描述中的值爲n(從0開始)。因此它只在同類型多個常量聲明的狀況下才顯得有意義。翻譯

好比,你們都瞭解的電商,訂單系統必定會涉及到訂單狀態的流轉。那麼這時候,咱們通常能夠這樣作:code

package main

import "fmt"

type OrderStatus int

const (
  Cancelled OrderStatus = iota //訂單已取消 0  NoPay OrderStatus = iota //未支付  1  PendIng OrderStatus = iota // 未發貨 2  Delivered OrderStatus = iota // 已發貨 3  Received OrderStatus = iota // 已收貨 4)

func main() {
  fmt.Println(Cancelled, NoPay) // 打印:0,1
}

固然,這樣看着好麻煩。其實,其餘常量能夠重複上一行 iota 表達式,咱們能夠改爲這樣。ci

package main

import "fmt"

type OrderStatus int

const (
  Cancelled OrderStatus = iota //訂單已取消 0  NoPay //未支付 1  PendIng // 未發貨 2  Delivered // 已發貨 3  Received // 已收貨 4)

func main() {
  fmt.Println(Cancelled, NoPay) // 打印:0,1
}

有人會用 0 的值來表示狀態嗎?通常都不會,咱們想以1開頭,那麼能夠這樣。字符串

package main

import "fmt"

type OrderStatus int

const (
  Cancelled OrderStatus = iota+1 //訂單已取消 1
  NoPay //未支付 2  PendIng // 未發貨 3  Delivered // 已發貨 4  Received // 已收貨 5)

func main() {
  fmt.Println(Cancelled, NoPay) // 打印:1,2
}

咱們還想在 Delivered 後跳過一個數字,纔是 Received 的值,也就是 Received=6,那麼能夠藉助 _ 符號。源碼

package main

import "fmt"

type OrderStatus int

const (
  Cancelled OrderStatus = iota+1 //訂單已取消 1
  NoPay //未支付 2  PendIng // 未發貨 3  Delivered // 已發貨 4  _
 Received // 已收貨 6)

func main() {
  fmt.Println(Received) // 打印:6
}

順着來能夠,倒着固然也行。string

package main

import "fmt"

type OrderStatus int

const (
  Max = 5)

const (
  Received OrderStatus = Max - iota // 已收貨  5  Delivered // 已發貨 4  PendIng // 未發貨 3  NoPay //未支付 2  Cancelled //訂單已取消 1)

func main() {
  fmt.Println(Received,Delivered) // 打印:5,4
}

你還能夠使用位運算,好比在 go 源碼中的包 sync 中的鎖上面有這麼一段代碼。it

const (
 mutexLocked = 1 << iota  //1<<0 mutexWoken               //1<<1 mutexStarving            //1<<2 mutexWaiterShift = iota  //3
)

func main() {
 fmt.Println("mutexLocked的值",mutexLocked) //打印:1 fmt.Println("mutexWoken的值",mutexWoken) //打印:2 fmt.Println("mutexStarving的值",mutexStarving) //打印:4 fmt.Println("mutexWaiterShift的值",mutexWaiterShift) // 打印:3}

可能有人日常是直接定義常量值或者用字符串來表示的。

好比,上面這些我徹底能夠用 string 來表示,我還真見過用字符串來表示訂單狀態的。

package main

import "fmt"

const (
  Cancelled = "cancelled"  NoPay = "noPay"  PendIng = "pendIng"  Delivered = "delivered"  Received = "received")

var OrderStatusMsg = map[string]string{
  Cancelled: "訂單已取消",
  NoPay:     "未付款",
  PendIng:   "未發貨",
  Delivered: "已發貨",
  Received:  "已收貨",
}

func main() {
  fmt.Println(OrderStatusMsg[Cancelled])
}

或者直接定義整形常量值。

package main

import "fmt"

const (
  Cancelled = 1  NoPay = 2  PendIng = 3  Delivered = 4  Received = 5)

var OrderStatusMsg = map[int]string{
  Cancelled: "訂單已取消",
  NoPay:     "未付款",
  PendIng:   "未發貨",
  Delivered: "已發貨",
  Received:  "已收貨",
}

func main() {
  fmt.Println(OrderStatusMsg[Cancelled])
}

其實上述兩種均可以,可是相比之下使用 iota 更有優點。

  • 能保證一組常量的惟一性,人工定義的不能保證。
  • 能夠爲一組動做分享同一種行爲。
  • 避免無效值。
  • 提升代碼閱讀性以及維護。

延伸

按照上面咱們所演示的,最後咱們能夠這樣操做。

package main

import (
 "fmt")

type OrderStatus int

const (
  Cancelled OrderStatus = iota + 1 //訂單已取消 1  NoPay //未支付 2  PendIng // 未發貨 3  Delivered // 已發貨 4  Received // 已收貨 5)

//公共行爲 賦予類型 String() 函數,方便打印值含義
func (order OrderStatus) String() string { return [...]string{"cancelled", "noPay", "pendIng", "delivered", "received"}[order-1]
}

//建立公共行爲 賦予類型 int 函數 EnumIndex()
func (order OrderStatus) EnumIndex() int { return int(order)
}

func main() {
 var order OrderStatus = Received
  fmt.Println(order.String())    // 打印:received
  fmt.Println(order.EnumIndex()) // 打印:5
}

總結

這篇文章主要介紹了 Golang 中對 iota 的使用介紹,以及咱們爲何要使用它。

不知道你們日常對於此類場景是用的什麼招數,歡迎下方留言交流。

相關文章
相關標籤/搜索