swiftinitializationtoplevelforward-reference

Why does Swift allow variable usage before declaration at the global scope but not within functions?


I'm encountering a strange behavior in Swift regarding the order of variable declarations. At the global scope, Swift seems to allow the use of a variable before its declaration, but within a function, it enforces stricter rules.

let a = b + 4
let b = 5

// prints 4
print(a)

In the global scope, a is initialized with b + 4 before b is declared, yet it prints 4. I expected it to either throw an error or return 9 after both variables are initialized.

Inside a function, Swift correctly enforces the order of variable declarations, resulting in an error: "Use of local variable 'y' before its declaration."

// error: Use of local variable 'y' before its declaration
func foo() -> Int {
    let x = y + 4
    let y = 5
    
    return x
}

Why does Swift allow this seemingly inconsistent behavior at the global scope, where it uses the variable before its declaration, instead of either throwing an error or correctly computing the value?

Any explanation into this would be appreciated!


Solution

  • Variables declared at the top level are global variables, so they can accessed from everywhere, including before they are declared textually.

    This logic is fine for proper Swift/Xcode projects, but it falls apart when you can also write statements at the top level, like in a Swift script/Swift playground. This allows you to do nonsensical things like:

    print(x) // prints 0
    let x = 10
    

    There has been many issues on the Swift Github repo to change global variables in a context that allows top level statements as if they are local variables declared in a function.

    Now to answer the question of "why", in #73300, it is said that this behaviour is a "historical accident".

    In top-level code, Swift behaves differently, defies common user expectations, perform badly, and crashes in inscrutable ways. This leads to confusion and misunderstanding among Swift developers and bad publicity among potential Swift adopters. The differences in Swift top-level code are subtle but important. As new language and performance features are added, the differences increase and become more problematic.

    This all comes down to the historical "accident" of pretending that top-level variables are globals, and initializing them in a way that can never be sound.