All Delphi developers obviously know about the var keyword, otherwise known as “by reference“. Some (although I doubt it) may not know that Delphi has an out keyword also. Both are identical in the fact that both are “by reference“, but there is a difference – var parameters are expected to carry a valid value when entering and exiting a function, and out is only expected to carry a valid value when exiting a function.
Now, to the weird stuff — consider this small example:
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.
Normally you would expect the result to be “Hello World“, as nothing basically would happen. AStr is copied to AOutStr which are references to the same variable: SomeStr. Well, if AOutStr was prefixed with a var keyword instead of out then you would have been right!
Remember that the Delphi compiler has a “contract” it must respect — All reference counted variables are initialized to “nothing” (or nil). So what happens in our case is that AOutStr is actually “a result value” and is not expected to contain a valid value when entering the function. The compiler must initialize it to nil before actually executing any code inside the function. So it clears the AOutStr string (which is also AStr) and that makes AOutStr receive an empty value from now cleared AStr parameter. The result is that SomeStr will be empty after the call to this function.
And now this begs the question: If the compiler is smart enough to figure this case out, why are the string result values considered var parameters and not out? This question is related to my previous post about the way the compiler treats the string and dynamic array result values. I guess this is because the out parameter type was introduced later, and so the compiler team was wary of breaking the way the things worked. Many pieces of software may actully depend in the fact that Result is treated like var and not being cleared out before entering the function.