Combining const and out parameters
This is a continuation of a previous blog post, Are const parameters
Basically, adding an
out parameter into the mix does not change anything that
has been said in the previous post about using
const parameters. But
parameters, with their own side-effects, create a specific situation that requires
First, let's see how an
out parameter works.
out parameter is a
var parameter with a twist. Just like a
passes the original variable as a reference (pointer), so does the
out also tells the compiler that the original value can be discarded, since it is
not for passing any input data to the procedure, and will be only used for
passing the result out of the procedure. For managed types, the compiler will clear the original contents
out parameter at the call site.
procedure OutInt(out p: integer); begin p := 5; end; procedure OutString(out p: string); begin p := '5'; end; procedure Test; var x: integer; s: string; begin x := 2; s := '2'; OutInt(x); Writeln(x); OutString(s); Writeln(s); end;
If we remove all code from the procedures, the string parameter will be cleared, but the integer will contain the previously assigned value:
procedure OutInt(out p: integer); begin end; procedure OutString(out p: string); begin end;
In combination with a
out can have unexpected results:
procedure InOutString(const s: string; out r: string); begin r := s + s; end; procedure Test; var s: string; begin s := Copy('abc', 1, 1); InOutString(s, s); Writeln(s); end;
Now, one would expect that the resulting string would have the value
aa, but instead
you will just get an empty string. That is because the output string variable has been
cleared before the
InOutString procedure is called, and adding an empty string to an empty
string will result in an empty string.
Let's remove the
procedure InOutString(s: string; out r: string); begin r := s + s; end;
If you thought the above code will work, you thought wrong.
const is not the
culprit here -
out is - and replacing the
const, will give the expected output.
procedure InOutString(const s: string; var r: string); begin r := s + s; end;
I am not going to cover other managed types in detail here. You can try it yourself, but don't expect miracles. On the contrary, with interfaces you can fully expect that the whole thing will just blow up right in your face. For instance, using the following procedure and then calling it with the same interface reference for input and output:
procedure InOutFoo(const f: IFoo; out r: IFoo); begin r := TFoo.Create(f.GetNumber * 2); end;
So... in a
out combination, it turns out that
const is not the real
source of problems. The real question is:
out parameters dangerous?
And the answer is... NO...
Just as with
const parameters, the real culprit are not parameter modifiers, but
the bad coding practice of passing the same variable as input and output
parameters. It is just bad code.
It can be easily broke, it obscures the intent of the code, and it is like drinking from and peeing into the same bottle. Don't do that.