1 /// A version of `std.typecons.Nullable` that reliably works with immutable data types. 2 module rebindable.Nullable; 3 4 import rebindable.Rebindable; 5 import std.traits; 6 7 /// A version of `std.typecons.Nullable` that reliably works with immutable data types. 8 struct Nullable(T) 9 { 10 private bool isNull_ = true; 11 12 private Rebindable!T payload; 13 14 /// Construct a new `Nullable!T`. 15 public this(T value) 16 { 17 this.isNull_ = false; 18 this.payload.set(value); 19 } 20 21 /// Return true if the `Nullable!T` does not contain a value. 22 public bool isNull() const nothrow pure @safe 23 { 24 return this.isNull_; 25 } 26 27 /** 28 * Get the value stored previously. 29 * It is undefined to call this if no value is stored. 30 */ 31 public CopyConstness!(This, T) get(this This)() 32 { 33 assert(!isNull, "Attempted Nullable.get of empty Nullable"); 34 return payload.get; 35 } 36 37 /** 38 * Get the value stored previously. 39 * If no value is stored, `default` is returned. 40 */ 41 public CopyConstness!(This, T) get(this This)(T default_) 42 { 43 if (isNull) 44 { 45 return default_; 46 } 47 return payload.get; 48 } 49 50 /// Assign a new value. 51 public void opAssign(T value) 52 { 53 nullify; 54 payload.set(value); 55 this.isNull_ = false; 56 } 57 58 /// 59 public void opAssign(Nullable!T source) 60 { 61 if (source.isNull) 62 { 63 nullify; 64 } 65 else 66 { 67 this = source.payload.get; 68 } 69 } 70 71 /// If a value is stored, destroy it. 72 public void nullify() 73 { 74 if (!this.isNull_) 75 { 76 this.payload.destroy; 77 this.isNull_ = true; 78 } 79 } 80 81 /// 82 public bool opEquals(const Nullable other) const pure @safe 83 { 84 if (this.isNull != other.isNull) 85 { 86 return false; 87 } 88 if (this.isNull) 89 { 90 return true; 91 } 92 else 93 { 94 return payload.get == other.payload.get; 95 } 96 } 97 } 98 99 /// 100 @nogc pure @safe unittest 101 { 102 Nullable!(const int) ni; 103 104 assert(ni.isNull); 105 106 ni = 5; 107 assert(!ni.isNull); 108 assert(ni.get == 5); 109 110 ni.nullify; 111 assert(ni.isNull); 112 113 assert(ni == Nullable!(const int)()); 114 } 115 116 @nogc pure @safe unittest 117 { 118 import rebindable.ProblematicType : ProblematicType; 119 120 Nullable!int a; 121 a = 3; 122 assert(a.get == 3); 123 124 Nullable!(const int) b; 125 assert(b.get(3) == 3); 126 b = 7; 127 assert(b.get == 7); 128 assert(b.get(3) == 7); 129 130 int refs; 131 int* refsPtr = () @trusted { return &refs; }(); 132 { 133 // construct 134 auto c = Nullable!ProblematicType(ProblematicType(refsPtr)); 135 assert(refs == 1); 136 137 // reassign 138 c = ProblematicType(refsPtr); 139 assert(refs == 1); 140 assert(c.get.properlyInitialized); 141 142 // release 143 c.nullify; 144 assert(refs == 0); 145 } 146 assert(refs == 0); 147 }