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

Reply via email to