Combining const and out parameters

This is a continuation of a previous blog post, Are const parameters dangerous? 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 out parameters, with their own side-effects, create a specific situation that requires additional explanation.

First, let's see how an out parameter works.

An out parameter is a var parameter with a twist. Just like a var parameter passes the original variable as a reference (pointer), so does the out parameter, but 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 of the 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;
5 5

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;
2

In combination with a const parameter, 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 const parameter:

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 out with var, while keeping 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 const-out combination, it turns out that const is not the real source of problems. The real question is:

Are 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.

Comments

Popular posts from this blog

Coming in Delphi 12: Disabled Floating-Point Exceptions

Assigning result to a function from asynchronous code

Beware of loops and tasks