Skip to main content
โšก Calmops

Go Constants and Enumerations

Go Constants and Enumerations

Constants are values that cannot be changed after declaration. Go provides powerful features for working with constants, including the iota keyword for creating enumerations. This guide covers everything you need to know about constants and enumerations in Go.

Basic Constants

Declaring Constants

package main

import "fmt"

func main() {
    // Single constant
    const Pi = 3.14159
    
    // Multiple constants
    const (
        Name = "Alice"
        Age = 30
        Height = 5.7
    )
    
    // Typed constants
    const MaxInt int = 2147483647
    const MinFloat float64 = -1.5
    
    fmt.Println(Pi)
    fmt.Println(Name, Age, Height)
    fmt.Println(MaxInt, MinFloat)
}

Untyped vs Typed Constants

package main

import "fmt"

func main() {
    // Untyped constant - more flexible
    const x = 42
    var a int = x
    var b int64 = x
    var c float64 = x
    
    fmt.Println(a, b, c)
    
    // Typed constant - strict type
    const y int = 42
    var d int = y
    // var e int64 = y  // โŒ Compile error: cannot use y (type int) as type int64
    
    fmt.Println(d)
}

Constant Expressions

package main

import "fmt"

func main() {
    const (
        Width = 100
        Height = 50
        Area = Width * Height  // Constant expression
    )
    
    const (
        Second = 1
        Minute = 60 * Second
        Hour = 60 * Minute
        Day = 24 * Hour
    )
    
    fmt.Println("Area:", Area)
    fmt.Println("Day in seconds:", Day)
}

The iota Keyword

Basic iota Usage

package main

import "fmt"

func main() {
    const (
        Sunday = iota    // 0
        Monday           // 1
        Tuesday          // 2
        Wednesday        // 3
        Thursday         // 4
        Friday           // 5
        Saturday         // 6
    )
    
    fmt.Println(Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday)
    // Output: 0 1 2 3 4 5 6
}

iota with Expressions

package main

import "fmt"

func main() {
    const (
        Byte = 1 << (10 * iota)  // 1 << 0 = 1
        KB                        // 1 << 10 = 1024
        MB                        // 1 << 20 = 1048576
        GB                        // 1 << 30 = 1073741824
        TB                        // 1 << 40 = 1099511627776
    )
    
    fmt.Println("Byte:", Byte)
    fmt.Println("KB:", KB)
    fmt.Println("MB:", MB)
    fmt.Println("GB:", GB)
    fmt.Println("TB:", TB)
}

iota Reset

package main

import "fmt"

func main() {
    const (
        A = iota  // 0
        B         // 1
        C         // 2
    )
    
    const (
        X = iota  // 0 (iota resets)
        Y         // 1
        Z         // 2
    )
    
    fmt.Println(A, B, C)  // 0 1 2
    fmt.Println(X, Y, Z)  // 0 1 2
}

Skipping Values with iota

package main

import "fmt"

func main() {
    const (
        Read = 1 << iota    // 1 << 0 = 1
        Write               // 1 << 1 = 2
        Execute             // 1 << 2 = 4
        _                   // Skip (1 << 3 = 8)
        Admin               // 1 << 4 = 16
    )
    
    fmt.Println("Read:", Read)
    fmt.Println("Write:", Write)
    fmt.Println("Execute:", Execute)
    fmt.Println("Admin:", Admin)
}

Enumerations

String Enumerations

package main

import "fmt"

func main() {
    const (
        Red = "red"
        Green = "green"
        Blue = "blue"
    )
    
    color := Red
    fmt.Println("Color:", color)
}

Integer Enumerations

package main

import "fmt"

func main() {
    const (
        StatusPending = iota
        StatusActive
        StatusInactive
        StatusDeleted
    )
    
    status := StatusActive
    fmt.Println("Status:", status)
}

Typed Enumerations

package main

import "fmt"

// Define a custom type for status
type Status int

const (
    StatusPending Status = iota
    StatusActive
    StatusInactive
    StatusDeleted
)

func main() {
    status := StatusActive
    fmt.Println("Status:", status)
    fmt.Printf("Type: %T\n", status)  // main.Status
}

Enumeration with Methods

package main

import "fmt"

type Color int

const (
    Red Color = iota
    Green
    Blue
)

// String method for Color
func (c Color) String() string {
    switch c {
    case Red:
        return "Red"
    case Green:
        return "Green"
    case Blue:
        return "Blue"
    default:
        return "Unknown"
    }
}

func main() {
    color := Red
    fmt.Println(color)  // Red
    fmt.Println(Green)  // Green
}

Bit Flags

Using Constants for Bit Flags

package main

