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