Disallowing S() when struct S has a constructor

Quirin Schroll qs.il.paperinik at gmail.com
Thu Sep 5 17:46:18 UTC 2024


On Friday, 30 August 2024 at 16:32:21 UTC, Jonathan M Davis wrote:
> On Friday, August 30, 2024 9:17:06 AM MDT Steven Schveighoffer 
> via Digitalmars- d wrote:
>> On Thursday, 29 August 2024 at 16:44:46 UTC, Nick Treleaven 
>> wrote:
>> > Should we make S() for any struct an error in the next 
>> > edition?
>>
>> I have a bold suggestion instead -- let's just start having 
>> default constructors.
>>
>> What's stopping us? We are on the cusp of ridding ourselves of 
>> magic runtime hooks, they are all now becoming templates.
>>
>> For instance, setting the length of an array now calls a 
>> template and that template could just call the default 
>> constructor if it exists.
>>
>> Then this whole mess of what S() means vs S.init, or whatnot
>> becomes much more sane.
>>
>> It's something we should start thinking about and discussing.
>
> I don't know. Initialization is just simpler if default 
> constructors aren't a thing. Occasionally, it would be really 
> nice to have them, but the vast majority of the time, they're 
> simply not needed. And there's a _lot_ of D code written with 
> the assumption that they're not a thing. It's already 
> problematic that we added the ability to disable default 
> initialization, since most code tends to assume that that isn't 
> really a thing, and it creates annoying corner cases. 
> Similarly, the fact that types can declare an init member just 
> causes problems, because most code assumes that that's not a 
> thing (and I'd argue that it shouldn't be a thing).

The right course of action is:
- Deprecate `init`. Replace it with `default(T)` (read: “default 
of `T`”) which gives you an uninitialized object of type `T` that 
might or might not be usable. It can’t be hooked (unlike `init`) 
because `default` is a keyword. A constructor transforms a the 
`default(T)` object into an object that satisfies the invariants 
of `T` if any. In general, using `default(T)` is a bad idea 
unless you do it knowing full well what you’re getting.
- Allow default constructors. Those are called on `T()` or 
variables declared of type `T` that are not explicitly 
initialized. `T x;` calls the default constructor of `T`.
- A default constructor is only implicitly present if all data 
members have a default constructor and no constructor is 
explicitly defined.
- A default constructor can be explicitly set to be the generated 
one using `default`: `default this();`

Initialization of a variable:
```d
T x = void; // random garbage; do not use
T y = default; // y contains T’s default; do not use, unless you 
know it’s okay
T z; // default constructor runs on z; safe to use

x.__default(); // bits default(T) onto x
x.__ctor(args); // runs a constructor on x, which in general 
expects that x is default(T)
```

Essentially, `T y = default;` is equivalent to `T y = void;` plus 
`y.__default()`; both are `@system`.
And `T z;` is equivalent to `T z = default;` plus `z.__ctor()` 
and the language knows that this isn’t inherently unsafe, i.e. 
`@safe` depends on the constructor.

C++ has had default constructors forever. It’s one of the few 
decisions where I’m convinced that C++ got it right and D isn’t. 
“Variable declarations must be cheap” is a dogma that I’d expect 
in a language like C or Zig, not in a language like D, where 
frequently, safety wins over performance. The fact that I can 
declare a variable in D and it might not be safe to use is a 
problem.

In C++, if you design a data structure or algorithm, the question 
whether a type parameter `T` must be default-constructible has an 
obvious answer (to me at least).

Last but not least (I cut it out, but you did mention it), D has 
`static opCall`, which IMO is a worse design than having default 
constructors because it means that `T()` can mean one thing or 
another, but worst, it may not return a `T` at all! Just thinking 
about it, `init` need not have the right type as well. (And some 
other magical properties like `tupleof`, `sizeof`, and other 
`of`s can be defined to lie, too.)


More information about the Digitalmars-d mailing list