Programming in D – Tutorial and Reference
Ali Çehreli

Other D Resources

Name Scope

Any name is accessible from the point where it has been defined to the point where its scope ends, as well as in all of the scopes that its scope includes. In this regard, every scope defines a name scope.

Names are not available beyond the end of their scope:

void main() {
    int outer;

    if (aCondition) {  // ← curly bracket starts a new scope
        int inner = 1;
        outer = 2;     // ← 'outer' is available here

    } // ← 'inner' is not available beyond this point

    inner = 3;  // ← compilation ERROR
                //   'inner' is not available in the outer scope
}

Because inner is defined within the scope of the if condition it is available only in that scope. On the other hand, outer is available in both the outer scope and the inner scope.

It is not legal to define the same name in an inner scope:

    size_t length = oddNumbers.length;

    if (aCondition) {
        size_t length = primeNumbers.length; // ← compilation ERROR
    }

Scopes need not be associated with statements; they can be defined by a free pair of curly braces inside functions (and most other constructs):

void main() {
    // This scope is not associated with any statement:
    {
        int a;
    }

    // Another one:
    {
        int a;    // This 'a' is different from the previous one
    }
}

However, such unassociated scopes cannot be defined at module scope (at the top level of the source code):

void main() {
    // ...
}

{    // ← compilation ERROR
}
Defining names closest to their first use

As we have been doing in all of the programs so far, variables must be defined before their first use:

    writeln(number);     // ← compilation ERROR
                         //   number is not known yet
    int number = 42;

For that code to be acceptable by the compiler, number must be defined before it is used with writeln. Although there is no restriction on how many lines earlier it should be defined, it is accepted as good programming practice that variables be defined closest to where they are first used.

Let's see this in a program that prints the average of the numbers that it takes from the user. Programmers who are experienced in some other programming languages may be used to defining variables at tops of scopes:

    int count;                                 // ← HERE
    int[] numbers;                             // ← HERE
    double averageValue;                       // ← HERE

    write("How many numbers are there? ");

    readf(" %s", &count);

    if (count >= 1) {
        numbers.length = count;

        // ... assume the calculation is here ...

    } else {
        writeln("ERROR: You must enter at least one number!");
    }

Contrast the code above to the one below that defines the variables later, as each variable actually starts taking part in the program:

    write("How many numbers are there? ");

    int count;                                 // ← HERE
    readf(" %s", &count);

    if (count >= 1) {
        int[] numbers;                         // ← HERE
        numbers.length = count;

        double averageValue;                   // ← HERE

        // ... assume that the calculation is here ...

    } else {
        writeln("ERROR: You must enter at least one number!");
    }

Although defining all of the variables at the top may look better structurally, there are several benefits of defining them as late as possible: