immutable, const and enum
Three ways to promise that a value will not change, what each promise costs, and why the compiler takes them more seriously than a comment.
immutable means nobody can change it
An immutable value is fixed at the moment it is created and never changes afterwards, anywhere in the program. immutable string Brand = "zoom"; declares one, and any later attempt to assign to Brand is a compile error rather than a bug that appears at three in the morning.
Because the value can never change, it is safe to share: two threads reading the same immutable data cannot interfere, and no copy is needed to protect it. That is the reason a D string is immutable — text passed between functions cannot be quietly rewritten by the function that received it.
const is a read-only view
const says that this name will not write to the data. The data itself may belong to someone else and may change through another name. A parameter written const int[] xs promises that the function only reads the array, and a promise like that lets the same function accept both mutable and immutable arguments.
The difference is worth holding on to: immutable is a fact about the data, const is a rule about one name. A function that takes const is telling every caller that the array will come back exactly as it went in.
A const parameter can accept an immutable argument, because a promise not to write is weaker than a guarantee that nobody can. The reverse is not true: an immutable parameter cannot take a mutable array, since the caller could change it afterwards. That one-way rule is why library functions usually ask for const.
enum is a constant with no storage
enum Wheels = 4; declares a manifest constant. Every use is replaced by 4 at compile time, so there is no variable, no memory, and no way to take its address. A manifest constant can be used where a value known at compile time is required, such as a case label in a switch.
Reach for enum for a number that is part of the definition of the program, immutable for a value computed once at startup, and const on parameters that should not be written. Three promises, and each one removes a class of mistake.
An enum can also name a value computed from other constants, as in enum Area = 3 * 4;. The compiler evaluates the expression, so the result is a number in the instruction rather than a load from memory. A static immutable computes the value once and keeps it in storage, which suits a value that is expensive to build.
The promise reaches inside
immutable and const are transitive: they apply to the value and to everything reachable from it. An immutable int[] is an array whose elements cannot change, and the compiler enforces that through every slice taken from it. A const view of a struct makes every field read-only at once, not only the field you happened to name.
That depth is what makes the promise useful. A function taking const int[] cannot keep the array and change it later, because it has no mutable name for it. The caller keeps the right to modify the data through its own name; the callee simply agrees not to.
When the value is known
An enum value exists only while the program is compiled, so it can stand in a case label, an array length or a template argument. A module-level immutable is initialized once before main runs and occupies storage; a local immutable is fixed when execution reaches its declaration.
Choose by the question you are answering. Is this a fact about the program that never needs memory? enum. Is it a value computed once and shared? immutable. Is it a parameter that must not be written? const. Three promises, and each one removes a class of mistake.
A constant, an immutable value and one read
import std.stdio;
enum Max = 3;
immutable string Unit = "kg";
void main() {
immutable int weight = 4;
writeln(Unit, " ", weight, " of ", Max);
}A read-only parameter and an immutable array
Run it, then add a line that appends 2 to seats and read the compiler error before removing the line. Then change total to take int[] instead of const int[] and see which call stops compiling.
An immutable array cannot grow: appending to it with ~= is a compile error, not a silent copy. Leave a collection mutable until its contents are final, then make the finished value immutable.
A read-only view of data that moves
Run it and compare the two views of `xs`. Then change `xs[0]` before the first print and explain the result. Finally try `fixed[0] = 0;` and read the error before removing the line.
Three mistakes: writing immutable int limit; and assigning later, which is a compile error because an immutable value must be initialized where it is declared; expecting a const parameter to stop the data changing elsewhere, when it only stops this function writing; and using a const variable as a case label, which needs the compile-time value an enum provides.
Try these next
- Change
xs[0]before the first print and explain why the const view changes with it. - Add a mutable copy of
fixedwith.dup, modify the copy, and confirm the immutable original is untouched. - Move
enum Maxinsidemainand check that thecaselabel still compiles.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
What is the difference between immutable and const?
