const or immutable?

Ali Çehreli acehreli at yahoo.com
Wed Sep 22 20:06:59 UTC 2021


Please excuse this thread here, which I know belongs more to the Learn 
forum but I am interested in the opinions of experts here who do not 
frequent that forum.

tl;dr Do you use 'const' or 'immutable' by-default for parameters and 
for local data?

Long version:

It was simpler in C++: We would make everything 'const' as much as 
possible. Unfortunately, that doesn't work in D for at least two reasons:

1) There is 'immutable' as well

2) There is no head-const (i.e. no 'const' pointer to mutable data; i.e. 
"turtles all the way down")

Further complications:

- Reference semantics versus copy semantics; by type (e.g. slices), by 
the 'ref' keyword, by a member of a struct that has reference semanticts 
(struct is by-copy; but a member may not be), etc.

- It is said that 'immutable' is a stronger type of const, which at 
first sounds great because if 'const' is good, 'immutable' should be 
even better, right? Unfortunately, we can't make everything 'immutable' 
because 'const' and 'immutable' have very different meanings at least in 
parameter lists.

- Parameters versus local data.

I am seeking simple guidelines like C++'s "make everything const."

Let's start with what I like as descriptions about parameters:

1) 'const' parameter is "welcoming" because it can work with mutable, 
'const', and 'immutable'. It (sometimes) means "I am not going to mutate 
your data."

2) 'immutable' parameter is "selective" because it can work only with 
'immutable'. It means "I require immutable data."

But it's not that simple because a 'const' parameter may be a copy of 
the argument, in which case, it means "I will not mutate *my* data." 
This is actually weird because we are leaking an implementation detail 
here: Why would the caller care whether we mutate our paramener or not?

// Silly 'const':
void foo(const int i) {
   // ...
}

But of course it matters if the type has indirections (e.g. a member is 
a reference to some other data).

Aside: If 'const' is welcoming, why do we type 'string' for string 
parameters when we don't actually *require* immutable:

// Unenecassary 'immutable' but I do this everywhere.
void printFirstChar(string s) {
   write(s.front);
}

It should have better been const:

void printFirstChar(const char[] s) {
   write(s.front);
}

But wait! It works above only because 'front' happened to work there. 
The problem is, 's' is not an input range; and that may matter elsewhere:

   static assert(isInputRange!(typeof(s)));  // Fails. :(

So only the elements of the string should be 'const':

void printFirstChar(const(char)[] s) {
   write(s.front);
   static assert(isInputRange!(typeof(s)));  // Passes.
}

(Granted, why is it 'const' anyway? Shouldn't printFirstChar be a 
function template? Yes, it should.)

So, what are your guidelines here?

More important to me: How do you define your local data that should not 
be mutated?

   const     c = SomeStruct();
   immutable i = SomeStruct();

In this case, 'const' is not "welcoming" nor 'immutable' is "selective" 
because these are not parameters; so, the keywords have a different 
meaning here: With local data, they both mean "do not mutate". Is 
'immutable' better here because we may pass that data to an 
immutable-requiring function? Perhaps we should learn from string and 
make it really 'immutable'? But it's not the same because here 
'immutable' applies to the whole struct whereas 'string's immutability 
is only with its elements. There! Not simple! :)

Or, should 'immutable' be preferable here because it's implicitly 
shared, which may be useful in the future?

Are there simple guidelines around this topic? Please? :)

Personally, I generally ignore 'immutable' (except, implicitly in 
'string') both for parameters and local data.

Thank you,
Ali


More information about the Digitalmars-d mailing list