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
- Use constants for fixed values - Don’t hardcode magic numbers
- Use iota for enumerations - Automatic numbering
- Create typed enumerations - Use custom types for type safety
- Implement String() method - For readable output
- Group related constants - Use const blocks
- Use meaningful names - Clear intent
- Document constants - Explain purpose
- 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