Unique pointers are a restrained type that are used when clear ownership of a resource needs to be established. Here, we'll create a UniqueArray(T)
object, which enforces that only one reference to its payload can exist at any time.
In order to use unique pointers, perform the following steps:
UniqueArray(T)
with a member of type T
.@disable this(this);
.T
.release
method that nullifies the current object and returns UniqueArray
with the payload.alias this
or any other method that may escape your reference.release
, as shown in the following code snippet:import core.stdc.stdlib; struct UniqueArray(T) { private T[] payload; @disable this(this); private this(T[] t) { payload = t; } this(size_t count) { payload = (cast(T*) malloc(count * T.sizeof))[0 ..count]; } UniqueArray!T release() { auto p = this.payload; this.payload = null; return UniqueArray!T(p); } ~this() { if(payload !is null) free(payload.ptr); } auto opDispatch(string method)() { return mixin("payload." ~ method); } T opIndex(size_t idx) { return payload[idx]; } T opIndexAssign(T t, size_t idx) { return payload[idx] =t; } // You may also choose to implement other operators } // testing use void main() { auto ptr = UniqueArray!int(5); // use release since plain assign will fail to compile auto ptr2 = ptr.release; import std.stdio; // ensure the first reference is indeed null writeln(ptr.ptr is null); // and that the second reference is correct writeln(ptr2.length); }
Running the program will print true and 5, confirming the correct operation.
Try writing auto ptr2 = ptr;
as well to get the error message Error: struct unique.UniqueArray!int.UniqueArray is not copyable because it is annotated with @disable. This error will force you to think about where the reference lives and explicitly release it, if required.
Postblits are only run on the recipient objects, making them useless to implement unique move semantics. However, the ability to disable them opens a new possibility; a disabled postblit will force the users toward another method of your choosing if they want to reassign it. We forced them to use release
, which nullifies the sending object, maintaining the reference's uniqueness.
Apart from that, we used all the same techniques we've seen before, with one major difference; we did not rely upon alias this
here to forward operations to the payload. Since alias this
allows implicit conversion, it would let the user bypass the disabled postblit, escaping a reference to the data:
int[] arr = ptr; // this would work with alias this
Instead, we do all forwarding internally. For brevity, in this example we didn't implement all possible operators, opting to implement only basic indexing and partial method forwarding. String mixins and opDispatch
are incredibly flexible tools in D, which we'll explore in greater detail in a later chapter. Here, we just scratched the surface of what they can do by building a code string, "payload." ~ member
and then compiling it as the function body with the mixin
expression. This gives us easy access to most of the payload members, letting us inspect that the code worked as expected.
3.136.233.153