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