When Did 'Guard Let Foo = Foo' Become Legal

When did `guard let foo = foo` become legal?

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.

Why isn't guard let foo = foo valid?

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.

guard let found nil

selectedRooms.text cannot return nil.

A UITextField and UITextView always returns a String value. An empty String ("") is returned if there is no text in the UITextField and UITextView. That's the reason else part is not executing and rooms value is nil.

Now, in the below statement you're force-unwrapping(!) the rooms.

x = Int(ceil(sqrt(Double(rooms!))))

But, since the rooms is nil, so forcefully unwrapping it is throwing runtime exception.

Solution:

You need to add an empty check as well for the else part to take effect, i.e.

guard let numberOfRooms = selectedRooms.text, !numberOfRooms.isEmpty else { //here...
return selectedRooms.placeholder = "type something"
}

About guard let syntax

Try this in playground:

guard let f = e else { fatalError("s is nil") }

Hope it helps!

Are multiple lets in a guard statement the same as a single let?

These are different in Swift 3. In this case:

guard let foo = bar, let qux = taco else { 

you're saying "optional-unwrap bar into foo. If successful, optional unwrap taco into qux. If successful continue. Else ..."

On the other hand this:

guard let foo = bar, qux = taco else {

says "optional-unwrap bar into foo. As a Boolean, evaluate the assignement statement qux = taco" Since assignment statements do not return Booleans in Swift, this is a syntax error.

This change allows much more flexible guard statements, since you can intermix optional unwrapping and Booleans throughout the chain. In Swift 2.2 you had to unwrap everything and then do all Boolean checks at the end in the where clause (which sometimes made it impossible to express the condition).

Although use of guard let , not unwrapped error

You have to optional downcast all dictionaries to the proper type

guard let json = try NSJSONSerialization.JSONObjectWithData(tickerData, options: []) as? [String:AnyObject] else { return nil }
guard let tableauUn = json["result"] as? [String:AnyObject] else {return nil }
guard let tableauDeux = tableauUn["XXBTZEUR"] as? [String:AnyObject] else { return nil}

and the final value

let prix = tableauDeux["o"] as? String

How to write this condition using guard let

This is the equivalent guard statement:

guard (self.fromLocation != nil && self.toLocation != nil) || !self.key.isEmpty else {
return
}

Explanation:

I arrived at this by taking the statement of when we want to return:

(self.fromLocation == nil || self.toLocation == nil) && self.key.isEmpty

and negating it to get when we want to stay:

!((self.fromLocation == nil || self.toLocation == nil) && self.key.isEmpty)

Applying De Morgan's law !(A && B) <==> !A || !B:

!(self.fromLocation == nil || self.toLocation == nil) || !self.key.isEmpty

and then applying De Morgan's law !(A || B) <==> !A && !B:

(!(self.fromLocation == nil) && !(self.toLocation) == nil) || !self.key.isEmpty

and then noting that !(A == nil) <==> A != nil gives us the final result:

(self.fromLocation != nil && self.toLocation != nil) || !self.key.isEmpty

Note:

You said you wanted to do this with guard let, but that doesn't make sense since you stated that you are willing to continue if one or both variables are nil as long as key is not empty, so it isn't possible to guarantee that the unwrapped variables will exist after the guard statement.



Related Topics



Leave a reply



Submit