On Monday, 16 March 2015 at 20:50:46 UTC, Marc Schütz wrote:
On Monday, 16 March 2015 at 19:43:01 UTC, Zach the Mystic wrote:
I always tend to think of member functions as if they weren't:
struct S {
T t;
ref T fun() return {
return t;
}
}
In my head, I just translate fun() above to:
ref T fun(return S* __this) {
return __this.t;
}
Therefore whatever the scope of `__this`, that's the scope of
the return, just like it would be for any other parameter.
Then:
S s;
s.fun();
... is really just `fun(s);` in disguise. That's why it's hard
for me to grasp `scope` members, because they seem to me to be
just as scope as their parent, whether global or local.
It works just the same:
struct S {
private int* payload_;
ref int* payload() return {
return payload_;
}
}
ref int* payload(scope ref S __this) return {
return __this.payload_; // well, imagine it's not private
}
More accurately,
// `return` is moved
ref int* payload(return scope ref S __this) {
return __this.payload_;
}
I think that if you need `return` to make it safe, there's much
less need for `scope`.
Both the S.payload() and the free-standing payload() do the
same thing.
From inside the functions, `return` tells us that we're allowed
to a reference to our payload. From the caller's point of view,
it signifies that the return value is scoped to the first
argument, or `this` respectively.
To reiterate, `scope` members are just syntactical sugar for
the kinds of accessor methods/functions in the example code.
There's nothing special about them.
That's fine, but then there's the argument that syntax sugar is
different from "real" functionality. To add it would require a
compelling use case.
My fundamental issue with `scope` in general is that it should be
the safe default, which means it doesn't really need to appear
that often. If @safe is default, the compiler would force you to
mark any parameter `return` when it detected such a return.
How a member could be scope when the parent is global is hard
for me to imagine.
The following is clear, right?
int* p;
scope int* borrowed = p;
That's clearly allowed, we're storing a reference to a global
or GC object into a scope variable. Now let's use `S`, which
contains an `int*` member:
S s;
scope S borrowed_s = s;
That's also ok. Doesn't matter whether it's the pointer itself,
or something containing the pointer. And now the final step:
scope int* p2;
p2 = s.payload; // OK
p2 = borrowed_s.payload; // also OK
static int* p3;
p3 = s.payload; // NOT OK!
However, if `payload` were not the accessor method/function,
but instead a simple (non-scope) member of `S`, that last line
would be allowed, because there is nothing restricting its use.
See above. With `return` being forced on the implicit this
parameter:
ref int* payload(return /*scope*/ ref S __this) { ... }
`return` covers the need for safety, unless I'm still missing
something.
For members that the struct owns and want's to manage itself,
this is not good. Therefore, we make it private and allow
access to it only through accessor methods/functions that are
annotated with `return`. But we could accidentally forget an
annotation, and the pointer could escape.
Same argument. Forgetting `return` in safe code == compiler
error. I think DIP25 already does this.