Why does guard let x = x exhibit different scope behavior?

I found that guard let behavior is different depending on where it is used - seems like a bug to me.

Playground code shown below.


var x:Int? = 10000

func foo(x: Int?) {
    guard let x = x else {
        // Value requirements not met, do something
        return
    }

    //output: "10000\n"
    print(x)
}

foo(x)

guard let x = x else {
    throw NSError(domain: "d", code: 0, userInfo: nil)
}

//output: "Optional(10000)\n"
print(x)

The "guard" version of x shadows the previous version. In the function "foo", the "guard" x shadows the parameter. There is no problem here: local variables are allowed to shadow parameters.


However... you're not supposed to be able to shadow variables in the same scope. I'm surprised that the top level guard in your code compiles at all.


You'll notice that:


func foo() {
  let y: Int? = 3
  guard let y = y else { return }

  print(y)
}


won't compile. ("Definition conflicts with previous value")


So... I'm surprised that Swift allows you to shadow globals at the file level. I guess there's another rule for main and playgrounds files that lets the duplicate definition through without and error but ultimately leads to the strange behavior that you're seeing.


Swift should probably disallow this construct in the same way that it disallows duplicate definitions in functions.

Thanks Matt. Shadowing aside, it seems like the unwrapped optional x outside the method is scoped somehow? Because print(x) uses the original optional instead.


As an aside, why would Swift allow method parameter to be shadowed - isn't that against the premise of safe coding? (Maybe this warrants another question)

The "wrong" x is printed at the end because a playground isn't a closure (a function body). It's a REPL, which means that each top-level statement is executed in the top-level environment that exists at the moment you enter the statement. The results may also depend on the order in which the statements were originally entered, but there's no unmbiguous sequential flow in the same sense as a normal function. There was a reference to this in one of the What's New videos about Swift at WWDC, though I don't remember which one.

Why does guard let x = x exhibit different scope behavior?
 
 
Q