1 /** 2 Provides a wrapper type to ensure objects are never null 3 */ 4 module optional.notnull; 5 6 import optional.internal; 7 import std.traits: isPointer; 8 9 /** 10 Creates a `NotNull` type 11 12 Params: 13 args = any arguments that need to be passed to T's constructor 14 */ 15 auto notNull(T, Args...)(Args args) { 16 static if (isPointer!T) { 17 import std.traits: PointerTarget; 18 auto instance = new PointerTarget!T(args); 19 } else static if (is(T == class) || is(T == interface)) { 20 auto instance = new T(args); 21 } else { 22 static assert( 23 0, 24 T.stringof ~ " cannot have a value of null", 25 ); 26 } 27 return NotNull!T(instance); 28 } 29 30 /** 31 A NotNull type ensure that the type you give it can never have a null value. So it is always 32 safe to use. It's specifically designed for pointers to values or classes. You can give it 33 a struct as well. 34 35 The one thing to watch out for is inner classes or structs. Since `notNull` is a template function, 36 and it ensures that a type T is always created, it has to allocate memory. But inner classes and 37 structs need a context pointer to be `new`ed, so this only works with static inner classes and 38 structs. 39 40 the constructor is disabled, so you have to use the function `notNull` to construct `NotNull` objects. 41 */ 42 struct NotNull(T) if (is(T == class) || is(T == interface) || isPointer!T) { 43 import std.traits: isPointer; 44 import optional: isNotNull; 45 46 private T _value; 47 @property inout(T) value() inout { return this._value; } 48 alias value this; 49 50 @disable void opAssign(typeof(null)); 51 @disable this(); 52 53 private this(T value) { 54 this._value = value; 55 } 56 57 /** 58 You can only init from another `NotNull` type. 59 */ 60 this(V)(NotNull!V other) { 61 self._value = other._value; 62 } 63 64 /** 65 You can only asign to another `NotNull` type. 66 */ 67 void opAssign(V)(NotNull!V other) { 68 this._value = other._value; 69 } 70 } 71 72 /// 73 @("Example of NotNull") 74 unittest { 75 static class C { int i; void f() { i = 3; } } 76 static struct S { int i; void f() { i = 3; } } 77 78 void f0(NotNull!C c) { 79 c.f(); 80 } 81 82 void f1(NotNull!(S*) sp) { 83 sp.f(); 84 } 85 86 auto c = notNull!C; 87 auto sp = notNull!(S*); 88 89 f0(c); 90 f1(sp); 91 92 assert(c.i == 3); 93 assert(sp.i == 3); 94 } 95