Introducing Inline Variables in the Delphi Language

Posted by on in Tutorial

The Delphi language in 10.3 has a fairly core change in the way it allows far more flexibility in the declaration of local variables, their scope and lifetime. This is a change that breaks a key tenet of the original Pascal language, but offers a significant number of advantages, reducing unneeded code in several cases.

Old Style Var Blocks 

Since Turbo Pascal 1 and until now, following classic Pascal language rules, all local variable declarations had to be done in a var block written before the beginning of a function, procedure or method:

procedure Test; 
I: Integer;
I := 22;
ShowMessage (I.ToString);

Inline Variable Declarations

The new inline variable declaration syntax allows you to declare the variable directly in a code block (allowing also multiple symbols as usual):

procedure Test; 
var I, J: Integer;
I := 22;
j := I + 20;
ShowMessage (J.ToString);

While this might seem a limited difference, there are several side effects of this change. It is these additional effects that make the feature valuable.

Initializing Inline Variables

A significant change compared to the old model, is that the inline declaration and initialization of a variable can be done in a single statement. This makes things more readable and smoother compared to initializing several variables at the beginning of a function.

procedure Test; // declaration and initialization in a single statement 
var I: Integer := 22;
ShowMessage (I.ToString);

More over, if the value of a variable is available only later in the code block, rather than setting an initial value (like 0 or nil) and later assign the actual value, you can delay the variable declaration to the point you can calculate a good initial value:

procedure Test1; // multiple inline declarations (symbols declared when used) 
var I: Integer := 22;
var J: Integer := 22 + I;
var K: Integer := I + J;
ShowMessage (K.ToString);

In other words, while in the past all local variables where visible in the entire code block, now an inline variable is visible only from the position of its declaration and up to the end of the code block.

Scope and Lifetime of Inline Variables Declared in Nested Blocks

The scope limitation is also more relevant as is doesn't apply to the entire procedure or method, but only to the begin-end code block the inline variable appears. In other words, the scope of an inline variable is limited to the declaration block and the variable cannot be used outside of the block. Not only, but the variable lifetime is also limited to the block. A managed data type (like an interface, string or managed record) will now get disposed at the end of the sub-block, rather than invariably at the end of the procedure or method.

procedure Test2; // scope limited to local block 
var I: Integer := 22;
if I > 10 then
var J: Integer := 3;
ShowMessage (J.ToString);
var K: Integer := 3;
ShowMessage (J.ToString); // COMPILER ERROR: "Undeclared identifier: J"
// J and K not accessible here

As you can see in the last code snippet above, a variable declared inside a begin-end block is visible only in the specific block, and not after the block has terminated. At the end of the if statements, J and K won’t be visible any more.
As I mentioned, the effect is not limited only to visibility. A managed variable, like an interface reference or a record, will be properly cleaned up at the end of the block, rather than at the end of the procedure or method:

procedure Test99; 

// some code

if (something) then
var Intf: IInterface = GetInterface; // Intf.AddRef
var MRec: TManagedRecord = GetMRecValue; // MRec.Create + MRec.Assign
end; // Intf.Release and MRec.Destroy are implicitly called at end of scope

// more code

end; // no additional cleanup

Type Inference for Inline Variables

Another huge benefit of inline variables is that the compiler now can, in several circumstances, infer the type of an inline variable by looking to the type of the expression or value assigned to it:

procedure Test; 
var I := 22;
ShowMessage (I.ToString);

The type of the r-value expression (that is, what comes after the :=) is analyzed to determine the type of the variable. Some of the data types are “expanded” to a larger type, as in the case above where the numeric value 22 (a ShortInt) is expanded to Integer. As a general rule, if the right hand expression type is an integral type and smaller than 32 bits, the variable will be declared as a 32-bit Integer. You can use an explicit type if you want a specific, smaller, numeric type.

Now while this feature can save you a few keystrokes for an Integer or a string, variable type inference becomes fairly nice in case of complex type, like instances of generic types. In the code snippet below, the types inferred are TDictionary for the variable MyDictionary and TPair for the variable APair.

procedure NewTest; 
var MyDictionary := TDictionary <string, Integer>.Create;
MyDictionary.Add ('one', 1);
var APair := MyDictionary.ExtractPair('one');
ShowMessage (APair.Value.ToString)


Inline Constants

Beside variables, you can now also inline a constant value declaration. This can be applied to types constants or untyped constants, in which case the type is inferred (a feature that has been available for constants for a long time). A simple example is below:

const M: Integer = (L + H) div 2; // single identifier, with type specifier 
const M = (L + H) div 2; // single identifier, without type specifier

For Loops With Inline Loop Variable Declaration

Another specific circumstance in which you can take advantage of inline variable declarations is with for loop statements, including the classic for-to loops and modern for-in loops:

for var I: Integer := 1 to 10 do ... 
for var Item: TItemType in Collection do...

You can further simplify the code taking advantage of type inference:

for var I := 1 to 10 do ... 
for var Item in Collection do ...

This is a case in which having the inline variable with limited scope is particularly beneficial, as in the sample code below: Using the I variable outside of the loop will cause a compiler error (while it was only a warning in most cases in the past):

procedure ForTest; 
var total := 0;
for var I: Integer := 1 to 10 do
Inc (Total, I);
ShowMessage (total.ToString);
ShowMessage (I.ToString); // compiler error: Undeclared Identifier ‘I’

Inline Summary

Inline variable declarations, with type inference and local scope, bring fresh air to the Delphi language. While Pascal source code readability remains a key tenet to preserve, it is important to modernize the language at the core level, removing some of the rust (real or perceived). In case of inline variables, there are so many advantages to depart from the traditional coding style, that the value is clear.

Still, you can keep your code as is and ask your fellow developers to stick with the traditional declaration: nothing in the existing code or style is wrong, but inline declarations offer you a new opportunity. I already have trouble going back to older versions of Delphi... just saying.


Gold User, Rank: 7, Points: 457
Delphi and RAD Studio Product Manager at Embarcadero.


  • Markus R.
    Markus R. Sunday, 11 November 2018

    i like that new way because in other programming languages it is also possible.
    new wishes is:
    procedure Test
    var I, J as Integer
    I = 22
    j = I + 20
    ShowMessage J.ToString

  • Adriaan Boshoff
    Adriaan Boshoff Saturday, 3 November 2018

    This seems really promising. I got used to C# for a while and after going back to Delphi. I found it really annoying to declare everything at the top.

  • FreezerUa
    FreezerUa Tuesday, 30 October 2018

    it's usefull. Thanks!

  • Pradyumna
    Pradyumna Monday, 29 October 2018

    When is 10.3 getting released.

  • benhouar yassine
    benhouar yassine Monday, 29 October 2018

    cool thanks marco ,very useful as functionality .

  • Please login first in order for you to submit comments
  • Page :
  • 1

Check out more tips and tricks in this development video: