HomeSearch

Swift Guard Example: Guard Else, Return

Use the guard statement to validate arguments to a method. Guard statements must break or return.
Guard. This is a special form of an if-statement. A guard ensures a condition is true. If it is not true, the guard exits (with return or break) the current block.
A guard validates. A method may have invalid results if a parameter is not in the correct range. It is unsafe to use in this case. A guard can prevent logic errors later in the program.
First example. This example introduces a printArea func. In printArea we require both parameters to be greater than or equal to 1. Zero and negative values are invalid.

Guard: We use a guard statement with "x" and one with "y." A return prevents invalid arguments from being used.

Note: Logically an area cannot be negative. And this program specially disallows empty areas.

Result: The first call to printArea succeeds, as both arguments are valid. But the next two fail because of the guard clauses.

Swift program that uses guard clause func printArea(x: Int, y: Int) { // Validate that "x" argument is valid. guard x >= 1 else { print("X invalid") return } // Validate "y" argument. guard y >= 1 else { print("Y invalid") return } // Print area. let area = x * y print(area) } // Call printArea. printArea(x: 5, y: 10) // Use invalid "X" then invalid "Y" arguments. printArea(x: 0, y: 1) printArea(x: 2, y: 0) Output 50 X invalid Y invalid
Must exit scope. A guard is more restricted than an if-statement. It is a special case of an if-statement. A guard must exit (with return, break) at the end of its list of statements.

Tip: Before the "return," however, a guard can use any other logic like a print call. It can have multiple interior statements.

Swift program that shows invalid guard func test(size: Int) { guard size >= 10 else { print(1) } print(2) } Output /.../main.swift:4:5: 'guard' body may not fall through, consider using 'return' or 'break' to exit the scope
Else error. The guard condition must have an else-keyword. We can think of a guard as an if-else with an empty "if" and a requirement that the control flow terminates in the "else."

Here: The "else" keyword was omitted. The Swift compiler issues a compile-time error.

Swift program that causes guard compile-time error func multiplySize(size: Int) -> Int { guard size >= 0 { return 0 } return size * 2 } Output /.../main.swift:2:21: Expected 'else' after 'guard' condition
Guard let. Consider this program. We introduce printSum, and this function has a guard statement. We bind the name "initial" to the result of the first property, which returns an optional.

And: If the first elements in the Int array exits, we continue with printSum. Otherwise we return early.

Swift program that uses guard let, optional binding func printSum(values: [Int]) { // Use optional binding in guard. // ... Return if no first element. guard let initial = values.first else { print("No initial element") return } // Print first element. print("First: \(initial)") // Sum elements. var sum = 0 for element in values { sum += element } // Print the sum. print("Sum: \(sum)") } // Use printSum func. printSum(values: []) printSum(values: [10, 11]) Output No initial element First: 10 Sum: 21
Loop continue, break. A guard can exit with a continue or break statement. So we can use guard conditions in loops (just like if-else statements).

Continue: In this loop we reach a continue statement unless the loop variable is even. So we skip odd numbers.

Odd, Even

Break: We reach a break statement if the "i" variable is greater than 10. This terminates the loop with a guard condition.

Swift program that uses guard, continue, break in loop // Loop from 0 to infinity. var i = 0; while (true) { // Ensure we are on an even number. guard i % 2 == 0 else { i += 1 continue } // Ensure our number is less than or equal to 10. guard i <= 10 else { break } // Print our number. print("Number: \(i)") i += 1 } Output Number: 0 Number: 2 Number: 4 Number: 6 Number: 8 Number: 10
FatalError. A guard must exit its containing scope. With fatalError we terminate the entire program. This counts as a valid exit condition.

Sometimes: A program must be called with a certain starting value. With guard and fatalError we can ensure this.

Swift program that uses guard, fatalError var i = Int.max // This program must never be run with Int.max. guard i != Int.max else { // This is an exit condition like "return." fatalError("The Int.max cannot be used") } print("End") Output fatal error: The Int.max cannot be used: file /.../main.swift, line 6 Program ended with exit code: 9
Throw. A guard block must exit somehow. This can be done with a throw-statement, which transfers control to the calling function. It enters an alternate control flow.

Here: The start() func throws an ErrorCode.CodeZero unless its argument is 0. So we can only start() with a zero argument.

Swift program that uses guard with throw enum ErrorCode: Error { case CodeZero } func start(code: Int) throws { // This func must be called with argument 0 or it throws. // Use guard with "throw." guard code == 0 else { throw ErrorCode.CodeZero } // Begin. print("Start") } // Call start. try start(code: 900) Output fatal error: Error raised at top level: Test.ErrorCode.CodeZero... (lldb)
Research. Why do we have guards in Swift? This is a construct that is meant to increase code readability. It makes code easier to validate and understand.

Quote: Using a guard statement for requirements improves the readability of your code, compared to doing the same check with an if-statement.

Control Flow: swift.org
A summary. With a guard, we encode a branch that validates arguments or variables. It must return or break. So we use guard to terminate on invalid state before further problems appear.
Home
Dot Net Perls
© 2007-2020 Sam Allen. Every person is special and unique. Send bug reports to info@dotnetperls.com.