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