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