import "fmt"

const (
    Read = 1 << iota    // 0001 = 1
    Write               // 0010 = 2
    Execute             // 0100 = 4
)

func main() {
    // Check permissions
    permissions := Read | Write  // 0011 = 3
    
    if permissions&Read != 0 {
        fmt.Println("Has read permission")
    }
    
    if permissions&Write != 0 {
        fmt.Println("Has write permission")
    }
    
    if permissions&Execute != 0 {
        fmt.Println("Has execute permission")
    } else {
        fmt.Println("No execute permission")
    }
}

Bit Flag Operations

package main

import "fmt"

const (
    Read = 1 << iota
    Write
    Execute
)

func main() {
    permissions := 0
    
    // Add permission
    permissions |= Read
    permissions |= Write
    fmt.Printf("Permissions: %b\n", permissions)  // 11
    
    // Check permission
    if permissions&Read != 0 {
        fmt.Println("Has read")
    }
    
    // Remove permission
    permissions &^= Write
    fmt.Printf("After removing write: %b\n", permissions)  // 1
    
    // Toggle permission
    permissions ^= Execute
    fmt.Printf("After toggling execute: %b\n", permissions)  // 101
}

Practical Examples

HTTP Status Codes

package main

import "fmt"

type StatusCode int

const (
    StatusOK StatusCode = 200
    StatusCreated = 201
    StatusBadRequest = 400
    StatusUnauthorized = 401
    StatusNotFound = 404
    StatusInternalServerError = 500
)

func (s StatusCode) String() string {
    switch s {
    case StatusOK:
        return "OK"
    case StatusCreated:
        return "Created"
    case StatusBadRequest:
        return "Bad Request"
    case StatusUnauthorized:
        return "Unauthorized"
    case StatusNotFound:
        return "Not Found"
    case StatusInternalServerError:
        return "Internal Server Error"
    default:
        return "Unknown"
    }
}

func main() {
    status := StatusOK
    fmt.Printf("%d: %s\n", status, status)
}

Log Levels

package main

import "fmt"

type LogLevel int

const (
    Debug LogLevel = iota
    Info
    Warning
    Error
    Fatal
)

func (l LogLevel) String() string {
    switch l {
    case Debug:
        return "DEBUG"
    case Info:
        return "INFO"
    case Warning:
        return "WARNING"
    case Error:
        return "ERROR"
    case Fatal:
        return "FATAL"
    default:
        return "UNKNOWN"
    }
}

func log(level LogLevel, message string) {
    fmt.Printf("[%s] %s\n", level, message)
}

func main() {
    log(Debug, "Debug message")
    log(Info, "Info message")
    log(Warning, "Warning message")
    log(Error, "Error message")
}

Direction Constants

package main

import "fmt"

type Direction int

const (
    North Direction = iota
    South
    East
    West
)

func (d Direction) String() string {
    switch d {
    case North:
        return "North"
    case South:
        return "South"
    case East:
        return "East"
    case West:
        return "West"
    default:
        return "Unknown"
    }
}

func move(direction Direction) {
    fmt.Printf("Moving %s\n", direction)
}

func main() {
    move(North)
    move(East)
    move(South)
    move(West)
}

Best Practices

โœ… Good Practices

  1. Use constants for fixed values - Don’t hardcode magic numbers
  2. Use iota for enumerations - Automatic numbering
  3. Create typed enumerations - Use custom types for type safety
  4. Implement String() method - For readable output
  5. Group related constants - Use const blocks
  6. Use meaningful names - Clear intent
  7. Document constants - Explain purpose
  8. Use bit flags for permissions - Efficient storage

โŒ Anti-Patterns

// โŒ Bad: Magic numbers
if status == 1 {
    fmt.Println("Active")
}

// โœ… Good: Named constants
const StatusActive = 1
if status == StatusActive {
    fmt.Println("Active")
}

// โŒ Bad: Untyped enumerations
const Red = 0
const Green = 1
const Blue = 2

// โœ… Good: Typed enumerations
type Color int
const (
    Red Color = iota
    Green
    Blue
)

// โŒ Bad: No String() method
fmt.Println(Red)  // 0

// โœ… Good: Implement String()
func (c Color) String() string {
    switch c {
    case Red:
        return "Red"
    // ...
    }
}
fmt.Println(Red)  // Red

Summary

Constants and enumerations are powerful Go features:

  • Use constants for fixed values
  • Leverage iota for automatic numbering
  • Create typed enumerations for type safety
  • Implement String() for readable output
  • Use bit flags for efficient permissions
  • Group related constants
  • Document your constants

Master these features for cleaner, more maintainable Go code.

Comments