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
Stop a Dispatchqueue That Is Running on the Main Thread
Handling Multiple Gesturerecognizers
Swift Imagepickercontroller Didfinishpickingmediawithinfo Not Fired
How to Use a @Fetchrequest with the New Searchable Modifier in Swiftui
Color Attribute Is Ignored in Nsattributedstring with Nslinkattributename
Implicitly Unwrapped Optional Made Immutable
Is There a Prefix Header (Or Something with This Functionality) in Swift
Sort Array by Calculated Distance in Swift
Making Simple Accordion Tableview in Swift
Swift 2.0: Protocol Extensions: Two Protocols with the Same Function Signature Compile Error
Nsimage to Nsdata as Png Swift
Using Function Parameter Names in Swift
Arkit: Placing an Scntext at a Particular Point in Front of the Camera
Simpliest Solution to Check If File Exists on a Webserver. (Swift)
Variable 'Xxx' Was Never Mutated, Consider Changing to 'Let'
Linker Command Failed with Exit Code 1 After Installing Cocoapods and Firebase Pod