On Tuesday, 30 September 2014 at 19:10:19 UTC, Marc Schütz wrote:
I'm convinced this isn't necessary. Let's take `setExtension()` as an example, standing in for any of a class of similar functions. This function allocates memory, returns it, and abandons it; it gives up ownership of the memory. The fact that the memory has been freshly allocated means that it is (head) unique, and therefore the caller (= library user) can take over the ownership. This, in turn, means that the caller can decide how she wants to manage it.

(I'll try to make a sketch on how this can be implemented in another post.)

Ok. What we need for it:

1) @unique, or a way to expressly specify uniqueness on a function's return type, as well as restrict function params by it (and preferably overloading on uniqueness). DMD already has this concept internally, it just needs to be formalized.

2) A few modifications to RefCounted to be constructable from unique values.

3) A wrapper type similar to std.typecons.Unique, that also supports moving. Let's called it Owned(T).

4) Borrowing.

setExtension() can then look like this:

    Owned!string setExtension(in char[] path, in char[] ext);

To be used:

    void saveFileAs(in char[] name) {
        import std.path: setExtension;
        import std.file: write;
        name.                    // scope const(char[])
            setExtension("txt"). // Owned!string
            write(data);
    }

The Owned(T) value implicitly converts to `scope!this(T)` via alias this; it can therefore be conveniently passed to std.file.write() (which already takes the filename as `in`) without copying or moving. The value then is released automatically at the end of the statement, because it is only a temporary and is not assigned to a variable.

For transferring ownership:

    RefCounted!string[] filenames;
    // ...
    filenames ~= name.setExtension("txt").release;

`Owned!T.release()` returns the payload as a unique value, and resets the payload to it's init value (in this case `null`). RefCounted's constructor then accepts this unique value and takes ownership of it. When the Owned value's destructor is called, it finds the payload to be null and doesn't free the memory. Inlining and subsequent optimization can turn the destructor into a no-op in this case.

Optionally, Owned!T can provide an `alias this` to its release method; in this case, the method doesn't need to be called explicitly. It is however debatable whether being explicit with moving isn't the better choice.

Reply via email to