Head Const

Jonathan M Davis via Digitalmars-d digitalmars-d at puremagic.com
Fri Feb 19 03:21:29 PST 2016


On Friday, 19 February 2016 at 10:03:44 UTC, Walter Bright wrote:
> On 2/18/2016 11:46 PM, Jonathan M Davis wrote:
>> We're in exactly the same boat as C++ in that we rely on the
>> programmer to be smart about casting away const in order for 
>> const to provide
>> any guarantees.
>
> No, we're not. @trusted is specifically designed to be 
> greppable (so are casts, by the way). This is quite unlike C++. 
> I defy you (or anyone) to look at a piece of non-trivial C++ 
> code and verify there are no deviated preversions going on.
>
> D is simply not in the same boat at all.

D is definitely in a better boat in that it's easier to find and 
catch problems with const, but it really doesn't do much more to 
actually guarantee that a const object isn't being violated. 
Programmers are free to do horrible things and mark it as 
@trusted, possibly thinking that what they're doing is fine. And 
in D, programmers are frequently tempted to cast away const 
thinking that it's a legitimate backdoor like it is in C++ just 
so long as the object isn't actually immutable, especially 
because there is no mutable keyword in D.

> We can debate about the holes in @safe that get bug reports 
> filed on them, but these are bugs and we intent to fix all of 
> them. There is no plan nor proposal to fix C++ unsafety.

Sure, but the main problem here is with code that is marked as 
@trusted. If you want to be 100% sure that an object isn't 
mutated via a const reference, you're still ultimately forced to 
dig through the code to verify it. That attack space is much 
reduced in comparison to C++, and it's much more greppable, so 
finding and fixing the problem is much easier, but the compiler 
still ultimately fails to actually guarantee that an object is 
not mutated via a const reference. To do that, it would have to 
be outright illegal to cast away const.

So, are we in a much better position than C++ with regards to 
const protecting against mutation? Yes. But we're not actually 
preventing it. And because of how restrictive D's const is, folks 
either end up casting away const to mutate, or they abandon const 
altogether. And if they abandon const altogether, then they 
actually end up with more error-prone code, because they don't 
have it preventing any mutation at all. Even protection with 
holes is better than no protection. But we don't even need to add 
holes on the same level as C++ in order to make D's const 
considerably more usable. I completely agree that we shouldn't 
make casting away const and mutating defined behavior (much as we 
can't actually prevent it), but having an equivalent to C++'s 
mutable would help considerably.

> > I said that a type with an @mutable member would be forced to
> be marked with @mutable as well
>
> You're proposing '@mutable const' ?

No, whether a type has @mutable members is part of its 
definition, not a type qualifier.  I'm proposing that we have 
something like

@mutable struct S
{
     int i;
     @mutable int* refCount;
     ...
}

or

@mutable class C
{
public:
     @property int i() shared const { Guard guard(m); return i; }
     @property void i(int val) shared { Guard guard(m); i = val; }
private:
     shared int i;
     shared @mutable Mutex m;
}

Then if you had an instance of C which was const, you could still 
mutate the Mutex member inside of a const function, and if it had 
a const instance of S, it could still mutate its ref-count 
appropriately in its postblit constructor and assignment operator 
and whatnot. And code which used S or C, would just use it like 
it would now. e.g.

     const S s;
     auto r1 = s.foo();

     const c = new C;
     auto r2 = c.i;

So, functionally, it should act the same as mutable in C++. 
However, it would act like abstract in that it would go with the 
members that it affects and with the type that contains such 
members. Any type that then contained such a type would also have 
to be marked with @mutable. e.g.

@mutable struct Foo
{
     // A member whose type declaration is marked with @mutable,
     // but the member variable itself is not @mutable
     C c;
}

Then if someone needed a mutable member, they could use @mutable 
just like they'd use the mutable keyword in C++, except that 
they'd also have to put it on the struct/class itself so that it 
works with opaque types and always makes it clear to the compiler 
that a type contains @mutable members and that it cannot do 
things like construct an immutable instance of it. If we had 
that, then all of the common uses cases for mutable in C++ that 
we currently cannot have in D (e.g. ref counting, mutexes, 
caching, etc.) could be used. And the code base wouldn't have to 
be littered with extra modifiers - just on the type declarations 
themselves for the types that have @mutable members and on the 
@mutable members themselves. Yes, it would be creating a backdoor 
in const, but it would be much safer and much more restricted 
than casting away const and mutating (assuming that that were 
legit), and it would take away most of the incentive to cast away 
const. It also wouldn't muck up const for code that doesn't use 
@mutable, and just like @trusted or cast, it would be highly 
greppable.

Certainly, if we're going to add a backdoor, I think that 
something well-controlled like this is the way to go, and if we 
don't add any backdoors, I expect that a lot of D code simply 
won't use const, and it'll lose out completely on the guarantees 
that const provides, leading to more bugs. Adding @mutable will 
increase type safety for code that can't currently use const 
while not reducing type safety for code that already uses const.

- Jonathan M Davis


More information about the Digitalmars-d mailing list