Question or problem in the Swift programming language:
Back in November of 2016 I posted a question asking why I couldn’t use guard to create an unwrapped version of a variable using the same name as the optional, like you can with if let:
Link: Why isn’t guard let foo = foo valid?
When I wrote that question, the code below would fail to compile with an error that “Definition conflicts with previous value”:
//Test of using guard to create an unwrapped version of a var, like if let func guardTest(_ viewController: UIViewController?) -> UIViewController? { // Check if the current viewController exists print(String(describing: viewController)) guard let viewController = viewController else { return nil } print(String(describing: viewController)) return viewController }
However, I just found some code at work that does this, and it now compiles without complaint and does what I want it to do! When run, the print statements show that foo is an optional before the guard, and an unwrapped optional after:
viewController = Optional() viewController =
(I added the test function guardTest(_:) to my latest open source project if you want to try it out. It’s available on Github at https://github.com/DuncanMC/TrochoidDemo)
I’m happy that this construct now works as I want it to, but confused as to why it’s now legal, and when the change occurred.
Is anybody aware of a recent change to the language definition that makes this construct work where it didn’t before?
How to solve the problem:
TL;DR
guard let foo = foo
is legal if foo
was defined in another scope.
The example from your linked question:
func test() { let a: Int? = 1 guard let a = a else{ return } print("a = \(a)") }
still doesn’t work because the guard
statement is trying to create another variable a
in the same scope.
This example:
//Test of using guard to create an unwrapped version of a var, like if let func guardTest(_ viewController: UIViewController?) -> UIViewController? { // Check if the current viewController exists print(String(describing: viewController)) guard let viewController = viewController else { return nil } print(String(describing: viewController)) return viewController }
works for the same reason that this does:
func test(a: Int) { print(type(of: a)) // Int let a = 3.14 print(type(of: a)) // Double }
The parameter to the function is defined in a different scope, so Swift allows you to create a local variable with the same name.