If the Swift 'Guard' Statement Must Exit Scope, What Is the Definition of Scope

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 and throw both exit the function/method
  • continue 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 in if 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
}
  1. 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
}
  1. 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.
  2. 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.
  3. 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



Leave a reply



Submit