Would love to override default ctor of struct

Jonathan M Davis newsgroup.d at jmdavisprog.com
Fri Feb 8 20:21:05 UTC 2019


On Thursday, February 7, 2019 4:14:47 AM MST Nick Treleaven via Digitalmars-
d wrote:
> On Thursday, 31 January 2019 at 15:41:28 UTC, Jonathan M Davis
>
> wrote:
> > The main problem with allowing a constructor with no parameters
> > when you @disable this(); is that it wouldn't act like a
> > default constructor and really couldn't act like one in
> > general, because too much is designed around using init. To
>
> I'm not asking for a default constructor, only an explicit
> nullary argument ctor.
>
> > Also, it's perfectly possible to use a factory function to
> > create immutable objects. Worst case, you have to cast to
> > immutable in the factory function, but it works just fine.
>
> Have you changed your opinion? You said only that "in some
> situations" casts can be used:
>
> "The only downside that I can think of at the moment for using a
> factory function over having this() as a non-default constructor
> is that when constructing immutable objects, the constructor
> usually has to do it (though in some situations, casts could be
> used - that depends primarily on whether the data is guaranteed
> to be unique). So, such a factory function would require a
> special constructor with dummy arguments or something similar in
> order to construct immutable objects."

At the moment, the only situations that I can think of where casting to
immutable would not work are actually situations where you couldn't use a
constructor anyway. The problem is that you can't cast data that's not
guaranteed to be unique to immutable, or you risk violating the type system.
With a constructor that did no casting, that would mean that you could only
accept data that was either immutable or implicitly convertible to
immutable. Anything else wouldn't compile. So, to get around that, you would
have to cast just like you'd need to do with a factory function, and you'd
then be at risk of violating the type system whenever the data was not
actually unique.

Because the factory function would have to cast, the programmer would have
to be more careful about making sure that they didn't cast anything that
wasn't guaranteed to be unique (whereas in a constructor with no casts, you
wouldn't have to worry about that), so it's @trusted and riskier, but it
works so long as you're appropriately careful.

Having a pure factory function would potentially fix the need for casting,
but if you're trying to create a default constructor, odds are that it can't
be pure anyway. If it could, then it's almost guaranteed that you could just
put everything in the init value and not need a constructor - though there
are some situations where that still wouldn't work - e.g. initializing a
member variable that was an AA (since AA's don't currently travel from
compile time to runtime) or when you wanted a dynamic array with known
values but where each instance of the struct gets a dynamic array referring
to unique memory rather than all instances sharing the same memory like you
get with the init value.


> > AFAIK, aside from stuff that requires that the type be
> > default-initialized, the only thing that you can't really do
> > with @disable this(); is to completely guarantee that all
> > objects are constructed via the factory method, because the
> > init value for the type still exists and can still be used. It
> > just isn't used for default initialization. So, having a type
> > with @disable this(); that does not take the init value into
> > account can cause bugs.
>
> struct S
> {
>      static @disable S init;
>      ...

I'm surprised that you can do that at the moment. I thought that that was at
least deprecated by now. TypeInfo was specifically fixed a while back so
that it didn't have an init function so that we could deprecate the ability
to declare any kind of override for init, because allowing it causes
problems. One example is that it causes serious problems with
metaprogramming, because init is _the_ reliable way to get your hands on an
instance of a type. It was quite purposefully the case that @disable this();
had no effect on the ability to reference this, because too much relies on
being able to access it. Trying to make it so that you cannot rely on init
existing would be extremely disruptive.

Now, even then, @disabling init doesn't actually get rid of the init value.
It's still there and used to do stuff like initialize the struct prior to
the constructor being called.

Also, interestingly, I just tried

struct S
{
    @disable this();
    static @disable S init;
}

and it won't compile, because that init variable uses default
initialization. That can be gotten around be declaring something like

static @disable S init();

instead, but really, trying to get around the fact that types have init
values in D is like trying to plug holes in a cheese grater.

Anyone who wants to try to add default constructors to D is free to create a
DIP to do so, but I expect that anyone who attempted it would have a serious
battle ahead of them. You would need an extremely good understanding of
exactly how the init value works and everywhere that it is used and why to
even attempt to come up with a proposal to inject default constructors that
would work (assuming that it's even possible to make init and default
constructors interact in a sane manner). And even assuming that you had the
appropriate technical knowledge to correctly indicate exactly how default
constructors would work to be able to integrate with the existing language,
I expect that convincing Walter and Andrei that it was worth it would not be
an easy task.

> > But all of the other stuff with regards to immutable and the
> > like can still be done. It just can't necessarily be done in an
> > @safe manner.
>
> This is a significant downside, you can't have a @safe struct
> that requires runtime calls for its construction but no arguments.

It could be @trusted. It's just that it's up to the programmer to verify it.
And sure, that's not ideal, but @safe can't be used everywhere without the
programmer having to deal with @trusted to make it work occasionally, and
we're talking about a pretty niche situation here with trying to have
something similar to a default constructor for an immutable object.
Regardless, with how D relies on init, that's just the way that it is.

- Jonathan M Davis





More information about the Digitalmars-d mailing list