【Golang】枚举(enumeration)

Posted by 西维蜀黍 on 2020-03-15, Last Modified on 2021-09-21

枚举(enumeration)

Golang 语言并没有提供enum的定义,我们可以使用const来模拟枚举类型。

type PolicyType int32

const (
    Policy_MIN      PolicyType = 0
    Policy_MAX      PolicyType = 1
    Policy_MID      PolicyType = 2
    Policy_AVG      PolicyType = 3
)

这里定义了一个新的类型PolicyType,并且定义了4个常量(Policy_MIN, Policy_MAX, Policy_MID, Policy_AVG),类型是PolicyType。

使用举例

func foo(p PolicyType) {
    fmt.Printf("enum value: %v\n", p)
}

func main() {
    foo(Policy_MAX)
}

iota

  • The iota keyword represents successive integer constants 0, 1, 2,…
  • It resets to 0 whenever the word const appears in the source code,
  • and increments after each const specification.
const (
    C0 = iota
    C1 = iota
    C2 = iota
)
fmt.Println(C0, C1, C2) // "0 1 2"

This can be simplified to

const (
	C0 = iota
	C1
	C2
)

Here we rely on the fact that expressions are implicitly repeated in a paren­thesized const declaration – this indicates a repetition of the preceding expression and its type.

Start from one

To start a list of constants at 1 instead of 0, you can use iota in an arithmetic expression.

const (
    C1 = iota + 1
    C2
    C3
)
fmt.Println(C1, C2, C3) // "1 2 3"

Or

const (
	_   int = iota // Skip the first value of 0
	Foo            // Foo = 1
	Bar            // Bar = 2
	_
	_
	Bin // Bin = 5
	// Using a comment or a blank line will not increment the iota value

	Baz // Baz = 6
)

Skip value

You can use the blank identifier to skip a value in a list of constants.

const (
    C1 = iota + 1
    _
    C3
    C4
)
fmt.Println(C1, C3, C4) // "1 3 4"

Complete enum type with strings [best practice]

Here’s an idiomatic way to implement an enumerated type:

  • create a new integer type,
  • list its values using iota,
  • give the type a String function.
type Direction int

const (
    North Direction = iota
    East
    South
    West
)

func (d Direction) String() string {
    return [...]string{"North", "East", "South", "West"}[d]
}

In use:

var d Direction = North
fmt.Print(d)
switch d {
case North:
    fmt.Println(" goes up.")
case South:
    fmt.Println(" goes down.")
default:
    fmt.Println(" stays put.")
}
// Output: North goes up.

Bitmask


const (
	read   = 1 << iota // 00000001 = 1
	write              // 00000010 = 2
	remove             // 00000100 = 4

	// admin will have all of the permissions
	admin = read | write | remove
)

By definition, multiple uses of iota in the same ConstSpec all have the same value:

const (
	bit0, mask0 = 1 << iota, 1<<iota - 1  // bit0 == 1, mask0 == 0  (iota == 0)
	bit1, mask1                           // bit1 == 2, mask1 == 1  (iota == 1)
	_, _                                  //                        (iota == 2, unused)
	bit3, mask3                           // bit3 == 8, mask3 == 7  (iota == 3)
)

Misc

const (
	a = 1 << iota  // a == 1  (iota == 0)
	b = 1 << iota  // b == 2  (iota == 1)
	c = 3          // c == 3  (iota == 2, unused)
	d = 1 << iota  // d == 8  (iota == 3)
)

const (
	u         = iota * 42  // u == 0     (untyped integer constant)
	v float64 = iota * 42  // v == 42.0  (float64 constant)
	w         = iota * 42  // w == 84    (untyped integer constant)
)

const x = iota  // x == 0
const y = iota  // y == 0
type ByteSize float64

const (
	_           = iota // ignore first value by assigning to blank identifier
	KB ByteSize = 1 << (10 * iota)
	MB
	GB
	TB
	PB
	EB
	ZB
	YB
)

Naming convention

The standard naming convention is to use mixed caps also for constants.

For example, an exported constant is NorthWest, not NORTH_WEST.

Creating names automatically

# install the stringer tool
go get -u -a golang.org/x/tools/cmd/stringer# grab the code from here 
# weekday.go
package main

import (
	"fmt"
)

// Declare a new type named Weekday which will unify our enum values
// It has an underlying type of unsigned integer (uint).
type Weekday int

// Declare typed constants each with type of Weekday
const (
   Sunday    Weekday = 0
   Monday    Weekday = 1
   Tuesday   Weekday = 2
   Wednesday Weekday = 3
   Thursday  Weekday = 4
   Friday    Weekday = 5
   Saturday  Weekday = 6
)

// String returns the name of the day
func (day Weekday) String() string {
    names := [...]string{"Sunday", "Monday", "Tuesday", "Wednesday",
        "Thursday", "Friday", "Saturday"}

    return names[day]
}

// Weekend return true for a weekend day
func (day Weekday) Weekend() bool {
    switch day {
    case Sunday, Saturday:
        return true
    default:
        return false
    }
}

func main() {
    // Which day it is? Sunday
	fmt.Printf("Which day it is? %s\n", Sunday)
	
	// Is Saturday a weekend day? true
	fmt.Printf("Is Saturday a weekend day? %t\n", Saturday.Weekend())
	
	// Is Monday a weekend day? false
	fmt.Printf("Is Monday a weekend day? %t\n", Monday.Weekend())
}
# enum names automatically
stringer -type Weekday weekdays.go

Reference