“This is the ninth day of my participation in the First Challenge 2022. For details: First Challenge 2022”

An enumerated type is a data type that consists of a set of values. In Go, there is no such keyword as enum. However, the best way to handle a set of values is with type aliases and constants. But we can’t achieve the level of security that other languages can. This is why we have to be careful when dealing with enumerated values. Let’s look at some related practices and how to avoid some common mistakes.

Here is a list of the days of the week:

type Weekday intconst (
    Monday Weekday = 0(2) Tuesday Weekday =1
    Wednesday Weekday = 2
    Thursday  Weekday = 3
    Friday    Weekday = 4
    Saturday  Weekday = 5
    Sunday    Weekday = 6
)
Copy the code

① Define a custom Weekday type

Create a Modany constant of type Weekday

The advantage of creating a Weekday type is that it forces compile-time type checking and improves readability. If we didn’t create a Weekday type, the following function signature might be a bit obscure to the caller:

func GetCurrentWeekday(a) int {
	// ...
}
Copy the code

An int can contain any value, and the reader cannot guess what value the function returns without reading the relevant documentation or code. Instead, defining a Weekday type makes the signature of the function clearer:

func getCurrentWeekday(a) Weekday {
	// ...
}
Copy the code

In this example, we enforce the return type.

The way we create enumerated values of type Weekday is appropriate. However, there is a common way to declare constants in enumerations in Go, and that is to use the constant generator IOTA

Note: In this example, we can also declare Weekday as a uint32 to enforce a positive value and ensure that each Weekday variable is assigned 32 bits.

iota

Iota is used to create a series of related values without explicitly setting them. It instructs the compiler to copy each constant expression until the block ends or assignment is found.

Here is the Weekday version using IOTA:

type Weekday int
const (
    Monday Weekday = iota① Tuesday Wednesday Thursday Friday Saturday SundayCopy the code

① Use IOTA to define enumeration values

The value of ITOA starts at 0 and increments by 1 per row. This version is equivalent to the first version:

  • Monday = 0
  • Tuesday = 1
  • Wednesday = 3
  • etc

Using IOTA allows us to avoid manually defining constant values. For example, manually setting constant values in large enumerations can be error-prone. Further, we don’t have to repeat the Weekday type for every variable: all variables we define are of a Weekday type.

Note: We can use IOTA in more complex expressions. Here is an example of handling ByteSize enumeration values from Effective Go:

type ByteSize float64
const(_ =iota1 KB ByteSize =1< < (10 * iota) ② MB ③ GB TB PB EB ZB YB)Copy the code

Iota = 1, so KB is set to 1 << (10 * 1). Iota = 2, so MB is set to 1 << (10 * 2).

Let’s look at how unknown values are handled in enumerations of Go

Unknow value

Now that we understand how enumeration values work in Go, let’s consider the following example. We will implement an HTTP processing to decode a JSON-formatted Request into a Request structure type. The structure will contain an Unknown value of type Weekday. Here is the implementation of the first version:

type Weekday intconst (
	Monday Weekday = iotaTuesday Wednesday Thursday Friday Saturday Sunday Unknown ②)type Request struct{(3) IDint `json:"id"`
	Weekday Weekday `json:"weekday"`
}

func httpHandler(w http.REsponseWriter, r *http.Request){④ bytes, err! = readBody (r) (5)iferr ! =nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	
	varRequest Request err = json.Unmarshal(bytes, &request) ⑥iferr ! =nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	
	// Use Request
}
Copy the code

① Reuse the Weekday enumeration values we defined

② Define Unknown constants

③ Define a Request structure containing Weekday fields

④ Implement an HTTP processor

⑤ Read the request body and return a []byte

⑥ Decode the JSON request body

In this example, we create a Request structure that is decoded from a JSON Request body. This code is very complete and effective. In our example, we can take a JSON content and decode it correctly:

{
	"id": 1234."weekday": 0
}
Copy the code

Here, the Weekday field will be equal to 0: Monday.

Now, what if you don’t include the Weekday field in your JSON content?

{
	"Id": 1235
}
Copy the code

Parsing this content will not raise any errors. However, the weekday field value in the Request structure will be set to an int: 0 value. So, just like Monday in the last request.

So how do we tell the difference between a request that passed Monday and a request that didn’t pass the Weekday field? The problem has to do with the way we define Weekday enumerations. In fact, Unknown is the last value of the enumerated value. So it should be equal to 7.

To solve this problem, the best practice for dealing with an enumeration value of unknown is to set it to 0 (the zero value of type int). Therefore, we should animate the Weekday enumeration as follows:

type Weekday int

const (
	Unknown Weekday = iotaMonday Tuesday Wednesday Thursday Friday Saturday SundayCopy the code

① Unknown is now equal to zero

If weekday is empty in the JSON request body, it will be parsed to Unknown; That’s what we need.

As a rule of thumb, the unknown value of an enumeration should be set to zero for the enumeration type. In this way, we can distinguish between displayed values and missing values.