immutable/mutable aliasing
H. S. Teoh via Digitalmars-d-learn
digitalmars-d-learn at puremagic.com
Thu Jul 3 14:33:31 PDT 2014
On Thu, Jul 03, 2014 at 09:06:11PM +0000, Jet via Digitalmars-d-learn wrote:
> There, how to distinguish between const and immutable? thank you~:)
>
> /**
> "Const types are like immutable types, except that const forms a
> read-only view of data. Other aliases to that same data may change it
> at any time. "
>
> "Any data referenced by the const declaration cannot be changed from
> the const declaration, but it might be changed by other references to
> the same data. "
> **/
>
> Can sample code, please?
This diagram may help understand D's const/immutable system:
const
/ \
(mutable) immutable
Mutable is the default unqualified type. It means you're free to modify
it. Immutable is the opposite: it is guaranteed that neither you, nor
anybody else, can modify it. Once an immutable value is initialized, it
might as well be "cast in stone", it can never change again.
int x = 1; // mutable
x++; // OK
immutable int y = 1;
//y++; // ILLEGAL: cannot modify immutable
Const is the bridge between mutable and immutable. It means that *you*
cannot change the value, but somebody else might. The reason we want
const is so that you can pass both a mutable or an immutable value to
the same function. The function cannot change the value, but the caller
can, if it's mutable. (And of course, immutable cannot be changed by
anyone -- which is OK, since the function isn't allowed to change a
const parameter anyway.)
class MyData {
int x;
}
auto data = new MyData; // mutable
data.x++; // OK
auto idata = new immutable(MyData);
//idata.x++; // ILLEGAL: cannot modify immutable
bool myFunc(const(MyData) obj) {
obj.x++; // ILLEGAL: cannot modify const
return obj.x > 0; // OK: can read const
}
bool b1 = myFunc(data); // OK, can pass mutable to const
bool b2 = myFunc(idata); // OK, can pass immutable to const
// (since myFunc cannot modify it)
void cache(immutable(MyData) iobj) {
static immutable(MyData) _cache;
_cache = iobj;
}
cache(data); // ILLEGAL: cannot pass mutable to immutable
cache(idata); // OK: can pass immutable to immutable
Usually, you'd write function parameters to be const rather than
immutable, because you want to accept both mutable and immutable
arguments. But sometimes, you want to be 100% sure that nobody outside
the function can modify the value while you're using it. For example:
int longCalculation(const(MyData) obj) {
int acc = 0;
foreach (i; 0 .. 1_000_000) {
acc += obj.x;
}
return acc;
}
auto evil = MyData(1);
spawn((const(MyData) d) {
// This runs in a different thread
writeln(longCalculation(d));
}, evil);
evil.x = 5; // Oops
Since "evil" is mutable, it's allowed to be passed to a const parameter:
it just means that longCalculation cannot modify it. However, that
doesn't guarantee that somebody else can't modify it; for example, in
the above code we run longCalculation in a different thread, and then we
set evil.x = 5 in the main thread. The result is that the value of obj.x
in longCalculation gets mutated while the loop is running, causing the
result to become corrupted. So the child thread will *not* print
"1000000", but something else, depending on the relative timing of the
threads.
To prevent this sort of problems, the solution is to make
longCalculation take an immutable parameter. Then if you try to compile
it, the compiler will complain that you can't pass mutable to immutable,
so you're forced to make "evil" either const or immutable. Which means
that the "evil.x = 5" line will be rejected by the compiler because
nobody is allowed to modify an immutable value, thus preventing the
corruption problem.
T
--
Truth, Sir, is a cow which will give [skeptics] no more milk, and so they are gone to milk the bull. -- Sam. Johnson
More information about the Digitalmars-d-learn
mailing list