H. S. Teoh 
Posted in reply to Brother Bill
| On Wed, Sep 10, 2025 at 04:52:51PM +0000, Brother Bill via Digitalmars-d-learn wrote:
> Is there any reason to pick one of these vs. another one, or are they all equivalent?
[...]
They are most definitely not equivalent.
`enum` defines a compile-time constant. It occupies no space, and its value is "copied" into every expression in which it appears. For example:
```d
enum x = [ 1, 2, 3 ];
auto myArray = x; // equivalent to `auto myArray = [ 1, 2, 3 ];`
auto arr2 = x; // a distinct copy of [ 1, 2, 3 ] is made
assert(myArray !is arr2);
```
Note that each time x is referenced, a new copy of its value is generated.
`const` and `immutable` are type qualifiers, and declaring a constant with them creates a variable in memory containing that value. For example:
```d
immutable y = [ 1, 2, 3 ];
auto myArray = y; // creates a reference to y
auto arr2 = y; // creates another reference to y
assert(myArray is y);
assert(myArray is arr2);
```
Note that y's contents are *not* duplicated when you assign it to other variables. Instead, it is copied by reference, and changes to the data via the copied reference will show up in the original (though in this case, it's illegal to modify immutable).
//
Now, what's the difference between `const` and `immutable`? The following diagram, if you'll excuse the ASCII art, represents an "inheritance diagram" of the 3 qualifiers, with "mutable" representing no qualifer at all:
const
/ \
mutable immutable
Both mutable and immutable implicitly convert to const, but const does not implicitly convert to either.
Mutable (i.e., no qualifier) means that the variable may be freely modified. Immutable means that it cannot be modified by anybody, not this code, nor any other code anywhere else in the program. Const means that it may be not be modified by this code, but *may* be modified by some other code elsewhere.
The implicit conversion to const is justified, because not being allowed to write to a mutable variable does not break any guarantees of mutable (there are none), and not being allowed to write to immutable is required because nobody may write to immutable. The code holding the const reference may not know whether the data is mutable or immutable, but it doesn't matter because it can't write to it anyway.
It's invalid to implicitly convert const to immutable, because the code holding the const reference does not know (cannot guarantee) that somebody else doesn't hold a mutable reference to the data.
For declaring a single constant, there's not much difference between const and immutable, of course. But the distinction becomes important when you start working with complex types. Unlike C/C++, const in D is enforced by the compiler (unless you override it with a cast, but that's @system and not allowed in @safe code), and is infectious (you cannot modify data through a const reference even if the data itself is mutable).
//
A good example of the interaction between const/immutable/mutable is strings. D defines `string` as `immutable(char)[]`, i.e., a mutable reference to an immutable array of characters. The reference is mutable (you can take substrings, reassign it to point to a different string, etc.) but the characters themselves are immutable -- nobody can modify them. This allows the char data to be stored in read-only memory, and freely shared between threads without worrying about race conditions.
However, sometimes you may wish to work with an array of mutable chars, e.g., if you're constructing a complex string and need to modify individual chars in the process. So you'd work with `char[]` instead of `string` (i.e., `immutable(char)[]`).
But then there will be code that doesn't care either way, and that could work with either string or char[]. They don't modify any data, so it's wasteful to use a template function for them (it would generate complete identical assembly code, causing needless redundancy in your executable). What can we do in this case? Const comes to the rescue! By having said code receive `const(char)[]` as argument, it can work with both string (immutable) and char[] alike with no code duplication. Since the code doesn't modify the data, it doesn't break guarantees of `immutable`, but it doesn't require immutable so it can also work with mutable data.
In short:
```d
auto myFunc(string s) {
// s cannot be modified by anybody, not this function, not
// anybody else. It's safe to expect s not to change in the
// future.
}
auto myFunc2(char[] s) {
// s can be freely modified, by this code and by other code.
// There's no guarantee s won't change later.
}
auto myFunc3(const(char)[] s) {
// This code can work with both string and char[].
// It cannot modify s, but somebody else might, so there's also
// no guarantee s won't change later.
}
```
Finally, recommendations:
- If your constant value is a POD, generally it's a good idea to declare
it as `enum`, which creates a compile-time constant. Then it can be
used for compile-time constructs that require its value to be known at
compile-time.
- If your constant value is an array or other non-POD, you should
probably declare it as immutable. This ensures no redundant copies of
it are made, and the data can be put in the program's read-only
segment, which can protect it from intrusive modification (e.g. by
malware / hacking attempts).
- `const` is probably OK for declaring POD variables if you're too lazy
to type `immutable`. But generally not a useful distinction from
immutable.
T
--
I have no Monet for Degas to make the Van Gogh, because I'm Baroque.
|