Last active
January 21, 2020 14:01
-
-
Save rikkimax/4cb2cc8ddcac33c1a9bb20de432f9dea to your computer and use it in GitHub Desktop.
This file contains hidden or 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
DoublyLinkedList!Something global; // error, | |
//DoublyLinkedList!Something contains headconst fields and is not marked as such (globals can't be anyway) | |
//also its null (=void wouldn't work either since then it wouldn't be tied to a stack) | |
void func() @system { | |
DoublyLinkedList!Something ll = new DoublyLinkedList!Something(); // ok in @system code but won't allow escaping because of headconst fields. | |
headconst DoublyLinkedList!Something ll = new DoublyLinkedList!Something(); // ok | |
ll.add(Something.init); | |
func2(ll); // ok | |
func3(ll); // error | |
global = ll; | |
} | |
void func2(headconst DoublyLinkedList!Something ll) {} // could be @safe if you wanted it to be | |
void func3(DoublyLinkedList!Something) @system {} // error, DoublyLinkedList!T contains headconst fields. | |
... | |
final class DoublyLinkedList(T) { | |
private { | |
Node nullNode; // can't be escaped because of headconst fields, | |
//but they are =void so they don't need to be set in constructor. | |
headconst Node first; | |
headconst Node last; | |
} | |
this() @system { | |
// the refernces inside of nullNode won't be checked because they are =void and this is @system code | |
// the reference checks for first and last will be checked however as an invariant of the class so we must set them to something. | |
first = nullNode; // if this wasn't here, it would error as first would be 'null' | |
last = nullNode; | |
} | |
void add(T value) @safe nothrow { | |
if (last is nullNode) { | |
first = new Node(nullNode, nullNode, value); // the value's lifetime is that of 'this' DIP25 | |
last = first; | |
} else { | |
last.next = new Node(last, nullNode, value); | |
last = last.next; | |
} | |
} | |
void contains(T value) @safe nothrow { | |
headconst Node current = first; | |
while (current !is nullNode) { | |
if (current.value == value) { | |
return true; | |
} | |
current = current.next; | |
} | |
return false; | |
} | |
void remove(T value) @safe nothrow { | |
headconst Node current = first; | |
while (current !is nullNode) { | |
if (current.value == value) { | |
if (current is first) | |
first = current.next; | |
else | |
current.previous.next = current.next; // ok, doesn't escape the lifetime of 'this' DIP25 | |
if (current is last) | |
last = current.previous; | |
else | |
current.next.previous = current.previous; // no need to do null checks, because its guaranteed to not be null! | |
// GC will handle auto deallocation | |
break; | |
} | |
current = current.next; | |
} | |
} | |
static struct Node { | |
headconst Node previous = void, // ok, can only be read or modified in @system/@trusted code. | |
next = void; | |
T value; | |
} | |
} |
This file contains hidden or 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
Foo global; | |
Foo func() { | |
auto foo = scoped!Foo(1, 2, 3); | |
global = foo; // error, uses alias this which returns via headconst | |
func2(foo); // ok | |
/+return+/ headconst Foo bar = func3(foo); // ok | |
return bar; // error, DIP25 bar outlives foo. | |
return foo; // error, Scoped!T.get() is treated as return ref, so it can't be escaped | |
} | |
void func2(/+return+/ headconst Foo foo) { | |
foo.method(); // ok, doesn't matter if it was passed in via new Foo(1, 2, 3), or via scoped | |
} | |
/+return+/ headconst Foo func3(/+return+/ headconst Foo foo) { // DIP25 pass through checks | |
return foo; | |
} | |
void func4() { | |
Foo foo = new Foo(1, 2, 3); | |
func2(foo); // ok, func2 doesn't escape foo | |
headconst Foo value = func3(foo); // ok, func3 doesn't escape foo and the return value can't escape us | |
// since value is tied to foo's lifetime of this function body | |
} | |
... | |
template scoped(T) if (is(T == class)) { | |
Scoped scoped(Args...)(Args args) { | |
return Scoped(new T(args)); // ugh lets not replicate everything completely | |
} | |
struct Scoped { | |
private T payload; | |
@disable this(this); | |
/+return+/ headconst T get() { | |
return payload; | |
} | |
alias get this; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment