Why Isn't Guard Let Foo = Foo Valid

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.

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.

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"
}

Swift: Can't unwrap an optional inside a loop

As explained in Why isn't guard let foo = foo valid?,

let name: String? = "Toto"
guard let name = name else { fatalError() }

is not valid because guard does not introduce a new scope, and you cannot have two variables with the same name in the same scope.

That this compiles on the file level (i.e. in “main.swift”) is a bug. Apparently the variable bound via guard hides the other variable of the same name, regardless of the type and the order in which they are declared:

let aName = "Toto"
guard let aName = Int("123") else { fatalError() }
print(aName) // 123

guard let bName = Int("123") else { fatalError() }
let bName = "Toto"
print(bName) // 123

This bug has been reported as SR-1804 No compiler error for redeclaration of variable bound by guard.

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

Is there a semantic equivalent of Swift guard let statement in C#?

Not as of right now. There is a proposal open (since February 2017) on the C# GitHub repo here.

C# way of doing would be something along the lines of

var optional = (int?) 5;

...

int nonOptional;
if (optional.HasValue)
nonOptional = optional.Value;
else
return;

As you can see, it's quite wordy, but that's because we are dealing with a Nullable value type not a reference type. For reference types it becomes much easier and conciser:

var optional = (string) null;

...

if (optional == null)
return;

// We known from now on that 'optional' is not null

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.

Not using unwrapping in guard statements

guard let unwrapped = optional is an Optional Binding (no link available directly to the correct book section, unfortunately). It safely tries to unwrap the value in the optional. If there is a value, the unwrap succeeds, and the value is assigned to the given name.

You should heavily favor the use of optional bindings, with either guard or if (the difference is the scope of the "unwrapped" name) over the use of forced unwrapping with !. A failed force unwrap is a fatal error; your program will simply crash.

Unwrapping optionals to a pre-defined variable in the guard condition without creating a new constant

I would just test for nil and then force unwrap when I know it's not:

var someString: String? = "hello"
let nonOptionalString: String // note, you don't have to initialize this with some bogus value

guard someString != nil else { return }
nonOptionalString = someString!

Or if someString was a parameter to some method or closure, you can unwrap in the guard statement using the same variable name, simplifying life even more:

func foo(someString: String?) {
guard let someString = someString else { return }

// now I can just use local `someString`, which is not optional anymore
}

If you're desperate to unwrap and exit-if-nil in a single statement, you could theoretically write a function to unwrap if it can or throw an error if it can't:

extension Optional {
enum OptionalError: Error {
case unwrapFailed
}

func unwrap<T>() throws -> T {
if self == nil { throw OptionalError.unwrapFailed }
return self as! T
}
}

Then you can do:

do {
firstNonOptional = try firstOptional.unwrap()
secondNonOptional = try secondOptional.unwrap()
thirdNonOptional = try thirdOptional.unwrap()
} catch {
return
}

I think that's horrible overkill, but if you're desperate to distill it down to one line per unwrap, that's one way to do it.

Which way is recommended to use guard let?

This is completely unrelated to Swift.

From a UI / UX perspective certainly the 2nd option since you can now point to the exact input field that is missing.



Related Topics



Leave a reply



Submit