Accidentally killing immutable is too easy in D (Was: cast()x - a valid expression?)

Jonathan M Davis jmdavisProg at gmx.com
Thu Jun 2 16:41:18 PDT 2011


On 2011-06-02 16:22, Steven Schveighoffer wrote:
> On Thu, 02 Jun 2011 18:39:48 -0400, Jonathan M Davis <jmdavisProg at gmx.com>
> 
> wrote:
> > On 2011-06-02 15:13, Steven Schveighoffer wrote:
> >> On Thu, 02 Jun 2011 17:52:41 -0400, Jonathan M Davis
> >> <jmdavisProg at gmx.com>
> >> 
> >> wrote:
> >> > On 2011-06-02 09:02, Bruno Medeiros wrote:
> >> >> On 01/06/2011 22:12, Jonathan M Davis wrote:
> >> >> >> It's nice in a way, but it's all based on the way cast handles
> >> >> >> modifiers.
> >> >> >> 
> >> >> >> > And I've always been a bit unconfortable with how that's handled
> >> >> 
> >> >> (In
> >> >> 
> >> >> >> > fact, I was just thinking about this yesterday). Specifically,
> >> 
> >> it
> >> 
> >> >> >> > seems extremely bad that it's so incredibly easy to accidentaly
> >> >> 
> >> >> cast
> >> >> 
> >> >> >> > away things like const and immutable:
> >> >> >> > 
> >> >> >> > For example, if I know I have an array of uint's, and I want to
> >> >> 
> >> >> deal
> >> >> 
> >> >> >> > with the individual bytes, it's perfectly safe and sensible to
> >> 
> >> cast
> >> 
> >> >> >> > it to a ubyte[] (a long as you factor in endianness, of course).
> >> >> 
> >> >> So,
> >> >> 
> >> >> >> > you do "cast(ubyte[])myArray". But, OOPS!!: If myArray happened
> >> 
> >> to
> >> 
> >> >> >> > be immutable, then merely trying to cast the type has
> >> 
> >> inadvertantly
> >> 
> >> >> >> > cast-away immutable. Not good! Casting away const/immutable
> >> 
> >> really,
> >> 
> >> >> >> > really should have to be explict.
> >> >> >> > 
> >> >> >> > Of course, you can probably use some fancy helper templates to
> >> 
> >> make
> >> 
> >> >> >> > sure you preserve all modifiers. But needing to do so is just
> >> >> 
> >> >> asking
> >> >> 
> >> >> >> > for mistakes: it seems like a huge violation of "make the right
> >> 
> >> way
> >> 
> >> >> >> > easy, and the wrong way hard".
> >> >> > 
> >> >> > You really shouldn't be casting much anyway. It's the sort of
> >> 
> >> feature
> >> 
> >> >> > where you're only supposed to use it when you know what you're
> >> 
> >> doing
> >> 
> >> >> > when you use it. And if there's really any possibility that you're
> >> >> > dealing with immutable, perhaps you should be casting to const
> >> 
> >> rather
> >> 
> >> >> > than mutable. Personally, I find how C++ created multiple types of
> >> >> 
> >> >> cast
> >> >> 
> >> >> > (include const_cast)_highly_ annoying and generally useless, and
> >> >> > I'm_very_ glad that D didn't do anything of the sort.
> >> >> > 
> >> >> > - Jonathan M Davis
> >> >> 
> >> >> "you're only supposed to use it when you know what you're doing when
> >> 
> >> you
> >> 
> >> >> use it"
> >> >> Well, that's kinda the requirement for any feature, isn't it?... :P
> >> >> Well, kinda, I do know what you mean. Yes, one needs to properly
> >> 
> >> learn
> >> 
> >> >> how cast() works, but that doesn't mean it would not be better for
> >> 
> >> the
> >> 
> >> >> feature to be designed in a way that is easier to learn (and just as
> >> >> powerful to use), or actually, it doesn't mean that even if you do
> >> 
> >> know
> >> 
> >> >> how cast() works, that you are not still likely to make a mistake
> >> 
> >> when
> >> 
> >> >> using it.
> >> > 
> >> > Really, you should only be using casts when you need a cast, and you
> >> > should be
> >> > careful when you use them. So, you have to know what you're doing and
> >> > why in
> >> > that particular instance and not use them willy-nilly. In most code,
> >> > casts
> >> > should be quite rare. If you're doing a lot of low-level stuff, then
> >> 
> >> they
> >> 
> >> > might be more common, but in general, they're a sledgehammer that
> >> > shouldn't be
> >> > used except when you really need them.
> >> 
> >> Casting is the recommended way to determine if something is actually a
> >> derived type:
> >> 
> >> interface I {}
> >> 
> >> class C : I {}
> >> 
> >> void foo(I i) // change this to const(I) and you have a huge const bug!
> >> {
> >> if(C c = cast(C)i)
> >> {
> >> // optimized branch
> >> }
> >> else
> >> {
> >> // default branch
> >> }
> >> }
> >> 
> >> so it's not so easy to say you should "never" cast. BTW, dcollections
> >> uses this quite a bit to support operations between multiple types of
> >> collections.
> >> 
> >> It would be nice if at least dynamic cast (with the added rules to
> >> prevent
> >> casting away const/immutable/shared) was a different syntax from cast,
> >> since it's a completely safe usage of casting (as long as you manually
> >> forward modifiers). It's as safe as to! is when used with the right
> >> context.
> > 
> > No. It's not the case that you should _never_ cast, but in most
> > programs, it
> > should still be rare. And yes, casting is the proper way to determine
> > whether
> > an object's type, is derived from a particular class, but it's generally
> > considered bad OO programming to have to ask that sort of question very
> > often.
> > 
> > There are a few places that you're forced to (such as opEquals), but in
> > general, it's a symptom of a bad design. Code should generally be
> > written in
> > such a manner that you either know what it's type is or don't care. You
> > shouldn't generally have to ask it. Obviously, there are exceptions, but
> > they
> > should be exceptions rather than the norm. As such, casts should be
> > fairly
> > rare.
> 
> Any place you have double-dispatch, you need to use it in the dispatch
> function.
> 
> > Personally, I find the added complexity of C++'s special casts to _far_
> > outweight what benefit they give you. Yes, having something like Java's
> > instanceof instead of having to cast to check whether an object's type is
> > derived from a particular type would arguably be an improvement, but
> > even it
> > shouldn't be used all that often, and I definitely don't think that
> > creating a
> > set of cast operators for different types of cast would be a good idea
> > at all.
> 
> added complexity? How so? There are four types of casts:
> 
> static_cast -> cast without discarding const or volatile
> const_cast -> cast only removing const or volatile
> dynamic_cast -> do a runtime cast to see if an object is actually a
> derived type
> reinterpret_cast -> force everything, and avoid any cast overloads.
> 
> In all of these, only const_cast and reinterpret_cast are unsafe. Each
> one clearly separates what you wish to do.
> 
> this is not complication, this is expressiveness. Something that D's
> casting does not have. D's casting conflates multiple things together,
> and because of the "I trust you" nature that casts have, you can easily
> make mistakes without complaint from the compiler. The fact that a
> dynamic cast is inherently unsafe in D is unnecessary. I don't know if it
> will change, but I can't say it's better than C++. It can be made better
> than C++, but I think I'd rather have the four cast types than what D has
> now.

It is definitely added complexity. Now, it allows you to be more explicit and 
what you want and so it does add expressiveness as well, but with one cast, 
you know which cast you need to use. With 4, you have to figure out which one 
to use in a particular instance. Honestly, in discussing the C++ special casts 
with other programmers in the past, I've gotten the impression that your 
average C++ programmer does not understand them all, and many of them just use 
C-style casts all the time. I care about such details in a language and try 
and understand them, and I confess that I still am not really sure of the 
exact difference between static_cast and reinterpret_cast and when you're 
supposed to use one or the other - especially when casting pointers. 
const_cast and dynamic_cast are easy enough, but having 4 casts instead of 1 
where the compiler just does the right thing is definitely added complexity, 
and it's highly subjective as to whether the added expressivness is worth the 
complexity. I don't think that it is. You obviously disagree. And for better 
or worse, D took the route of simplicity rather than expressiveness in this 
case.

- Jonathan M Davis


More information about the Digitalmars-d mailing list