Ownership and Borrowing in D

Sebastiaan Koppe mail at skoppe.eu
Sat Jul 20 12:42:39 UTC 2019


On Tuesday, 16 July 2019 at 06:12:42 UTC, Walter Bright wrote:
> Now I just have to deliver the goods!

I am very excited about this, together with unique these are some 
very desired semantics.

I have a practical question. In spasm I have to solve the 
lifetime of javascript objects that are referenced from D. The 
way it works is that objects in Javascript are put in a big array 
and the index - the handle if you will - is passed to D. When D 
code wants to do anything with the js object, it passes the 
handle to a js glue function which looks up the object in the 
table and does what it needs to.

The trick is finding an efficient mechanism to free the js 
objects in the js array. At first I tried reference counting - 
which worked, but introduced a lot of code bloat.

Now I have settled on having the wrapper struct (JsHandle) of the 
handle be non-copyable, and pass it around by either moving or 
using `ref scope` with dip1000 and @safe. Then if you need to 
have the handle around for longer you can always wrap it in a RC 
wrapper.

This works remarkably well. It gives cheap, efficient and safe 
code and still allows multiple long-lived references with RC if 
that is what is wanted.

There are 2 issues I currently have.

First some code:

---
alias Handle = uint;

struct JsHandle {
   nothrow:
   package Handle handle;
   alias handle this;
   ~this() {
     if (handle != invalid)
       spasm_removeObject(handle);
   }
   @disable this(this);
}

struct HTMLElement {
   spasm.bindings.dom.Element _parent;
   alias _parent this;
   this(JsHandle h) {
     _parent = .Element(h);
   }
   // ... functions
}

struct HTMLCanvasElement {
   spasm.bindings.html.HTMLElement _parent;
   alias _parent this;
   this(JsHandle h) {
     _parent = .HTMLElement(h);
   }
  // ... functions
}
---

1) Sometimes the web api's accept an Nullable/Optional, which 
means I have to wrap, for instance, the HTMLElement into an 
Optional type and pass that. Of course, I want to maintain the 
unique reference constraint and enforce that the Optional won't 
outlive the original HTMLElement. Which I can't, since they are 
all non-pointers and I can't store a scope ref inside a Optional. 
The solution I have now is to create a Nullable!(T*). This does 
solve the unique semantics, at the expense of having an api that 
accepts both Nullable!(T) and Nullable!(T*). Pretty ugly.

2) the other issue is that I sometimes want to "upcast" a 
HTMLElement to a HTMLCanvasElement. I can simply do this by 
moving the handle around, but I want the HTMLCanvasElement not to 
outlive the HTMLElement. Again, the solution is to involve 
pointers. Since all wrapper structs have the same memory layout 
(only a uint), I used this hack:

---
template as(Target) {
   auto as(Source)(scope return ref Source s) {
     return cast(Target*)&s;
   }
}
---

Anyway my question is whether the ownership and borrowing 
semantics ever gets applied to non-pointers?


More information about the Digitalmars-d-announce mailing list