On Tuesday, 21 January 2025 at 13:28:48 UTC, ryuukk_ wrote:
>On Tuesday, 21 January 2025 at 03:43:02 UTC, Paul Backus wrote:
>On Monday, 20 January 2025 at 22:55:29 UTC, Walter Bright wrote:
>On 1/19/2025 9:33 AM, Paul Backus wrote:
>Why not just add a simple reinterpretCast!T
helper function to Phobos or druntime?
Great question.
reinterpretcast!T
is just ugly. I never liked it in C++. cast(ref T)
, on the other hand, looks nice.
Would you be open to using a helper function if it had a shorter, less ugly name?
please stop trying to kill D improvement proposals by suggesting Yet Another Template
nobody, literally, nobody ever thought of using a template TO CAST, this is ridiculous, stop
[…]
Generally, I somewhat agree. A good idea that died this death is dependency-carrying declarations aka. imports in function declarations, because you can make Import!"std.conv".text
work.
However, in this particular case, I’m on the templaters’ side. A reinterpret cast is so rare, you can be bothered to import some module for it, especially because it’s an expert tool. It’s not like an import which virtually every D program has.
To be honest, requiring reinterpretCast!int(someLong)
and allowing cast(string)someLong
makes more sense than the reverse. Currently, both require an import, but what would naïve users expect to have as a core-language feature? If I were new to D, to get from a long
to a string
, I’d try someLong.toString()
and the cast, possibly string(someLong)
. Then, I’ll try to look it up with an annoyed mood to find the template.
C++ made reinterpret_cast<…>(…)
purposefully annoying to type. And I know the critique, C++ bad, we different. But C++ got some things right, and one was making reinterpret-casts annoying to type and obvious to spot. D seriously lacks discipline in making dangerous constructs look dangerous.
There at least these three levels of cast. An implicit cast is a safe cast where data can’t be lost, e.g. casting a ushort
to uint
or uint*
to const(uint)*
. An explicit cast is a safe cast where data can be lost, e.g. casting uint
to ushort
. A dangerous cast is one that could incur undefined behavior, e.g. casting const(uint)*
to uint*
.
Kind | C++ | D |
---|---|---|
Implicit | T{…} |
T(…) |
Explicit | static_cast<T>(…) |
cast(T)… |
Dangerous | reinterpret_cast<T>(…) |
cast(T)… |
Some static_cast
are dangerous, and of course there are more dangerous C++ casts such as C-style casts, T(…)
, and const_cast<T>(…)
, it’s a mess, but many dangerous casts are easily spotted. However, D only ever offers two syntaxes, and one is consistently used for explicit casts and dangerous casts. As a rule of thumb, if the argument to cast
involves casting to a pointer, it’s dangerous. If it were up to me, cast
should only ever do @safe
casts, and be required for stuff like cast(ushort) myInt
. For const(uint)*
to uint*
, D should require something like, __traits(unsafeCast, uint*, &myConstInt)
. Of course, I’m aware this ship has sailed.
The bottom line is: It’s way more reasonable having to write import std.conv;
such that reinterpretCast
is available than you presume. It is much more reasonable than having to write import std.conv;
to use myInt.to!string
instead of myInt.toString()
working without any import whatsoever.