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.
When would you use the last example?
That’s good to know, thx.
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
@Biot – what is your Delphi version? I am currently using Delphi XE, and I have different result – outTest(x) outputs 123.