Created
November 9, 2021 16:58
-
-
Save run-dlang/d1982a29423b2cb545bc9fa452d94c5e to your computer and use it in GitHub Desktop.
Code shared from run.dlang.io. Run with '-preview=dip1000'
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- app.d | |
import borrowcheck; | |
import std.stdio; | |
import core.lifetime; | |
@safe Owned!int getOwned() | |
{ | |
Owned!int n = makeOwned(123); | |
// can return by move (or NRVO) | |
return move(n); | |
} | |
@safe void receiveOwned(Owned!int n) | |
{ | |
return; | |
} | |
@safe void main() | |
{ | |
Owned!int n = getOwned(); | |
// can access and modify value | |
n.borrow.apply!writeln; | |
n.borrow.apply!((ref n) { n = 456; }); | |
n.borrow.apply!writeln; | |
// can't escape a reference: | |
//int* p = n.borrow.apply!((return ref n) => &n); | |
// can't escape a borrow: | |
static Borrowed!int global; | |
//global = n.borrow; | |
// can't destroy while borrowed | |
{ | |
Borrowed!int b = n.borrow; | |
//destroy(n); // assertion failure | |
} | |
// can pass by move | |
receiveOwned(move(n)); | |
// no double free | |
assert(n == Owned!int.init); | |
} | |
--- borrowcheck.d | |
import core.stdc.stdlib; | |
struct Owned(T) | |
{ | |
private T* ptr; | |
private int borrowCount; | |
// ptr must be allocated with malloc | |
@system this(T* ptr) | |
{ | |
this.ptr = ptr; | |
} | |
~this() | |
{ | |
assert(borrowCount == 0); | |
// ok because borrowCount == 0 guarantees exclusive access | |
() @trusted { free(ptr); }(); | |
} | |
@disable this(ref inout typeof(this) other) inout; | |
} | |
Owned!T makeOwned(T)(T value) | |
{ | |
// ok because malloc has a safe interface | |
void* rawPtr = (() @trusted => malloc(T.sizeof))(); | |
// ok because we only use this memory to hold a T | |
T* ptr = (() @trusted => cast(T*) rawPtr)(); | |
*ptr = value; | |
// ok because ptr is allocated with malloc | |
return (() @trusted => Owned!T(ptr))(); | |
} | |
struct Borrowed(T) | |
{ | |
Owned!T* owner; | |
this(Owned!T* owner) | |
{ | |
this.owner = owner; | |
assert(owner.borrowCount < int.max, "Too many borrows!"); | |
owner.borrowCount++; | |
} | |
~this() | |
{ | |
owner.borrowCount--; | |
} | |
} | |
Borrowed!T borrow(T)(return ref Owned!T owner) | |
{ | |
return Borrowed!T(&owner); | |
} | |
auto apply(alias fun, T)(auto ref Borrowed!T this_) | |
{ | |
// ensure no reference to *ptr can escape from `apply` | |
scope T* ptr = this_.owner.ptr; | |
return fun(*ptr); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I think your general approach here may be sound (but awkward to use).
However, the implementation does have some problems: