All learning & notes
Learning now

Computed constants in Go

In Go, a const must be compile-time computable. That doesn’t mean it must be a literal; it can be the result of operations on other constants:

const secondsInMinute = 60
const minutesInHour  = 60
const secondsInHour  = secondsInMinute * minutesInHour

The compiler can evaluate secondsInMinute * minutesInHour at compile time, so secondsInHour is a valid constant.

Key properties:

  • All values in the expression must themselves be constant.
  • Only operations that the compiler can fully evaluate at compile time are allowed.
  • Types can be explicit (const x int = 5) or inferred (const x = 5).

What isn't allowed?

Anything that requires information only known at runtime:

// invalid: needs current time at runtime
const now = time.Now()

// invalid: function call that isn't a const expression
const v = math.Sin(0.5)

Even though math.Sin(0.5) is conceptually “pure,” Go doesn’t have a general “const function evaluation” mechanism. Only a small set of built-in operations are allowed in constant expressions (arithmetic, comparisons, shifts, etc. on constants).

Also, you can’t assign a non-constant to a constant:

var x = 10
// const y = x   // invalid: x is not a constant

Go’s “untyped” constants

Go has untyped constants:

const x = 5

Here, x doesn’t immediately have a concrete type; it gets one when used:

var a int64 = x   // x becomes int64 here
var b float64 = x // x becomes float64 here

This makes numeric constant arithmetic flexible, but only as long as everything stays in the constant world and fits in range.


Comparisons with other languages

Java

  • final fields can be either:
    • Compile-time constants (inlined by the compiler): static final int X = 5;
    • Runtime constantsfinal LocalDate now = LocalDate.now();
  • Java allows calling methods when initializing final values, even if they’re not compile-time constants; Go does not for const.

So:

final int secondsInMinute = 60;
final int minutesInHour  = 60;
final int secondsInHour  = secondsInMinute * minutesInHour; // valid, but runtime

In Go, the same pattern is compile-time constant.

Java also has enums where constants can be richer objects; Go uses iota with plain consts instead.


C / C++

C:

  • #define is a preprocessor macro, purely textual.
  • const int x = 5; is a variable with restricted mutation; whether it’s a true compile-time constant can be subtle (depends on context).

C++:

  • const int x = 5; often behaves like a compile-time constant.

  • constexpr explicitly means “must be evaluable at compile time”:

    constexpr int secondsInMinute = 60;
    constexpr int minutesInHour  = 60;
    constexpr int secondsInHour  = secondsInMinute * minutesInHour;
    
  • C++ can have constexpr functions that are evaluated at compile time when used in constant expressions. Go doesn’t have this concept; only built-in operations are allowed in const expressions.


JavaScript / TypeScript

JavaScript:

  • const is a read-only binding, not a compile-time constant:

    const now = new Date(); // perfectly valid
    
  • All evaluation is at runtime; there is no language-level guarantee about compile-time computability.

TypeScript:

  • Adds as const and const enum, but they’re compile-time TypeScript constructs that vanish after compilation; they don’t behave like Go’s const in the runtime environment.

So const in JS/TS is mostly about immutability of the variable binding, not about compile-time evaluation.


Rust

Rust has a split similar to Go’s const vs var, but stricter and richer:

  • const – compile-time constants, like Go:

    const SECONDS_IN_MINUTE: u32 = 60;
    const MINUTES_IN_HOUR: u32 = 60;
    const SECONDS_IN_HOUR: u32 = SECONDS_IN_MINUTE * MINUTES_IN_HOUR;
    
  • let – normal variables.

  • static – global variables (with optional mut).

  • Rust supports const fn, functions that can be evaluated in constant expressions, which is more powerful than Go’s constant system.


Caveats in Go

  1. No function calls in const expressions (in general)
    Except for a few built-ins like len on strings and some special cases, you can’t call arbitrary functions in a const expression.

  2. Overflow rules are strict
    Constant expressions must fit in their destination type:

    const big = 1 << 100
    // var x int64 = big // error: overflows int64
    
  3. No runtime data
    You can’t use values derived from input, environment variables, system time, etc. in constants.

  4. Different from “immutable variable”
    Go doesn’t have a way to declare an immutable variable at runtime. const is only for compile-time constants; var is always mutable.