On Wednesday, 16 September 2015 at 17:15:55 UTC, Ola Fosheim
Grøstad wrote:
On Wednesday, 16 September 2015 at 17:03:14 UTC, Marc Schütz
wrote:
On Tuesday, 15 September 2015 at 21:44:25 UTC, Freddy wrote:
On Tuesday, 15 September 2015 at 17:45:45 UTC, Freddy wrote:
Rust style memory management in a library
Wait nevermind about that part, it's harder than I thought.
Yeah, I thought about type-states as a way of implementing
borrowing, too. I think the biggest difficulty is that the
state of one object (the owner) can be affected by what
happens in other objects (i.e., it becomes mutable again when
those are destroyed).
If the borrowed reference itself follows move semantics, can't
you just require it to be swallowed by it's origin as the
"close" operation?
pseudocode:
File<Open> f = open();
(File<OpenLending> f, FileRef<Ready> r) = f.borrow();
dostuff(r);
(File<Open> f, FileRef<Void> r) = f.unborrow(r);
File<Closed> f = f.close()
But the `unborrow` is explicit. What I'd want is to use the
implicit destructor call:
struct S {
static struct Ref {
private @typestate alias owner;
private S* p;
@disable this();
this()
typestate(alias owner) {
this.owner := owner; // re-alias operator
this.owner.refcount++;
}
body {
this.p = &owner;
}
this(this) {
this.owner.refcount++;
}
~this() {
this.owner.refcount--;
}
}
@typestate size_t refcount = 0;
S.Ref opUnary(string op : "*")() {
// overload address operator (not yet supported)
return S.Ref(@typestate this);
}
~this() static if(refcount == 0) { }
}
void foo(scope S.Ref p);
void bar(-> S.Ref p); // move
void baz(S.Ref p);
S a; // => S<0>
{
auto p = &a; // => S<1>
foo(p); // pass-by-scope doesn't copy or destroy
// => S<1>
p.~this(); // (implicit) => S<0>
}
{
auto p = &a; // => S<1>
bar(p); // pass-by-move, no copy or destruction
// => S<1>
p.~this(); // (implicit) => S<0>
}
{
auto p = &a; // => S<1>
baz(p); // compiler sees only the copy,
// but no destructor => S<2>
p.~this(); // (implicit) => S<1>
}
a.~this(); // ERROR: a.refcount != 0
The first two cases can be analyzed at the call site. But the
third one is problematic, because inside `baz()`, the compiler
doesn't know where the alias actually points to, because it could
be in an entirely different compilation unit. I guess this can be
solved by disallowing all operations modifying or depending on an
alias type-state.
(Other complicated things, like preserving type-state through
references or array indices, probably shouldn't even be
attempted.)