transporting qualifier from parameter to the return value

Steven Schveighoffer schveiguy at yahoo.com
Sat Dec 19 19:19:08 PST 2009


On Sat, 19 Dec 2009 18:35:13 -0500, Jason House  
<jason.james.house at 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



More information about the Digitalmars-d mailing list