In Swift, you can use if let optional binding to unwrap an optional into a constant or variable with the same name:
func test()
{
let a: Int? = 1
if let a = a {
print("a = \(a)")
}
}
For everything inside the if let
statement, the optional a
is unwrapped into a regular int.
Likewise, I can use a guard statement to achieve a similar effect
func test()
{
let a: Int? = 1
guard let requiredA = a else{
return
}
print("a = \(requiredA)")
}
However, I can't use code like this: guard let a = a else
:
func test()
{
let a: Int? = 1
guard let a = a else{
return
}
print("a = \(a)")
}
Why not?
In a guard statement, if the conditional of the guard statement fails, the else clause is executed and you exit the current scope. If the conditional succeeds, a new variable/constant is created from guard statement's closing brace to the end of the current scope.
Why can't I do the same trick of mapping an optional into a variable/constant with the same name for remainder of the current scope?
P.S.: I realize this question isn't a perfect fit for this site. I'm open to suggestions as to where would be a better place for this question.
The reason you can't do this:
func test()
{
let a: Int? = 1
guard let a = a else{
return
}
print("a = \(a)")
}
is because guard
creates the new variable in the same scope, thus you have two variables called a
in the same scope. One is an Int
and the other is an Int?
. That is not allowed.
The error that you get Definition conflicts with previous value is exactly the same as if you had done this:
func test()
{
let a: Int? = 1
let a = a!
}
Compare that with:
func test()
{
let a: Int? = 1
if let a = a {
print("a = \(a)")
}
}
In this case, the new variable a
which is an Int
exists only in the new scope of the if
's then clause, so this works.
From the comments:
But I submit to you that the section of code after the closing brace and to the end of the enclosing scope is actually an inner scope.
I can understand that you would like it to be so, but it isn't. If that were the case, then you could do this, but it too gives an error:
func test()
{
let a: Int? = 1
guard let b = a else{
return
}
print("b = \(b)")
let a = 5 // Definition conflicts with previous value
print("a = \(a)")
}
The beauty of guard
is that it doesn't create new scopes and you avoid creating the pyramid of death that results when you repeatedly use if let
to unwrap optionals (and in the process create new scopes).
See the follow-up question When did guard let foo = foo become legal? for more insight on this topic.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With