Constants in Golang

Learn how constants work in Golang. This comprehensive guide covers constant declarations, typed vs untyped constants, const blocks, iota for enums, compile-time evaluation, and best practices for using constants in Go programming.

Constants in Golang are a fundamental feature. One of Go’s design goals is clarity: code should be predictable, readable, and safe. Constants support that philosophy because they represent values that must not change.

If a value is truly fixed (like a version string, a timeout limit, HTTP status code, or “magic number”), making it a constant communicates intent clearly. It also allows the compiler to catch mistakes early.

In Go, constants are evaluated at compile time. That means they are not like variables that hold changing state constants are “locked” values.

Constant Declaration

There are multiple ways to declare constants in Go. Below are the common styles, along with when to use each one.

Declare a single constant

The simplest form: declare one constant with const.

Syntax: const <name> = <value>

const AppName = "JyotishLabs"
const MaxRetries = 3
const Debug = false

This method is used in the following scenarios:

  • When a value should never change across the program.
  • When you want to remove “magic numbers” from the code.
  • When the value is shared across functions/files in the same package (package-level constants).
  • When the value is part of documentation (readability) as much as functionality.

Declare with explicit type (typed constants)

You can explicitly assign a type to a constant. This makes the constant stricter and prevents accidental mixing.

Syntax: const <name> <type> = <value>

const Port int = 8080
const Pi float64 = 3.1415926535
const AppCode string = "JYOTISH-001"

This method is used in the following scenarios:

  • When you want to lock a constant to a specific type for APIs or struct fields.
  • When type clarity matters in a public package (readability for other developers).
  • When you want to avoid implicit conversions in math-heavy logic.

Declare without explicit type (untyped constants)

By default, constants can be untyped. This is a very Go-specific feature and it’s extremely useful.

const Limit = 10 // untyped integer constant
const Rate = 2.5 // untyped floating-point constant

Because they are untyped, Go can “fit” them into different types when needed:

const Limit = 10
var a int = Limit
var b int64 = Limit
var c float64 = Limit

This method is used in the following scenarios:

  • When you want flexibility without writing type casts everywhere.
  • When the constant is a general numeric value and the actual type depends on usage.
  • When writing clean beginner-friendly examples.

Constant block (grouped constants)

A const block lets you group related constants like a configuration section. This is the cleanest style when you have multiple fixed values.

const (
 Host = "localhost"
 Port = 8080
 Debug = false
 MaxRetries = 3
)

This method is used in the following scenarios:

  • Reads like a config section.
  • Keeps related constants together and makes the file easier to scan.
  • Common at package level (shared constants).

Constants from expressions (compile-time only)

Constants can be derived using expressions—as long as the compiler can evaluate them at compile time.

const (
 KB = 1024
 MB = KB * 1024
 GB = MB * 1024
)
const (
 DefaultTimeoutSeconds = 30
 DefaultTimeoutMillis = DefaultTimeoutSeconds * 1000
)

This method is used in the following scenarios:

  • Unit conversions (KB/MB/GB, seconds/millis).
  • Defining limits in a readable and maintainable way.
  • Avoiding repeated calculations and magic numbers.

What cannot be a constant

Constants must be compile-time values. Anything that needs runtime evaluation cannot be a constant.

❌ Not allowed:

// const Now = time.Now() // runtime value
// const IDs = []int{1, 2, 3} // slices are runtime structures
// const M = make(map[string]int) // maps are runtime structures

✅ Use var instead for runtime values.

This rule exists because constants are embedded by the compiler and don’t “live” as runtime objects like maps or slices.

iota (enums and sequences)

Go doesn’t have “enums” like Java, but iota gives you a clean way to create enum-like constants.

iota starts at 0 inside a const block and increments automatically.

Basic example

const (
 Pending = iota // 0
 Running // 1
 Done // 2
)

This method is used in the following scenarios:

  • Defining states (Pending/Running/Done).
  • Defining fixed modes (Dev/Staging/Prod).
  • Avoiding manual numbering mistakes.

Start from 1 (common style)

const (
 StatusNew = iota + 1 // 1
 StatusOpen // 2
 StatusClosed // 3
)

Used when:

  • You want 0 to mean “unknown/unset”.
  • You are mapping values to external systems where 0 is reserved.

Bit flags with iota (advanced but very common)

const (
 Read = 1 << iota // 1
 Write // 2
 Exec // 4
)

Used when:

  • You want combinable flags like permissions:
    • Read | Write
    • Read | Exec
    • Feature toggles and capability flags.

    Scope rules (where constants can be declared)

    Constants can be declared:

    • At package level (shared across the package)
    • Inside functions (local to the function)
    package main
    const Version = "1.0.0" // package-level
    func main() {
     const Greeting = "Hello" // local
     _ = Greeting
    }

    Best practices for constants

    • Prefer constants for fixed values (avoid magic numbers).
    • Use const (...) blocks for grouped values.
    • Use meaningful names: MaxRetries, DefaultTimeoutSeconds, StatusOpen.
    • Use iota for sequences/states, but don’t overuse it if it hurts readability.
    • Keep constants close to where they matter (package-level for shared values, function-level for local logic).

    Quick mental model: const vs var

    Use const when the value is fixed at compile time.
    Use var when the value can change or is determined at runtime (config, env, DB, time, user input).

    srnyapathi
    srnyapathi
    Articles: 41