‘var’ vs ‘out’ as a function argument

The Delphi Documentation is known to be a confusing and untrustful source of information. I am not quoting the current documentation content here and hope it will be fixed – right now it is a nonsense.

You can replace ‘var’ by ‘out’ and get binary identical code if only POD types are used.

Still the ‘out’ keyword is not an alias for ‘var’. The difference appears when lifetime managed types (strings, dynamic arrays, interfaces) are involved:

program Test1;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  TIntArray = array of Integer;

var
  S: string;
  II: IInterface;
  A: TIntArray;
  Obj: TObject;

procedure TestString(out AStr: string);
begin
  Assert(AStr = '');
  Writeln('Passed');
end;

procedure TestInterface(out AII: IInterface);
begin
  Assert(AII = nil);
  Writeln('Passed');
end;

procedure TestDynArray(out AArr: TIntArray);
begin
  Assert(AArr = nil);
  Writeln('Passed');
end;

procedure TestObject(out AObj: TObject);
begin
  Assert(AObj = nil);  // Failed
  Writeln('Passed');
end;

begin
  try
    try
      S:= '123';
      TestString(S);
      II:= TInterfacedObject.Create;
      TestInterface(II);
      A:= TIntArray.Create(42);
      TestDynArray(A);
      Obj:= TObject.Create;
      TestObject(Obj);
    except
      on E: Exception do
        Writeln(E.ClassName, ': ', E.Message);
    end;
  finally
    Obj.Free;
    ReadLn;
  end;
end.

The above code shows that when you use ‘out’ instead of ‘var’ in a function declaration and pass a reference counted instance the compiler makes the input value of an instance unavailable in a function. The ‘out’ keyword guaranties that the input value of a reference counted instance will never be changed. That is the only difference between ‘var’ and ‘out’ parameters I could find. The 4th test (procedure TestObject) shows that ‘out’ does not protect input value of an object (non-refcounted instance).
Sometimes use of ‘out’ keyword leads to unexpected results (the next example is taken from Alex Ciobanu blog):

procedure CopyStr(const AStr: String; out AOutStr: String);
begin
  AOutStr := AStr;
end;

var
  SomeStr: String;

begin
  SomeStr := 'Hello World';
  CopyStr(SomeStr, SomeStr);
  WriteLn(SomeStr);
  ReadLn;
end.
Advertisements

5 thoughts on “‘var’ vs ‘out’ as a function argument

  1. I believe there is a misunderstanding. Look at the following example:

    ////////////////////////////////////////////////////////////////////////////////////////////////
    procedure varTest(var x: Integer);
    begin
    Writeln(x);
    end;

    procedure outTest(out x: Integer);
    begin
    Writeln(x);
    end;

    var
    x: Integer;
    begin
    x := 123;
    Writeln(x); // 123
    varTest(x); // 123
    Writeln(x); // 123
    outTest(x); // 0
    Writeln(x); // 0
    end.
    ////////////////////////////////////////////////////////////////////////////////////////////////

    As you see the main differences between var and out are as followings:
    – var is passed as reference and the current value of that parameter is accessible inside the procedure
    – out is passed as reference but (as it says) does not pass the current value of parameter to procedure and it is reset (to all zeros) at the beginning of the procedure.

    So because the out does not use the current value, it does not increment it’s reference count.

    Also in your example it is obvious and not unexpected that the procedure fails to duplicate the string because it empties the out parameter at the start of the procedure.

    Regards

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s