On Sat, 19 Dec 2009 18:35:13 -0500, Jason House <jason.james.ho...@gmail.com> wrote:

The docs should also discuss inout variables declared within the function (to store intermediate results while processing input arguments).

Yes, good point. Also important is that inout can ONLY be used on stack variables, it can never be used in global or class member variables.

The meaning of inout by a nested function isn't obvious when the enclosing function is already using inout. Does inout of the nested function match that of the enclosing function? Or are they distinct. If distinct, there may semantically ambiguous cases...

One thing that inout does is it makes the function not really have to care what the calling 'const match' is, because the compiler takes care of that detail at the call site. So the compiled function isn't any different depending on the const match, just the meaning of the return value is different.

Here is an example:

class Foo
{
   private Bar b;

   @property inout(Bar) bee() inout
   {
      return b;
   }
}

class Baz
{
   private Foo f;
   @property inout(Bar) getBOfFoo() inout
   {
inout(Bar) b = f.bee; // the 'const match' is inout, because at this point f is inout.
      return b;
   }
}

Notice that when compiling getBOfFoo, the compiler doesn't have to care what inout(Bar) means in order to call f.bee, it just knows that because f is inout, the const match is inout. It's not like it needs to recompile Foo.bee with a new const match, it just allows the result to be implicitly cast to inout. As long as the rules are followed, it doesn't have to be complicated. It's like a template, but where all the instantiations are identical, just the semantic meaning of the return is parameterized. The rules are carefully designed to make the implementation simple and intuitive, but allow precise const protection where it is needed.

Another example, showing 2 different types of inout with a nested call:


inout(char)[] trim(inout(char)[] input)
{
   while(input.length != 0 && isspace(input[0]))
      input = input[1..$];

   while(input.length != 0 && isspace(input[$-1]))
      input = input[0..$-1];

   return input;
}

inout(char)[][] split(inout(char)[] src, const(char)[] delimiter)
{
delimiter = trim(delimiter); // const match for this one call is const, so it's valid to reassign back to const. // inout(char)[] d = trim(delimiter); // illegal, because the const match is const, and const does not implicitly cast to inout.

    int i;
    inout(char)[][] result;
    while((i = src.indexOf(delimiter)) != src.length)
    {
       result ~= src[0..i];
       src = src[i + delimiter.length..$];
    }
    result ~= src;
    return result;
}

Now, split can be called with a mutable, const, immutable, or even inout src, and it doesn't alter your contract on src's data :) The signature says "src is whatever you want it to be, the constancy will be forwarded to the return value, and I promise not to molest src's data. delimiter will not be molested, but its constancy will not play a part in the return value."

The cool part about it is that it just works the way you want it to, and provides the protection you want it to. It gives you const protection without the viral nature of const that people dislike.

-Steve

Reply via email to