DEV Community

Cover image for Implementing Enums in Golang
Ankit malik
Ankit malik

Posted on

Implementing Enums in Golang

Introduction

Enums, short for enumerations, are a powerful concept in programming that allow developers to define a set of named values, each of which represents a distinct constant. Enums make code more readable, maintainable, and less error-prone by providing a clear and self-documenting way to work with a specific set of related values. While the Go programming language (often referred to as Golang) does not have a built-in enum type like some other languages, it offers a few approaches to achieve similar functionality. In this article, we'll explore how to implement enums in Go.

The Need for Enums

Enums are valuable in scenarios where you want to represent a finite set of values that have a clear relationship or significance. For example, days of the week, HTTP status codes, error types, or any other situation where the set of possible values is fixed and known in advance.

In some programming languages, like C++ or Rust, enums are a language feature, and defining them is straightforward. However, in Go, we need to use creative techniques to achieve enum-like behavior.

Using Constants

The simplest way to mimic enums in Go is by using constants. Constants provide a way to declare a fixed value that cannot be changed during the execution of the program. By defining a set of related constants, we can achieve enum-like behavior.
I have discussed the use of iota in my previous blog.

package main

import "fmt"

// Define constants for days of the week
const (
    Sunday = iota
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
)

func main() {
    day := Wednesday
    fmt.Println("Today is", day)
    // output: Today is 3
}
Enter fullscreen mode Exit fullscreen mode

In the code above, we use the iota keyword to automatically assign successive integer values to each constant. This creates a clear mapping between the constants and their underlying values. While this approach works, it lacks the type safety and self-contained behavior of enums in some other languages.

Using Custom Types

Another way to create enum-like behavior in Go is by using custom types and defining methods on those types. This approach allows us to encapsulate the enum values within a specific type, providing better type safety and more control over the behavior of the enum.

package main

import "fmt"

// Define a custom type for days of the week
type DayOfWeek int

// Define constants for the days of the week
const (
    Sunday DayOfWeek = iota
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
)

// String method to convert the enum value to a string
func (d DayOfWeek) String() string {
    switch d {
    case Sunday:
        return "Sunday"
    case Monday:
        return "Monday"
    case Tuesday:
        return "Tuesday"
    case Wednesday:
        return "Wednesday"
    case Thursday:
        return "Thursday"
    case Friday:
        return "Friday"
    case Saturday:
        return "Saturday"
    default:
        return "Invalid day"
    }
}

func main() {
    day := Wednesday
    fmt.Println("Today is", day.String())
    //Output: Today is Wednesday
}
Enter fullscreen mode Exit fullscreen mode

In this code, we define a custom type DayOfWeek as an integer. We also define constants for each day of the week. The String method is a receiver function for the DayOfWeek type, allowing us to convert the enum value to a string representation. This approach provides better type safety and encapsulates the enum values within the custom type.

Using Unexported Fields

Another approach to creating enums in Go is to use unexported fields and constants within a struct. This approach provides encapsulation and prevents external code from directly modifying the enum values.

package main

import "fmt"

// Define a struct to represent the enum
type DaysOfWeek struct {
    Sunday    int
    Monday    int
    Tuesday   int
    Wednesday int
    Thursday  int
    Friday    int
    Saturday  int
}

// Create a variable of the struct type to represent the enum
var Days DaysOfWeek = DaysOfWeek{
    Sunday:    0,
    Monday:    1,
    Tuesday:   2,
    Wednesday: 3,
    Thursday:  4,
    Friday:    5,
    Saturday:  6,
}

func main() {
    day := Days.Wednesday
    fmt.Println("Today is", day)
    //Output: Today is 3
}
Enter fullscreen mode Exit fullscreen mode

In this approach, we define a struct DaysOfWeek with unexported fields representing the enum values. We then create a variable of this struct type to hold the enum values. Since the fields of the struct are unexported (start with a lowercase letter), they are not accessible outside the package, providing encapsulation.

Choosing the Right Approach

Each of the approaches described above has its own advantages and trade-offs. The choice of which approach to use depends on the specific requirements of your application.

Constants: This approach is simple and works well for small sets of related constants. It lacks some of the encapsulation and type safety benefits of the other approaches.

Custom Types: This approach provides better type safety and encapsulation by using methods on custom types. It's a good choice when you want to ensure that the enum values are only used in appropriate contexts.

Unexported Fields: This approach provides encapsulation, preventing external code from directly modifying enum values. It's useful when you want to ensure that the enum values are not modified outside the package.

Consider the readability, maintainability, and type safety requirements of your code when choosing the appropriate enum implementation approach.

Conclusion

Although Go does not have a built-in enum type, developers can implement enum-like behavior using various techniques. By using constants, custom types, or unexported fields, you can achieve the benefits of enums, such as improved code clarity, maintainability, and type safety. Choose the approach that best fits the needs of your application, and enjoy writing clean and robust Go code.

Top comments (4)

Collapse
 
wynandpieters profile image
Wynand Pieters

I was using Custom Types when I first needed enums as well, but I've since discovered Stringer and now Enumer which just makes it much easier, FWIW.

Collapse
 
ankitmalikg profile image
Ankit malik

Thank @wynandpieters, I will go through this.

Collapse
 
respect17 profile image
Kudzai Murimi

Thanks a lot!

Collapse
 
jhelberg profile image
Joost Helberg • Edited

Can't you create a function type and create enums as functions of this function type, enforcing types where type aliases fail?