OT: How to initialize *non-null reference* lazily? WAS: Re: Proposal : allocations made easier with non nullable types.

Denis Koroskin 2korden at gmail.com
Mon Feb 9 15:50:58 PST 2009


On Mon, 09 Feb 2009 14:41:59 +0300, Ary Borenszweig <ary at esperanto.org.ar> wrote:

[skip]
> How would you do this?
>
> X x;
>
> if(someCondition) {
>    x = new SomeX();
> } else {
>    x =  new SomeOtherX();
> }

Note: I am not talking about an "X x(42);" shortcut now, see the subject.

This is a good question, I think.

One way to do it is as follows:

[code]
T enforce(T? t);

X? tmp; // null

if (someCondition) {
    tmp = new SomeX();
} else {
    tmp = new SomeOtherX();
}

X x = enforce(tmp);
[/code]

But it is too verbose and has one (potentially unsafe) operation (enforce). A check is moved from compile-time to run-time, which is what we wanted to avoid when introduced non-nullable references.

Here is my alternative - just like that, there is no need for extra syntax.

X x;
if(someCondition) {
   x = new SomeX();
} else {
   x =  new SomeOtherX();
}

For example, the following code is *invalid* C#:

[code]
X x;

if (someCondition) {
    X = new SomeX();
}

foo(x); // error:  Use of unassigned local variable 'x'
[/code]

However, if you initialize the variable at all code-paths, then it is ok:

[code]
X x;

if (someCondition) {
    x = new SomeX();
} else {
    x =  new SomeOtherX();
}

foo(x);
[/code]

The only difference is that 'x' is allowed to be null-initialized in C#:

X x = null;
foo(x); // ok, x is /initialized/ (to null)

Making it a compile-time error (x is non-nullable) in D will solve the issue.

Other issue is non-nullable objects as members - how would you make sure that they are properly initialized prior to first access to them?

class Foo {}

class Bar
{
    private Foo x;
    private Foo y;

    this()
    {
        // how would you initialize x and y?
    }
}

A C++ solution would be to initialize them in the initialization list:

Bar::Bar() : x(new Foo()), y(new Foo())
{
}

but it is not flexible enough and we don't have it in D anyway.

As a solution, local variable initialization rules could be extended to member variables - you should have all non-nullable members initialized before you successfully leave the ctor scope and before you pass this to some function or store it in global variable to ensure that 'this' is not visible outside of the ctor scope until all members initialized:

class Bar : Exception
{
    private Foo x;
    private Foo y;
   
    this()
    {
        //super(""); error, base class ctor may call virtual method (e.g., toString) that potentially may access x or y.

        // gFoo = this; // error: x and y are not initalized, possible use of uninitialized variables detected

        x = new Foo();

        if (someCondition) {
            throw new Exception("Just an example"); // okay, object is not constructed
        }

        // throw this; // error: y is not initialized
        // y = new Foo(this); // error: y is not initialized yet

        y = new Foo(x); // okay, x is fine
        
        super("");

        someFunc(this); // okay, all members are initialized by now
    }
}

Do you have any other ideas to solve the issue above?




More information about the Digitalmars-d mailing list