60

What's the difference between parameters declared with var and those declared with out? How does the compiler treat them differently (e.g., by generating different code, or by changing which diagnostics it issues)? Or do the different modifiers merely allow the programmer to document intended use of the parameters? What effect do the types of the parameters have on the matter?

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
  • 8
    I have always had the same question, never bothered to ask though. – Jerry Dodge Jan 24 '13 at 17:39
  • Me too. I've actually [run into this problem](http://stackoverflow.com/a/8380755/886783), so I would like a really good answer to this one. – Glenn1234 Jan 24 '13 at 17:48
  • 1
    Documentation makes it pretty clear, IMO: http://docwiki.embarcadero.com/RADStudio/XE3/en/Parameters_%28Delphi%29 – ain Jan 24 '13 at 19:29
  • 3
    @ain It is clear. It's just incorrect. It's only accurate for managed types. – David Heffernan Jan 24 '13 at 19:41
  • maybe asking Embarcadero will make the difference... – RBA Jan 24 '13 at 21:10
  • Even for unmanaged types it acts as a form of documentation, telling other programmers that any input value will be ignored. – Gerry Coll Jan 25 '13 at 03:08
  • @Gerry But the input value isn't ignored. It's passed to the function. The function can use it. – David Heffernan Jan 25 '13 at 07:26
  • @David, the function *can* use the input value of an out parameter, but it shouldn't, because by declaring an out parameter, the function has promised not to use it that way. I've always thought the compiler should warn when a function reads an out parameter before assigning to it, just as it does for local variables. Likewise, I think it should warn when the caller assigns a value to a variable immediately before passing it to a function as an out parameter. – Rob Kennedy Jan 25 '13 at 14:34
  • @Rob I personally like `out` the C# way. It is illegal to read an out parameter before it has been fully initialised. And the function is not allowed to return unless it has fully initialised it. And C# gets function return semantics right as well which is another bug bear of mine. – David Heffernan Jan 25 '13 at 14:55
  • @DavidHeffernan - but if its declared as out, it **shouldn't** (and functions shouldn't have side effects and various other often broken rules :-) ). It's a violation of the implied contract. – Gerry Coll Jan 25 '13 at 23:11
  • @GerryColl Yes, the Delphi compiler lets the programmer break the contract. The C# compiler does not. – David Heffernan Jan 25 '13 at 23:16
  • Related issue: https://stackoverflow.com/questions/74477432/out-parameter-is-not-behaving-according-to-the-documentation – Gabriel Jan 29 '23 at 12:57
  • When was the out parameter added in Delphi? – Gabriel May 05 '23 at 10:27
  • In 1994, @Gabriel, concurrent with its first release. It's inherited from Turbo Pascal. – Rob Kennedy May 06 '23 at 18:31

4 Answers4

52

A var parameter will be passed by reference, and that's it.

An out parameter is also passed by reference, but it's assumed that the input value is irrelevant. For managed types, (strings, Interfaces, etc,) the compiler will enforce this, by clearing the variable before the routine begins, equivalent to writing param := nil. For unmanaged types, the compiler implements out identically to var.

Note that the clearing of a managed parameter is performed at the call-site and so the code generated for the function does not vary with out or var parameters.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
Mason Wheeler
  • 82,511
  • 50
  • 270
  • 477
  • This answer was nice and simple, but now I've read Serg's (deleted) answer, and I'm confused again. He says `out` is important for interface parameters of external functions because other (non-Delphi) code might call it with a "rubbish value." But if the only difference between `var` and `out` is in the way the compiler handles the *calling* side, and the calling side isn't in Delphi in Serg's scenario, then I don't understand what distinction Serg is trying to point out. – Rob Kennedy Jan 24 '13 at 21:03
  • 2
    @Rob I think Serg's a bit confused here. He get's it right on his blog: http://sergworks.wordpress.com/2012/10/01/a-word-about-out-parameters-in-delphi/ – David Heffernan Jan 24 '13 at 21:29
  • The more I think why `out` params were implemented so the more I wonder. There is no need to nil the `out` parameter outside of the procedure in native Delphi code, and I could not find out how the current implementation of `out` parameters can help to make a bridge to other languages that treat interface-type `out` params differently. – kludg Jan 25 '13 at 07:30
  • 2
    @Serg, if other languages *don't* clear the output parameter before assigning to it, and Delphi doesn't clear it *before* the call, then that's a memory leak. Delphi doesn't know what the called function will do, so the only safe choice is to always clear the variable before passing it to the other code. Better to clear the variable twice than not at all. – Rob Kennedy Jan 25 '13 at 14:41
  • Okay, I'm reading all of the answers and comments here and am still not clear on this topic, specifically what I was asking in reference to the link, specifically "why did my definition of SHChangeNotification_Lock require 'out' in the parms to work and wouldn't work with 'var'?" I assume "managed types" have something to do with .NET, but there's a longint type that appears in that definition. So "var" doesn't clear the input value upon passing it to the function while "out" does? – Glenn1234 Jan 25 '13 at 16:12
  • @Glenn1234: "Managed types" has nothing to do with .NET. It refers to data types where the compiler adds special code to deal with memory management, such as strings, dynamic arrays, and Interfaces. – Mason Wheeler Jan 25 '13 at 16:22
  • 1
    @RobKennedy +1 possible memory leak on the Delphi side is a sufficient explanation, thank you. – kludg Jan 25 '13 at 18:51
  • @masonwheeler Okay, so what's the answer to the question? If the difference between "var" and "out" is nothing but a bookkeeping one (what I gather from reading all of this), why did I observe what I observe? There has to be a substantial reason why "var" didn't work in that definition and "out" did. – Glenn1234 Jan 25 '13 at 20:28
  • @Glenn: I have no idea. It looks like you had several points in your answer. If you take your answer, which apparently is working now, and change `out` to `var` but leave everything else the same, does it stop working? – Mason Wheeler Jan 25 '13 at 20:41
  • @Glenn1234 Your definition of `SHChangeNotification_Lock` is wrong. It is as described in the QC report. Changing `out` to `var` for the declaration in that answer changes nothing. You can look at the code generated and see that it is identical. I don't know why you accepted your own answer there, because it is badly wrong, at least regarding `SHChangeNotification_Lock`. – David Heffernan Jan 25 '13 at 22:14
  • What's the point of this functionality in the end? are there any savings memory/cpu-wise by using out? – hikari Jan 24 '17 at 21:47
  • I have seen people doing something like this: procedure GetList(out tsl: TstringList) and then passing an external object to that procedure. I want to make it clear for them that this code will nil the tsl, resulting in a dangling object. Probalby waht they actually intended was something like GetList(tsl: TstringList) or GetList(const tsl: TstringList). So, whenever you put OUT in front of a parameter that is an object, ask yourself "is this what I really want to do"? – Gabriel Aug 26 '22 at 19:55
