Passing arguments into functions - in, out, inout, const, and contracts

Derek Parnell derek at psych.ward
Sun Feb 11 04:29:33 PST 2007


On Sun, 11 Feb 2007 01:09:33 -0500, Jason House wrote:

> I believe that everything (except maybe fundamental types such as int) 
> are passed by reference.  I'd expect "in" parameters to be considered 
> const, but I know that member functions can't be declared const.
> 
> It seems to me that in/out/inout has no effect on how the language 
> operates.  I'm wondering how those affect the language.  Does "in" 
> impose a contract that the ending value must equal the starting value? 
> I also haven't thought of any good way to differentiate out from inout 
> from the compiler's standpoint (obviously, it makes sense to users).

Everyone describes the in/inout/out attributes in a different manner, so I
may as well be a part of that movement too <G>

** 'in' **
causes the parameter to be passed by value. This means that whatever the
called function does to the passed parameter, the original value prior to
being called is not modified. In effect, the caller passes a copy of the
parameter's value to the called program. But the usual misunderstanding is
'what exactly is the parameter'? For scalar types (char, int, float,
etc...) this is simply a copy of the arguments value. For structs, it is a
copy of the whole struct. For fixed-length arrays, it is a copy of the
entire array. For variable-length arrays and for objects, it is the copy of
the reference that is given to the called program and not the data that the
reference points to. This means that the called program can mess about with
the reference all it likes but the original reference is unchanged. Thus if
a function is passed a char[] parameter, and the function changes the
length, the array's length doesn't actually get changed. This also means
that the called function has unrestricted access to the data being referred
to, so that even if a char[] is passed as 'in', the function is still free
to modified the characters in that array - just not its RAM pointer or its
length.


  char[] a = "abc".dup;
  void foo(in char[] x)
  {
      x[1] = 'B';  // This will replace the second character.
      x ~= 'd';    // This doesn't actually change the length.
  }
  std.stdio.writefln("%s", a); // outputs "aBc";

** 'inout' **
causes the parameter to be passed by reference. This means that the called
function actually gets the address of the argument rather than its value. 
Having the address, means that it can get its current value and change it
such that the change is propagated back to the caller. This has
implications for variable-length arrays and objects. In these cases, the
called function receives the address of the reference. And with that the
function can modify the reference. For example if the function receives an
inout char[], it can change the length of the string and/or change its
location in RAM.

  char[] a = "abc".dup;
  void foo(inout char[] x)
  {
      x[1] = 'B';  // This will replace the second character.
      x ~= 'd'; // Change its length, and if this means that the
                // allocated RAM needs to be expanded, the function
                // also allocates new RAM can copies the old data 
                // to it and invalidates the old RAM for potential
                // GC cleanup.
  }
  std.stdio.writefln("%s", a); // outputs "aBcd";

In the above example had just been "void foo(in char[] x)" then the string
would have been effected because "foo" would have been playing around with
a copy of the reference and not the reference itself.

** 'out' **
identical to inout except that the parameter being passed is initialized to
the default value(s) prior to the function getting control. This is only
used by functions that return data without considering the data's previous
value. 

   int a = 4;
   void foo(out int x)
   {
        // Because the parameter is 'out', the value passed will 
        // always be zero because that is the initial value for an
        // int datatype. 
        if (x > 1)
          x = 10;  // Never executed because 'out' always inits the parm.
        else
          x = 1;
   }
   foo(a);   // Disregard the current value in 'a' as foo
             // will either initialize it or set it to something.
   std.stdio.writefln("%s", a); // outputs 1;


A special note with respect to reference data types (variable-length arrays
and objects): The D programming language does not yet have a parameter
passing mechanism that ensures that referenced data cannot be changed by
the called function. This is because D either passes a copy of the
reference or the address of the reference.  In either case, the called
function has access to the data being referenced. This means that
regardless of whether or not you use use in/inout/out on a variable-length
array or object parameter, the called function can still modified the
array/object's data. The calling function cannot prevent that or otherwise
guarrentee that the called function will behave itself. If you positively
must ensure that passed data is preserved, only pass a copy.

    char[] a; // Some string data.
    a = bar(); // fill the string with something.
    foo( a.dup );  // Pass a copy to prevent foo from messing with it.

-- 
Derek Parnell
Melbourne, Australia
"Justice for David Hicks!"
skype: derek.j.parnell


More information about the Digitalmars-d-learn mailing list