On Wednesday, 29 May 2013 at 04:15:52 UTC, Walter Bright wrote:
It's not in Phobos yet:

I did a pull request a while ago that stalled for some reason I don't remember now, but the struct I did needed to be a little more complex than yours to pass all the tests....

======

struct NotNull(T) if(__traits(compiles, { T t; assert(t is null); }))
{
    private T _notNullData;
    @property inout(T) _notNullDataHelper() inout
    {
assert(_notNullData !is null); // sanity check of invariant
        return _notNullData;
    }
// Apparently a compiler bug - the invariant being uncommented breaks all kinds of stuff.
    // invariant() { assert(_notNullData !is null); }

alias _notNullDataHelper this; /// this is substitutable for the regular (nullable) type
    @disable this();

    // this could arguably break the static type check because
    // you can assign it from a variable that is null.. but I
    // think it is important that NotNull!Object = new Object();
    // works, without having to say assumeNotNull(new Object())
    // for convenience of using with local variables.

    /// constructs with a runtime not null check (via assert())
    this(T value)
    {
        assert(value !is null);
        _notNullData = value;
    }

@disable this(typeof(null)); /// the null literal can be caught at compile time
    @disable typeof(this) opAssign(typeof(null)); /// ditto

    /// .
    NotNull!T opAssign(NotNull!T rhs)
    {
        this._notNullData = rhs._notNullData;
        return this;
    }
}




/// A convenience function to construct a NotNull value from something you know isn't null.
NotNull!T assumeNotNull(T)(T t)
{
return NotNull!T(t); // note the constructor asserts it is not null
}

/// A convenience function to check for null. If you pass null, it will throw an exception. Otherwise, return NotNull!T.
NotNull!T enforceNotNull(T)(T t)
{
    enforce(t !is null);
    return NotNull!T(t);
}

unittest
{
    import core.exception;
    import std.exception;

void NotNullCompiliationTest1()() // I'm making these templates to defer compiling them
    {
NotNull!(int*) defaultInitiliation; // should fail because this would be null otherwise
    }
    assert(!__traits(compiles, NotNullCompiliationTest1!()()));

    void NotNullCompiliationTest2()()
    {
NotNull!(int*) defaultInitiliation = null; // should fail here too at compile time
    }
    assert(!__traits(compiles, NotNullCompiliationTest2!()()));

    int dummy;
    NotNull!(int*) foo = &dummy;

assert(!__traits(compiles, foo = null)); // again, literal null is caught at compile time

    int* test;

    test = &dummy;

    foo = assumeNotNull(test); // should be fine

    void bar(int* a) {}

// these should both compile, since NotNull!T is a subtype of T
    bar(test);
    bar(foo);

    void takesNotNull(NotNull!(int*) a) { }

assert(!__traits(compiles, takesNotNull(test))); // should not work; plain int might be null
    takesNotNull(foo); // should be fine

    takesNotNull(assumeNotNull(test)); // this should work too
assert(!__traits(compiles, takesNotNull(assumeNotNull(null)))); // notNull(null) shouldn't compile
    test = null; // reset our pointer

assertThrown!AssertError(takesNotNull(assumeNotNull(test))); // test is null now, so this should throw an assert failure

    void takesConstNotNull(in NotNull!(int *) a) {}

    test = &dummy; // make it valid again
    takesConstNotNull(assumeNotNull(test)); // should Just Work

NotNull!(int*) foo2 = foo; // we should be able to assign NotNull to other NotNulls too
    foo2 = foo; // including init and assignment
}



=========



One thing I wasn't quite happy with  was a lot of people request:

if(a !is null) {
 // a is now NotNull!T
}


Of course, that directly won't work, but maybe we can get close. Best I could do was

if(a !is null) {
  auto a2 = assumeNotNull(a);
  // use a2 instead
}


which isn't quite what people wanted, but it is simple and works.

Reply via email to