If the Swift 'guard' statement must exit scope, what is the definition of scope?
It is totally possible to do what you envision, it just happens to not be what that particular code does. return
always exits a method, not the local scope. To do what you wish, you can use a label, and break
:
func testGuardControlFlow () {
let x = 2
let y = 2
func embededFunc () {
breakLabel:
if y == 2 {
guard x == 1 else {
print("oops, number is not 1")
break breakLabel
}
print ("from in embededFunc")
}
print ("I still want this to print even if x != 1")
}
embededFunc()
print("Great, return still allows this to be printed.")
}
testGuardControlFlow()
To add on to vadian's answer:
guard
forces you to exit the scope using a control transfer statement. There are 4 available to you:
return
andthrow
both exit the function/methodcontinue
can be used within loops (while
/for
/repeat-while
)break
can be used in loops (while
/for
/repeat-while
) to exit the immediate scope. Specifying a label to break to will allow you to exit multiple scopes at once (e.g. breaking out of nested loop structure). When using a label,break
can also be used inif
scopes.
Additionally, you may exit the scope by calling a function that returns Never
, such as fatalError
.
Swift: guard let vs if let
if let
and guard let
serve similar, but distinct purposes.
The "else" case of guard
must exit the current scope. Generally that means it must call return
or abort the program. guard
is used to provide early return without requiring nesting of the rest of the function.
if let
nests its scope, and does not require anything special of it. It can return
or not.
In general, if the if-let
block was going to be the rest of the function, or its else
clause would have a return
or abort in it, then you should be using guard
instead. This often means (at least in my experience), when in doubt, guard
is usually the better answer. But there are plenty of situations where if let
still is appropriate.
How to exit GUARD outside and inside a function - Swift
If the condition in your guard
statement is not met, the else branch has to exit the current scope. return
can only be used inside functions, as the error message shows, but return
is not the only way to exit scope.
You can use throw
outside functions as well and if the guard
statement is in a loop, you can also use break
or continue
.
return
valid in functions:
func testGuard(){
guard 2+2 == 4 else {
print("The universe makes no sense")
return // this is mandatory!
}
print("We can continue with our daily lives")
}
throw
valid outside of function as well:
guard 2+2 == 4 else { throw NSError() }
break
valid in loops:
for i in 1..<5 {
guard i < 5 else {
break
}
}
Swift: Benefit of using a guard-statement?
I don't think that is a very good example of using guard, it is more common to use it with variables that might be nil (aka optional) or functions that might return nil. I suggest you read about the guard statement in the Swift Programming Language book (just scroll down a bit to "Early Exit")
We could make a better example from your code that is lacking some validation
func getEmail(email: String?) -> String? {
guard let input = email, !input.isEmpty else {
return nil
}
return input + "@somewhere.com"
}
Here we use guard to check that the parameter email
is not nil by assigning it to a local variable input
. If it is nil the function will return nil and otherwise it will check if it is empty and then it will also return.
If it is ok the function will continue and create and return an email address. Note that the function is declared to return an optional string since I think it is much clearer if a function like this returns nil rather than an empty string if it fails.
What is the different between guard and invert if
Yes, both lines are functionally equivalent and produce the same result.
guard
is considered better practice since the program must exit the scope in its else
cause.
This generates an error:
guard optString != nil else { // no return statement }
But this does not:
if optString == nil { // no return statement }
Swift error: guard body must not fall through
The guard
statement needs to have a something to take the flow of the program away from the enclosing scope (e.g. most likely case is return
to return from the function). This is required as the condition that guard is guarding will not be valid, so the program flow needs to go elsewhere!
Documentation:
The else clause of a guard statement is required, and must either call
a function marked with the noreturn attribute or transfer program
control outside the guard statement’s enclosing scope using one of the
following statements:
- return
- break
- continue
- throw
Guard does not exit function
You are guarding AGAINST the case where name == "Hans"
Change your reverse to be inside the guard, and the print should replace the reverse
What is basic difference between guard statement and if...else statement?
Using guard might not seem much different to using if, but with guard your intention is clearer: execution should not continue if your conditions are not met. Plus it has the advantage of being shorter and more readable, so guard is a real improvement, and I'm sure it will be adopted quickly.
There is one bonus to using guard that might make it even more useful to you: if you use it to unwrap any optionals, those unwrapped values stay around for you to use in the rest of your code block. For example:
guard let unwrappedName = userName else {
return
}
print("Your username is \(unwrappedName)")
This is in comparison to a straight if statement, where the unwrapped value would be available only inside the if block, like this:
if let unwrappedName = userName {
print("Your username is \(unwrappedName)")
} else {
return
}
// this won't work – unwrappedName doesn't exist here!
print("Your username is \(unwrappedName)")
https://www.hackingwithswift.com/swift2
Swift's guard keyword
Reading this article I noticed great benefits using Guard
Here you can compare the use of guard with an example:
This is the part without guard:
func fooBinding(x: Int?) {
if let x = x where x > 0 {
// Do stuff with x
x.description
}
// Value requirements not met, do something
}
Here you’re putting your desired code within all the conditions
You might not immediately see a problem with this, but you could imagine how confusing it could become if it was nested with numerous conditions that all needed to be met before running your statements
The way to clean this up is to do each of your checks first, and exit if any aren’t met. This allows easy understanding of what conditions will make this function exit.
But now we can use guard and we can see that is possible to resolve some issues:
func fooGuard(x: Int?) {
guard let x = x where x > 0 else {
// Value requirements not met, do something
return
}
// Do stuff with x
x.description
}
- Checking for the condition you do want, not the one you don’t. This again is similar to an assert. If the condition is not met,
guard‘s else statement is run, which breaks out of the function.- If the condition passes, the optional variable here is automatically unwrapped for you within the scope that the guard
statement was called – in this case, the fooGuard(_:) function.- You are checking for bad cases early, making your function more readable and easier to maintain
This same pattern holds true for non-optional values as well:
func fooNonOptionalGood(x: Int) {
guard x > 0 else {
// Value requirements not met, do something
return
}
// Do stuff with x
}
func fooNonOptionalBad(x: Int) {
if x <= 0 {
// Value requirements not met, do something
return
}
// Do stuff with x
}
If you still have any questions you can read the entire article: Swift guard statement.
Wrapping Up
And finally, reading and testing I found that if you use guard to unwrap any optionals,
those unwrapped values stay around for you to use in the rest of your
code block
.
guard let unwrappedName = userName else {
return
}
print("Your username is \(unwrappedName)")
Here the unwrapped value would be available only inside the if block
if let unwrappedName = userName {
print("Your username is \(unwrappedName)")
} else {
return
}
// this won't work – unwrappedName doesn't exist here!
print("Your username is \(unwrappedName)")
Related Topics
Retrieve Multiple Photos Under a Node from Firebase Storage
How to Read a Property List from Data in Swift 3
Appending Tuples to an Array of Tuples
Swift Mutable Set: Duplicate Element Found
How to Use a Variadic Closure in Swift
Toggle Selection in a List - Swiftui
Codable Class Does Not Conform to Protocol Decodable
Slide Sidebar Menu iOS 8 Swift
Nsimage Getting Resized When I Draw Text on It
Iphonex Not Call Prefersstatusbarhidden
How to Stream Remote Audio in iOS 13? (Swiftui)
Swift Extension on Generic Struct Based on Properties of Type T
Binding in a Foreach in Swiftui
Uibarbuttonitem Doesn't Work When Created as a Property, But Does When Created in a Function