1 /// `Rebindable!T` is a container for `T` that permits managing `T`'s lifetime explicitly.
2 module rebindable.Rebindable;
3 
4 import rebindable.DeepUnqual;
5 import std.traits;
6 
7 /// `Rebindable!T` is a container for `T` that permits managing `T`'s lifetime explicitly.
8 public struct Rebindable(T)
9 {
10     private DeepUnqual!T store;
11 
12     /// Construct a Rebindable from a value.
13     public this(T value)
14     {
15         set(value);
16     }
17 
18     /**
19      * Move-return the stored value by value.
20      * This is equivalent to `get` followed by `destroy`.
21      *
22      * This operation invalidates the `Rebindable`.
23      * Until `set` is called again, `move` and `get` are undefined.
24      */
25     public CopyConstness!(This, T) move(this This)() @safe
26     {
27         auto result = get;
28 
29         destroy;
30         return result;
31     }
32 
33     /**
34      * Replace a currently stored value with a new value.
35      * This is equivalent to `destroy` followed by `set`.
36      */
37     public void replace()(T value) @safe
38     {
39         destroy;
40         set(value);
41     }
42 
43     /**
44      * Get a copy of a previously stored value.
45      */
46     public CopyConstness!(This, T) get(this This)() @nogc nothrow pure @safe
47     {
48         return reference;
49     }
50 
51     /**
52      * Set the `Rebindable` to a new value.
53      * Calling this function while an existing value is stored is undefined.
54      * The passed value is copied.
55      */
56     public void set(T value) @trusted
57     {
58         // Since DeepUnqual doesn't call destructors, we deliberately leak one copy.
59         static union BlindCopy
60         {
61             T value;
62         }
63 
64         BlindCopy copy = BlindCopy(value);
65         this.store = *cast(DeepUnqual!T*)©
66     }
67 
68     /**
69      * Destroys the stored value.
70      * This is equivalent to a variable going out of scope.
71      */
72     public void destroy() @trusted
73     {
74         import std.typecons : No;
75 
76         static if (is(T == class) || is(T == interface))
77         {
78             reference = null;
79         }
80         else
81         {
82             // call possible struct destructors
83             .destroy!(No.initialize)(reference);
84         }
85     }
86 
87     private ref CopyConstness!(This, T) reference(this This)() @nogc nothrow pure @trusted
88     {
89         return *cast(typeof(return)*)&this.store;
90     }
91 }
92 
93 ///
94 unittest
95 {
96     import rebindable.Rebindable : Rebindable;
97 
98     struct DataStructure(T)
99     {
100         private Rebindable!T store;
101 
102         this(T value)
103         {
104             this.store.set(value);
105         }
106 
107         ~this()
108         {
109             this.store.destroy;
110         }
111 
112         T get()
113         {
114             return this.store.get;
115         }
116 
117         void set(T value)
118         {
119             this.store.replace(value);
120         }
121     }
122 
123     DataStructure!(const int) ds;
124 
125     ds.set(5);
126     assert(ds.get == 5);
127 }