Meaning of const variables

H. S. Teoh via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Mon Jun 20 20:03:04 PDT 2016


On Tue, Jun 21, 2016 at 02:24:03AM +0000, jmh530 via Digitalmars-d-learn wrote:
> I feel like I have a reasonable understanding of when to use const as
> a parameter in a function or for const member functions. However, I
> don't really understand why/when it should be used as a type modifier.
> 
> For instance, the Programming in D book basically just says
> (http://ddili.org/ders/d.en/const_and_immutable.html) that a const
> variable is the same as an immutable variable, but you should just use
> immutable.

I think that's an oversimplification. :-)  Here's a diagram that helped
me understand what the deal with const/immutable is:

	        const
	       /     \
	(mutable)   immutable

This is a kind of "type inheritance diagram" analogous to a class
inheritance diagram.  What it means is that mutable and immutable cannot
implicitly convert to each other (except for atomic by-value types), but
both mutable and immutable can implicitly convert to const.

Why?

Because const means "you are not allowed to modify this value" (but
somebody else may).  Converting mutable to const, which usually happens
in function calls, simply means the callee is bound to a contract never
to modify the value, but the caller may have a mutable reference to the
same value and may legally modify it.

OTOH, immutable means "NOBODY is allowed to modify this value". Meaning
that the value is truly, physically immutable; neither caller nor callee
nor any 3rd party may modify its value.  This is actually a strong
guarantee because the compiler can statically verify that nobody
actually modifies the value; therefore certain optimizations can be made
(e.g., storing it in ROM, in an immutable data segment, eliding multiple
reads from the variable because it is guaranteed that its value never
changes no matter what else happens in between).  Such optimizations
cannot be applied to const, because we aren't guaranteed that a 3rd
party won't change the value after we last read it. Const only means
*we* cannot change it.

So const is a weaker form of immutable... its main purpose is to reduce
redundancy in functions that can work with both mutable and immutable
data.  For example, a function that only reads the value of a parameter
can be passed an immutable value safely -- since it won't modify it --
and can also take a mutable value -- even though modification is valid,
the function doesn't modify it anyway. This means we can pass both a
mutable value and an immutable value to the same function and it's fine,
as long as the function itself never changes anything.

But we can't write the function as:

	auto func(immutable X x) { ... }

because converting mutable to immutable is illegal, so we couldn't call
the function with mutable arguments. Obviously we also can't write the
function without type qualifiers:

	auto func(X x) { ... }

because immutable cannot convert to mutable, so we can't pass an
immutable value to it. The solution is to use const:

	auto func(const(X) x) { ... }

Const ensures that func doesn't attempt to modify an immutable value
when we give it one, but since const doesn't guarantee a 3rd party won't
modify the value, we can also legally pass a mutable value to it. Since
func is bound by const never to modify x, it doesn't matter.

Without const, we'd have to write two identical functions, one that
takes an immutable value, and one that takes a mutable value.  Const
lets us unify the two, which is valid since const ensures that func
doesn't do any funny business behind our backs by trying to modify
its arguments.

Of course, this is only part of the story. The other part is that if
a value has a by-value type, then it's OK to implicitly convert to/from
immutable: because you're always making a copy of the by-value type. You
can't convert a mutable *reference* to an immutable reference or vice
versa (otherwise you could use the mutable reference to break
immutability), but you *can* copy the value itself from mutable to
immutable and vice versa. So a function that takes an int argument can
be legally passed an immutable int -- since the function gets a copy of
the immutable value, and it's OK to modify the copy instead of the
original value.

With this in view, it would make sense that const would usually be used
in function parameters, since that is where it's most useful, whereas
immutable would normally be used in variables that you wish to
initialize once and have its value never change again thereafter. This
is probably where that advice came from that you should use immutable
variables rather than const variables.

Still, there *are* times when const variables make sense -- this applies
when iterating read-only over a collection of objects, for example. The
iterator would need to be const so that it can refer to both mutable and
immutable members of the collection, again so that you don't have to
copy-n-paste the same code, one in a function that takes an immutable
collection and another that takes a mutable collection.

Hope this helps.


T

-- 
Talk is cheap. Whining is actually free. -- Lars Wirzenius


More information about the Digitalmars-d-learn mailing list