What is the case against a struct post-blit default constructor?

Jonathan M Davis jmdavisProg at gmx.com
Wed Oct 10 10:47:37 PDT 2012


On Wednesday, October 10, 2012 13:40:06 foobar wrote:
> Can you please elaborate on where the .init property is being
> relied on? This is an aspect of D I don't really understand.
> What's the difference between a no-arg ctor and one with args in
> relation to this requirement?

init is used anywhere and everywhere that an instance of a type needs to be 
default-initialized. _One_ of those places is a local variable. Not all places 
where an instance of an object needs to be created can be initialized by the 
programmer. The prime example of this would be arrays. If you declare

auto i = new int[](5);

or

int[12] s;

all of the elements in the array need to be initialized or they'll be garbage, 
and there's no way for the programmer to indicate what values they should be. 
The whole point of init is to avoid having variables ever be garbage without 
the programmer explicitly asking for it. And having a default constructor 
wouldn't help one whit with arrays, because the values of their elements must 
be known at compile time (otherwise you couldn't directly initialize member 
variables or static variables or anything else which requires a value at 
compile time with an array). With init, the compiler can take advantage of the 
fact that it knows the init value at compile time to efficiently initialize the 
array.

But even constructing objects sanely relies on init. All user-defined objects 
are fully initialized to what their member variables are directly initialized 
to before their constructors are even called. In the case of a struct, that's 
the struct's init value. It's not for a class, because you can't have a class 
separate from its reference (so it's the reference which gets the init value), 
but the class still has a state equivalent to a struct's init value, and 
that's the state that it has before any of its constructors are called.

If it weren't for that, you'd get the insanity that C++ or Java have with 
regards to the state of objects prior to construction. C++ is particularly bad 
in that each derived class is created in turn, meaning that when a constructor 
is called, the object _is_ that class rather than the derived class that 
you're ultimately constructing (which means that things can go horribly wrong 
if you're stupid enough to call a virtual function from a constructor in C++). 
I believe that Java handles that somewhat better, but it gets bizarre ordering 
issues with regards to initializing member variables that cause problems if 
you try and alter member variables from base classes inside of a derived 
constructor. With D, the object is guaranteed to be in a sane state prior to 
construction.

And without init, even if every place that an object is instantiated could be 
directly initialized by the programmer (which it can't), then you would either 
end up with garbage every time that a variable isn't directly initialized, or 
you'd have to directly initialize them all. In order for D's construction 
model to work, this would include directly initializing _all_ member variables 
even if the constructor then set them to something else (which would actually 
cause problems with const and immutable). And that would get _very_ annoying, 
even if it would be preferable for the local variable to require explicit 
initialization.

Another case where init is required is out parameters. All out parameters are 
set to their init value when the function is called in order to avoid bugs 
caused by reading the value of an out parameter before it's set within the 
function. That wouldn't work at all without init.

One of the more annoying AA bugs makes it so that if the foo function in this 
code

aa[5] = foo();

throws, then aa[5] gets set with a init value of the element type. While this 
clearly shouldn't happen, imagine how much worse it would be if we didn't have 
init, and that element got set to garbage?

There are probably other cases that I can't think of right now where init gets 
used - probably in the runtime if nowhere else. Every place that could 
possibly result in a variable being garbage _doesn't_ result in garbage, 
because we have init.

And regardless of what the language does, there are definitely places where the 
standard library takes advantage of init. It uses it a lot for type 
inferrence, but it also uses it directly in places such as std.algorithm.move. 
Without init, it would end up dealing with garbage values. It's also a 
lifesaver in generic code, because without it, generic code _can't_ initialize 
variables in many cases. Take something like

T t;

if(cond)
{
 ...
 t = getValue();
 ...
}
else
{
 ...
 t = getOtherValue();
 ...
}

How on earth could a generic function initialize t without T.init? void? 
That's just begging for bugs when one the paths doesn't actually set t like 
it's supposed to. It doesn't know anything about the type and therefore 
doesn't know what a reasonable default value would be, so it can't possibly 
initialize t properly.

I can understand prefering that local variables have to be directly 
initialized by the programmer, but it just doesn't scale. Having init is 
_far_more flexible and far more powerful. Any and every situation that might 
need to initialize a variable can do it. Without init, that just isn't 
possible.

- Jonathan M Davis


More information about the Digitalmars-d mailing list