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