First Draft: cast(ref T)... as shorthand for *cast(T*)&...
Quirin Schroll
qs.il.paperinik at gmail.com
Thu Jan 23 01:56:32 UTC 2025
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.
More information about the dip.development
mailing list