# Swift Early Exits: guard, break, continue, and return

Source: https://devdocs10.com/articles/swift/swift-early-exits  
By Murat Bo · Published 2026-05-12 · DevDocs10 (https://devdocs10.com)

Swift gives you several ways to leave the current path of execution early. These early exit tools help you stop a function, skip a loop iteration, leave a loop or switch, or reject invalid conditions before the main logic runs. Learning `guard`, `break`, `continue`, and `return` makes your code easier to read, safer, and less deeply nested.

> **Quick answer:** In Swift, `guard` exits the current scope when a required condition fails, `break` leaves a loop or `switch`, `continue` skips to the next loop iteration, and `return` exits a function or method, optionally sending back a value.

> **Difficulty:** Beginner
>
> **You'll understand this better if you know:** basic Swift syntax, how `if` statements and loops work, and simple types like `String`, `Int`, and `Bool`.

## 1. What Is Swift Early Exits?

Early exits are control flow statements that stop the current path before reaching the bottom of a block. Instead of wrapping everything inside nested conditions, you can leave early when a condition is not valid or when there is nothing more to do.

- `guard` checks a condition that must be true to continue.
- `break` stops a loop or exits a `switch` case.
- `continue` skips the rest of the current loop iteration.
- `return` exits a function, method, or computed property.
- These statements reduce nesting and make intent clearer.

These ideas are closely related, but they are not interchangeable. A common beginner confusion is thinking that `guard` and `if` do the same thing, or that `break` and `continue` both simply “skip” loop work. In practice, each one solves a different control flow problem.

## 2. Why Early Exits Matter

Early exits matter because real code often has invalid input, edge cases, and stop conditions. If you do not handle those cases early, code becomes deeply nested and harder to follow.

For example, a function that validates user input should stop immediately when the input is empty. A loop that processes only positive numbers should skip negative values without doing extra work. A search loop should stop as soon as the target is found.

Use early exits when:

- A condition must be true before the main logic can continue.
- You want to stop processing once a result is found.
- You want to ignore certain loop values and move on quickly.
- You want to avoid several levels of nested `if` statements.

Do not use them carelessly just to jump around code. The goal is clarity, not surprise.

## 3. Basic Syntax

### Using guard

A `guard` statement checks a condition that must be true. If the condition is false, the `else` block must exit the current scope using something like `return`, `break`, `continue`, or `fatalError`.

```swift
func printUsername(name: String?) {
    guard let name = name else {
        print("No username provided")
        return
    }

    print("Welcome, \(name)!")
}
```

Here, the function exits immediately if `name` is `nil`. If the value exists, the unwrapped `name` is available after the `guard`.

### Using break

`break` stops the current loop or exits a `switch` case early.

```swift
for number in 1...10 {
    if number == 5 {
        break
    }

    print(number)
}
```

Output (tested with Swift 6.4):

```text
1
2
3
4
```

This loop prints 1 through 4, then stops completely when it reaches 5.

### Using continue

`continue` skips the rest of the current loop iteration and moves to the next one.

```swift
for number in 1...5 {
    if number == 3 {
        continue
    }

    print(number)
}
```

Output (tested with Swift 6.4):

```text
1
2
4
5
```

This prints 1, 2, 4, and 5. Only the iteration where the number is 3 is skipped.

### Using return

`return` exits the current function. If the function has a return type, it must return a value of that type.

```swift
func square(of number: Int) -> Int {
    return number * number
}
```

This function returns the square of the input and then finishes.

## 4. Step-by-Step Examples

### Example 1: Validating input with guard

This example checks whether a password is empty before continuing.

```swift
func savePassword(_ password: String) {
    guard !password.isEmpty else {
        print("Password cannot be empty.")
        return
    }

    print("Password saved.")
}
```

The function stops immediately for invalid input, so the main logic stays simple.

### Example 2: Leaving a loop with break

This example searches an array and stops as soon as the target value is found.

```swift
let scores = [12, 18, 24, 31, 45]
let target = 31

for score in scores {
    if score == target {
        print("Found the target score.")
        break
    }

    print("Checked \(score)")
}
```

Output (tested with Swift 6.4):

```text
Checked 12
Checked 18
Checked 24
Found the target score.
```

Once 31 is found, the loop ends. That avoids unnecessary work.

### Example 3: Skipping unwanted values with continue

This loop prints only even numbers.

```swift
for number in 1...8 {
    if number % 2 != 0 {
        continue
    }

    print("Even: \(number)")
}
```

Output (tested with Swift 6.4):

```text
Even: 2
Even: 4
Even: 6
Even: 8
```

Odd numbers are skipped, so only even numbers reach the print statement.

### Example 4: Returning early from a function

This function checks for division by zero before performing the calculation.

```swift
func divide(_ left: Double, by right: Double) -> Double? {
    guard right != 0 else {
        return nil
    }

    return left / right
}
```

This is a common pattern: reject an invalid case early, then return the real result.

## 5. Practical Use Cases

- Checking whether a function argument is valid before doing expensive work.
- Unwrapping optional values with `guard let` before using them.
- Stopping a search loop once a match is found.
- Skipping invalid records while processing a list of input values.
- Leaving a `switch` case when no action is needed.
- Returning early from functions when permissions, limits, or preconditions fail.

## 6. Common Mistakes

### Mistake 1: Using guard without exiting the scope

A `guard` statement requires the `else` block to leave the current scope. Beginners sometimes forget that the failure case cannot simply print a message and continue.

**Problem:** This code does not exit inside the `else` block, so Swift reports an error because `guard` must transfer control out of the scope.

```swift
func showAge(_ age: Int?) {
    guard let age = age else {
        print("Age is missing")
    }

    print(age)
}
```

**Fix:** Add a control transfer statement such as `return` so the invalid path leaves the function.

```swift
func showAge(_ age: Int?) {
    guard let age = age else {
        print("Age is missing")
        return
    }

    print(age)
}
```

The corrected version works because the failure case exits the function before execution can continue.

### Mistake 2: Confusing break and continue

`break` and `continue` are both used in loops, so it is easy to mix them up. They do very different things.

**Problem:** This code uses `break` when the goal is only to skip negative values. As a result, the whole loop stops too early.

```swift
let values = [3, 8, -1, 10]

for value in values {
    if value < 0 {
        break
    }

    print(value)
}
```

Output (tested with Swift 6.4):

```text
3
8
```

**Fix:** Use `continue` when you want to ignore one iteration and keep processing the rest.

```swift
let values = [3, 8, -1, 10]

for value in values {
    if value < 0 {
        continue
    }

    print(value)
}
```

Output (tested with Swift 6.4):

```text
3
8
10
```

The corrected version works because the loop skips only the negative value and continues with later items.

### Mistake 3: Forgetting to return a value from a non-void function

If a function declares a return type, all code paths must return a value of that type. This often happens when early exits are added but one path is left incomplete.

**Problem:** This function promises to return an `Int`, but one path exits the `if` without returning anything, which causes a compile-time error.

```swift
func scoreLabel(_ score: Int) -> Int {
    if score < 0 {
        print("Invalid score")
    }

    return score
}
```

**Fix:** Make every path return a value, or change the return type to an optional if failure should return no value.

```swift
func scoreLabel(_ score: Int) -> Int? {
    guard score >= 0 else {
        print("Invalid score")
        return nil
    }

    return score
}
```

The corrected version works because both the failure and success paths return a value that matches the function signature.

### Mistake 4: Using continue outside a loop

`continue` only makes sense inside loops. It cannot be used in a normal function body or inside an `if` statement that is not part of a loop.

**Problem:** This code tries to skip execution with `continue` even though there is no loop, so Swift reports an error.

```swift
func checkName(_ name: String) {
    if name.isEmpty {
        continue
    }

    print(name)
}
```

**Fix:** Use `return` to leave the function, or restructure the logic if you are not inside a loop.

```swift
func checkName(_ name: String) {
    if name.isEmpty {
        return
    }

    print(name)
}
```

The corrected version works because `return` is the proper way to exit a function early.

## 7. Best Practices

### Use guard for required preconditions

When a condition must be true before the rest of the code makes sense, `guard` is usually clearer than wrapping the main logic inside an `if`.

Less-preferred deeply nested code:

```swift
func sendEmail(to address: String?) {
    if let address = address {
        print("Sending email to \(address)")
    }
}
```

Preferred early-exit version:

```swift
func sendEmail(to address: String?) {
    guard let address = address else {
        return
    }

    print("Sending email to \(address)")
}
```

This style keeps the success path less indented and easier to scan.

### Use break only when you are truly done

If the loop should stop completely, `break` is correct. If you only want to ignore one value, use `continue` instead.

```swift
let letters = ["a", "b", "stop", "c"]

for letter in letters {
    if letter == "stop" {
        break
    }

    print(letter)
}
```

Output (tested with Swift 6.4):

```text
a
b
```

This clearly communicates that `stop` is a terminating value, not just a skipped one.

### Prefer early return to reduce nesting

Many functions are easier to read when invalid or special cases return immediately and the main logic is left at the top level.

```swift
func calculateDiscount(for price: Double) -> Double {
    guard price > 0 else {
        return 0
    }

    return price * 0.1
}
```

This avoids an unnecessary `else` block around the main calculation.

## 8. Limitations and Edge Cases

- `guard` must exit the current scope in its `else` block. You cannot leave the block without transferring control.
- `break` works in loops and `switch` statements, but it does not exit a function.
- `continue` works only inside loops. It cannot be used inside a plain `if` block outside looping code.
- `return` exits the current function or method, not just a loop inside that function.
- In nested loops, plain `break` exits only the innermost loop. If you need to leave an outer loop, labeled statements may be necessary.
- Code after an unconditional `return`, `break`, or `continue` in the same path is unreachable and may trigger warnings or show logical mistakes.

## 9. guard vs if, and break vs continue

### guard vs if

Both `guard` and `if` check conditions, but they emphasize different reading styles.

| Statement | Best for | What happens when condition fails |
| --- | --- | --- |
| `guard` | Required conditions and early exits | The current scope must exit |
| `if` | Branching between possible paths | Execution simply skips the block |

Use `guard` when the rest of the code should not run unless a condition is true. Use `if` when you are choosing between actions.

```swift
func printMessage(_ text: String?) {
    guard let text = text else {
        return
    }

    print(text)
}
```

This reads as “the value must exist, otherwise stop.”

### break vs continue

These two are commonly confused in loops.

| Statement | Effect |
| --- | --- |
| `break` | Ends the entire loop immediately |
| `continue` | Skips the current iteration and starts the next one |

If you are done looping, use `break`. If you are only done with one item, use `continue`.

## 10. Practical Mini Project

This mini project processes a list of raw score strings. It uses `guard` to validate values, `continue` to skip bad entries, `break` to stop at a special marker, and `return` to return the final average.

```swift
func averageScore(from items: [String]) -> Double {
    var total = 0
    var count = 0

    for item in items {
        if item == "STOP" {
            break
        }

        guard let score = Int(item) else {
            print("Skipping invalid value: \(item)")
            continue
        }

        guard score >= 0 && score <= 100 else {
            print("Skipping out-of-range score: \(score)")
            continue
        }

        total += score
        count += 1
    }

    guard count > 0 else {
        return 0
    }

    return Double(total) / Double(count)
}

let rawScores = ["80", "95", "-3", "oops", "70", "STOP", "100"]
let result = averageScore(from: rawScores)
print("Average: \(result)")
```

Output (tested with Swift 6.4):

```text
Skipping out-of-range score: -3
Skipping invalid value: oops
Average: 81.66666666666667
```

This example shows all four early exit tools working together in a realistic data-processing task. Invalid values are skipped, the special stop marker ends the loop, and the function returns a final result.

## 11. Key Points

- `guard` is for conditions that must be true to continue.
- `break` ends a loop or exits a `switch`.
- `continue` skips only the current loop iteration.
- `return` exits a function or method, optionally with a value.
- Early exits reduce nesting and often make code easier to read.
- `guard` is especially useful for optional unwrapping and input validation.
- Choose `break` versus `continue` based on whether the whole loop should stop or only one item should be skipped.

## 12. Practice Exercise

Try writing a function that processes a list of numbers and prints only the positive even values.

- Use `continue` to skip odd numbers.
- Use `continue` to skip negative numbers.
- Use `break` if the number `0` appears.
- Use `return` to exit the function early if the array is empty.

**Expected output:** For input `[4, -2, 7, 10, 0, 12]`, the function should print 4 and 10, then stop when it reaches 0.

**Hint:** Check the empty array first, then loop through the numbers in order and decide whether to skip, stop, or print.

```swift
func printPositiveEvenNumbers(from numbers: [Int]) {
    guard !numbers.isEmpty else {
        print("No numbers to process.")
        return
    }

    for number in numbers {
        if number == 0 {
            break
        }

        if number < 0 {
            continue
        }

        if number % 2 != 0 {
            continue
        }

        print(number)
    }
}
```

## 13. Final Summary

Swift early exits are simple tools, but they have a big effect on code quality. `guard` lets you reject invalid conditions early, `break` ends loops and `switch` cases, `continue` skips unneeded loop iterations, and `return` exits functions cleanly. Used well, they make your code flatter, clearer, and easier to maintain.

The most important idea is to choose the right exit for the job. Use `guard` when a condition must hold, `break` when you are done looping, `continue` when one item should be skipped, and `return` when the function should end. A great next step is learning Swift conditionals and optionals more deeply, especially `if let`, `guard let`, and labeled statements in nested loops.
