Objects with const and immutable

In the context of class and struct instances, it's easier to understand the relationship const and immutable have with data. Here's a simple example:

struct ModMe {
    int x;
}
void main() {
    immutable ModMe mm;
    immutable(ModMe)* pmm;
    mm.x = 1;   // Error
    pmm.x = 2;  // Error
}

The declaration of mm creates an instance of type immutable(ModMe). Not only can no assignments be made to mm, mm.x cannot be modified. pmm is a pointer to immutable data, so the pointer can be reassigned, but pmm.x cannot be modified. Now look at this:

struct ModMeHolder {
    ModMe mm;
}
void main() {
    immutable ModMeHolder mmh;
    mmh.mm.x = 1;
}

As immutable is transitive, applying it to mmh causes mm to also be immutable. If, in turn, it had any class or struct instance members, they would also become immutable. As they say, it's turtles all the way down. The same is true for const, but recall that its contract is not as strict as that of immutable:

ModMe mm;
const(ModMe)* pmm = &mm;
mm.x = 10;
writeln(pmm.x);

Attempting to modify pmm.x would be an error, since pmm is a pointer to const data, but it's perfectly legal to modify the original data behind pmm's back.

Changing the previous ModMeHolder example to use const instead of immutable will not change the outcome. This sometimes causes consternation for C++ programmers experimenting with D. Imagine you want to implement a class that increments an internal counter every time a function is called. Pass an instance of that class to a function that attempts to call its incrementing member function through a const reference and a compiler error will result. In C++, programmers can get around this by declaring the counter as mutable. Not so in D.

D const is strictly physical const. This can be seen as a guarantee that not a single bit of an instance can ever be modified through a const reference, internally or externally. The alternative is logical const. With this definition, a reference appears const to the outside world, but the instance is able to modify its internal structure. While this may seem quite useful to the programmer, it completely breaks any guarantee of transitivity. If the compiler cannot guarantee transitivity, then any assumptions it makes about the state of an instance are invalid. This could have wide-ranging consequences. For example, it could be a source of race conditions in a multi-threaded program. The short of it is, in order to guarantee transitivity, logical const does not exist in D as I write. Who can say, though, what the future holds.

const and immutable can be applied to class and struct declarations, but any instances of such types must still be declared as const or immutable.

const as a storage class

We've only seen const used as a type qualifier thus far, but when applied to a function it is a storage class. As in C++, this is used to allow member functions to be called from a const or immutable reference:

class CallMe {
    void foo() {}
    void bar() const {}
 }
void main() {
    const CallMe cm = new CallMe;
    cm.foo();   // Error – mutable function on const reference
    cm.bar();   // OK
}

const can be applied to the front of the function declaration as well as at the end. This has implications for how const return values are declared:

const AStruct returnSomething() {}

It's easy to assume this is returning a constant struct instance, but that is not the case. This is a function with a const storage class. For clarity, it's good practice to apply const at the end of a function declaration when needed and to declare const return values using the type qualifier syntax, const(AStruct).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.189.199