On Fri, 19 Mar 2010 22:56:59 -0400, Andrei Alexandrescu
<[email protected]> wrote:
Hello,
Walter and I are mulling over an idea that may turn out to be extremely
interesting for D.
Consider the following desideratum: D is a garbage-collected language,
but we want to be able to define "completely encapsulated" types. That
means types that never escape pointers to their internal data. If we
manage to do so, then the type can manage memory any way it desires.
Consider the case of a simple container, e.g. an array. If we manage to
convince the array to never "leak" a reference to an element, the array
is free to deallocate memory as it wishes.
One way to achieve that is by simply having the array type return
rvalues. That is (stylized code below for a type Array!T) instead of:
ref T opIndex(size_t index);
we'd have
T opIndex(size_t index);
and so on. That way nobody can escape a pointer to stuff pointing into
the array, so the array is safe and can reallocate memory whenever it
wishes without fearing that someone has squirreled a pointer to its
buffer.
I'm encouraged that Scott Meyers' Effective C++ topic 'Avoid returning
"handles" to internal data' talks essentially about that.
Of course, the problem is that you now can't change stuff inside the
array. Also there is an efficiency issue.
So I was thinking of the following: how about still returning a
reference, but define a rule in the language that references can't
escape - you can't take their address and squirrel it away; the only
thing you can do is use them right on the spot or pass them down to
functions.
Essentially that means: "I'm giving you the address of the object, but
in a scoped manner - there's nothing you can do to save it beyond the
current expression."
To clarify, for an Array!T object, you can do:
arr[5] = obj;
but you can't do:
T* p = & arr[5];
The former uses the reference anonymously right there, and the latter is
verboten because it could potentially escape "p" outside the current
expression, and an array resize would leave p dangling.
Are we hamstringing the language in a way that would disable important
idioms?
What about returning refs that are ref returns or part of other refs? For
example:
ref int foo(ref int x)
{
return x;
}
ref int bar()
{
int x;
return foo(x);
}
The reason I bring this up is because it's exactly what a struct is
doing. Basically, the problem is not so much that you cannot squirrel it
away, but you can return it out of the stack scope it was allocated on. I
don't know if there's a way to fix this without restricting struct members
from returning ref items.
For instance, try to find a rule that prevents the above from compiling,
but allows the following to compile.
struct S
{
private int x;
ref int getX() { return x;}
}
struct T
{
S s;
ref int getSX() { return s.x; }
}
-Steve