Constants
A constant is a name for a value the compiler computes once, before the program runs. It is declared with const, can never change, and can be used wherever the language needs a compile-time value — an array size, for instance.
constant = ["pub"] "const" Name [":" type] "=" expression ";"
const MaxConnections = 100;
const Ratio: float64 = 1.5;
const Greeting: char8[..] = "hello";
The type annotation is optional. Without one, the constant takes its initializer's type, exactly as a let would: MaxConnections above is an int. With one, an unsuffixed literal takes the annotated type, and must fit it — const Small: uint8 = 300; is error: integer literal is out of range for type 'uint8'.
By convention, constant names are PascalCase.
Where a constant may appear
| Place | Example | Named as |
|---|---|---|
| A module, at top level | const Limit: uint = 4; | Limit |
| A function body | const Local = Limit * 2; | Local |
An extend block | const Zero = Point { x: 0, y: 0 }; | Point::Zero |
A module-level constant may be pub, like any other declaration — see Visibility. A constant declared in an extend block is an associated constant of the type, and is named through it:
extend Point {
const Zero = Point { x: 0, y: 0 };
const Dimensions: int = 2;
}
PrintLine("{} {}", Point::Zero.x, Point::Dimensions);
The primitive types carry associated constants of their own, such as int8::Min and float64::Epsilon, once their type is imported from Core — see Integers.
What the initializer may contain
The initializer is computed by the compiler, so everything in it must be known before the program runs. It may use:
- literals, and other constants
- the arithmetic, comparison, logical, bitwise and shift operators, casts and the conditional
? : sizeof(T)andalignof(T)- compile-time values such as
#target— see Compile-time context - structure, array, tuple and enum values built from all of these
const Limit: uint = 4;
const Area = Limit * Limit + 1;
const Mask = (1u32 << 4) - 1;
const Origin = Point { x: 0, y: 0 };
const Primes: int32[4] = [2, 3, 5, 7];
const Pair = (1, true);
const Favourite = Color::Green;
const Bytes = sizeof(Point) + alignof(Point);
const OnWindows = #target.os == OperatingSystem::Windows;
A function call and a variable hold run-time values, so neither may appear:
const FromCall = Seven();
const FromVariable = counter;
error: call to 'Seven' is not a compile-time value
help: declare 'FromCall' with 'let' to compute it at run time
error: 'counter' is not a compile-time constant
help: declare 'FromVariable' with 'let' to compute it at run time
The same is true of a parameter. When the value can only be known as the program runs, it belongs in a let.
Constants as compile-time integers
An integer constant is a compile-time integer wherever one is required. It can give an array its size and a repeated array literal its count, and another constant can be computed from it:
const Limit: uint = 4;
const Area = Limit * Limit + 1;
func Main() -> int {
const Local = Area * 2;
var flags: bool[Limit] = [false; Limit];
let grid: int32[Local] = [0; Local];
PrintLine("{} {}", flags.length, grid.length);
return 0;
}
This prints 4 34. A let holding the same number could not do this: its value exists only at run time.
A constant cannot change
A constant is not a variable, and nothing can assign to it. MaxConnections = 5; is error: cannot modify constant 'MaxConnections'.
Constants are not patterns
In a match arm, a bare name always binds a new variable, so a constant's name cannot be used to compare against its value. Writing one is an error rather than a silent new binding:
error: pattern 'Limit' cannot bind a new variable because 'Limit' already names a constant
help: a pattern compares with literal values; write the value of 'Limit', or bind the value and compare it with 'Limit' in a guard such as 'value if value >= Limit'
Compare in a guard instead:
match n {
v if v == Limit => PrintLine("at the limit"),
else => PrintLine("elsewhere")
}
const or let
const | let | |
|---|---|---|
| Computed | by the compiler, once | each time the line runs |
| Initializer | compile-time values only | any expression |
| Module level | allowed | not allowed |
extend block | allowed, as Type::Name | not allowed |
| Array size, repeat count | allowed | not allowed |
See also
- Bindings —
letandvar - Compile-time evaluation —
whenand the compile-time values - Arrays — sizes and repeated literals
- Learn: Const