13

There is not much difference, for the compiler that is. See Mason's answer for that.

Semantically, there is a big difference:

  • var tells the programmer that the routine could work with its current value,
  • out tells the programmer that the routine will ignore/discard its current value.
Community
  • 1
  • 1
NGLN
  • 43,011
  • 8
  • 105
  • 200
  • 8
    @David, this answer is dealing with documentation, not actual behavior. I think most people generally *assume* that `out` in Delphi works the same way as `out` in C#, where it's illegal to read the parameter before it's been written. When people declare `out` parameters in Delphi, I think that's usually how they intend to treat them. Using `out` instead of `var` serves to *document* that intention, even if the compiler doesn't actually enforce it. Reading an `out` parameter before writing to it is a mistake and a bug, even if the compiler doesn't flag it as an error. – Rob Kennedy Jan 25 '13 at 15:07
  • 1
    @Rob If the compiler doesn't check, then the whole edifice collapses. You the programmer have to go and check the function before you call it. I mean, when writing a function, I use `out` to document my intent. But when I come across a function with an out parameter, I have to go and check. – David Heffernan Jan 25 '13 at 15:13
  • @David You don't get it. What I mean is that de declaration, thus the meaning, differs _for the user_ of the routine; it's not about the internals. A parameter being declared as `out` tells a programmer that its no use to prepare that what you are feeding it with. A routine with a `var` parameter needs preparation and/or consideration, an `out` not. Please advise in how to rewrite my answer to address that more clearly. – NGLN Jan 25 '13 at 16:11
  • 2
    @NGLN I totally get it. I fully understand the semantics of out. It's just that since the compiler doesn't enforce it, the function may very well use the value I feed in. In other words the author of the routine can lie to me, and the compiler doesn't complain. Imagine if the compiler didn't bother enforcing type safety. Just because the function said it expects to receive a parameter of one type, the errant programmer could send something else. – David Heffernan Jan 25 '13 at 16:33
  • 1
    The problem IMO is that the documentation concerning how Delphi treats `out` parameters was probably written for Delphi.NET or else, but not for Delphi. Delphi compiler just cannot implement `out` parameters according to the documentation, and it raises questions what `out` parameters really are and when they should be used. – kludg Jan 25 '13 at 19:02
  • 2
    I think that if a programmer uses "out" then he is declaring that he will not read the value and assume it is useful. I personally only check if someone has written a routine properly if I have problems. So I think NGLN's answer is spot on. – Dsm Sep 16 '15 at 15:09
2

Slightly late but just for the record, I came across a case where var or out made a big difference.

I was working on a SOAP web service which exported the following method:

function GetUser( out User :TUser ) :TResult;

which was getting imported into C# as the equivalent of

function GetUser( out Result :TResult) :TUser;

when I changed the out to a var it it imported correctly.

I'm guessing that the Delphi SOAP invoker treats the function result as an out parameter and that having two out parameters confuses the Delphi SOAP routines. I'm not sure if there is a workaround to allow you to use out parameters.

Steve
  • 1,769
  • 2
  • 22
  • 33
1

I read earlier that out parameter is set to default by called function, but today I realized that it is not completely true. Value of out parameter is discarded by called routine, but if that routine does not change its value, caller can still get it initial value, which was assigned before passing to called thread.

For example:

procedure JustNothing(out x : integer);
begin
  // do nothing
end;

procedure TestOutVar;
var i : Integer;
begin
  i := 100;
  JustNothing(i); // after this call, i will still be 100
end;
Shadab Mozaffar
  • 111
  • 1
  • 2
  • 1
    AFAIK, it's only for *managed* types (ie. interfaces and strings, and probably records that contain these, and - according to earlier comment - also CLASS variables) that the caller clears OUT parameters before calling the function. "Integer" is not such a type, so an OUT parameter is the same as a VAR parameter in this case (except that it documents the intent on not to rely on any initial value in the function). – HeartWare Sep 01 '22 at 07:16
  • Did you intend for this to be an _answer_ to my question? It seems like you're just sharing one limited observation, without really attempting to form it into a rule or other explanation for _why_ you've observed what you did. – Rob Kennedy Jan 30 '23 at 16:36