RFC: scope and borrowing
Ivan Timokhin via Digitalmars-d
digitalmars-d at puremagic.com
Fri Oct 3 12:05:24 PDT 2014
29.09.2014 18:17, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm at gmx.net>" пишет:
> * What is ElementType!(ByLineImpl!(char, "\n")) in the example from the
> wiki page [1]?
OK, I think I have an idea, but it's not overly elegant.
First of all, I would like to note that the same issue exists with, for
example, findSubstring function (from "scope with owners" section),
which does not have a definite return type (so it's unclear what
ReturnType!findSubstring should be).
Now, an idea that I have is that bare scope should be just a syntactic
sugar for self-owned scope; i.e., `scope(int*) x;` would be just a short
way of writing `scope!x(int*) x;`. This would imply that every scoped
variable has its own unique type, which is probably not that terrible,
considering that they can't be assigned freely to each other either way.
This is also somewhat more natural, because it means that there is no
such thing as just "scoped" variable, it is always connected to a
particular lifetime.
Then, the following:
---
string findSubstring(scope(string) haystack,
scope(string) needle)
---
would be equivalent to
---
string findSubstring(scope!haystack(string) haystack,
scope!needle(string) needle)
---
so each argument is self-owned and all ownership information is lost
(but function is still callable with scoped arguments, because scope
narrowing is allowed).
To preserve ownership information, which is encoded in an arguments'
types, template is needed:
---
scope!haystackOwner(string)
findSubstring(alias haystackOwner)
(scope!haystackOwner(string) haystack,
scope(string) needle)
---
So, findSubstring *still* doesn't have a definite return type, but now
it's because it is a function template, not a function.
As for passing unscoped strings to findSubstring, I see two alternatives:
1) Declare that all unscoped references are implicitly convertible to
scope!GC or something like that (and this conversion is used in such
cases). This one is probably better.
2) Require a separate overload for an unscoped string.
Now, to address ElementType problem, I would like to reconsider `this`
lifetime. Since
---
struct S {
void f() {}
}
---
is more or less equivalent to
---
// not a valid D code
struct S {}
void f(ref S this) {}
---
(not strictly equivalent, of course, but the general idea is like that),
`this` inside a method body would behave like a ref parameter and have
approximately the same lifetime as normal parameters.
Then this code:
---
@property scope!(const this)(Char[]) front() const
---
would become illegal, since the return value is declared as having
function-local scope. However, this code:
---
@property scope!(const this)(Char[])
front(alias thisOwner)() const scope!thisOwner
---
would work just fine, because it explicitly propagates current object's
scope (the return type seems the same, but `this` itself now has a
different type). The downside is that `front` is now callable only for
scoped variables (it seems inappropriate to convert structs to scope!GC).
Now, ByLineImpl!(char, "\n") wouldn't be an input range, because front
would not be defined in an unscoped case, and `scope ByLineImpl!(char,
"\n")` is not a type (because bare scope would be purely a syntactic
sugar in declarations), so not a range.
However, with this declaration:
---
scope(ByLineImpl!(char, "\n")) x = ...;
---
typeof(x) would be scope!x(ByLineImpl!(char, "\n")), and
ElementType!(typeof(x)) would be scope!(const x)(char[]).
As I have said in the beginning, not too elegant, but I think it may
work. As a bonus, this is a little bit more straightforward and requires
less special-casing from the compiler side: has only one scope type
instead of two and no magic tricks with scoped return values. The only
problems that I can see right away are that the code now is a little
verbose, and that 'front' is restricted to scoped variables in a new
version.
Any thoughts?
More information about the Digitalmars-d
mailing list