【Swift入門 #16】早期リターンで安全なコード - guard文の使い方

コードの安全性と可読性を高めるguard文について、if文との違いや使い分けを初心者向けに解説します。

📚 Swift入門シリーズ: #14 switch文#15 whileループ


前回はwhileループによる条件に基づいた繰り返し処理を学びました。今回は、コードをより安全で読みやすくするguard文について学びましょう。

著者
著者: Sera
大学院でAI作曲に関して研究中!
来春からデータサイエンティストとして働く予定の技術オタク。
初心者
登場人物: あかり
流行りのAIやWeb技術に興味津々!
『知りたい』気持ちで質問を止められない、好奇心旺盛な学生。
normalの表情
初心者

前回はwhileループで条件に基づく繰り返し処理を学んだね!

専門家

そうだね。今回は、Swiftの特徴的な機能の1つ、guard文 について学んでいくよ。これはコードを安全で読みやすくするための重要な機能なんだ。

confusedの表情
初心者

guard?それって何?if文とは違うの?

専門家

guard文は、「条件が満たされていない場合に早期に処理を抜ける」ための構文なんだ。if文と似ているけど、使い方と目的が違うんだよ。

guard文の基本

専門家

guard文 の基本的な書き方はこうなるよ。

guard 条件 else {
    // 条件がfalseの場合の処理
    // ここで必ず処理を抜ける(return, break, continueなど)
}
// 条件がtrueの場合だけ、ここに到達する

guard文の特徴は、elseブロックで必ず処理を抜けなければならない ということなんだ。

confusedの表情
初心者

「必ず処理を抜ける」ってどういうこと?

専門家

elseブロックの中では、returnbreakcontinuethrow、あるいは fatalError() のような「制御を転送する文」を書かないとエラーになるんだ。

func checkAge(age: Int) {
    guard age >= 18 else {
        print("未成年です")
        return  // これがないとエラー
    }

    print("成人です")
}

checkAge(age: 20)  // 成人です
checkAge(age: 15)  // 未成年です

この例では、18歳未満の場合は早期に関数から抜けて、18歳以上の場合だけ「成人です」と表示されるんだよ。

if文との違い

normalの表情
初心者

これってif文でも書けるんじゃない?

専門家

その通り!同じことはif文でも書けるんだ。でも、guard文の方が 意図が明確読みやすい 場合があるんだよ。

if文での書き方:

func processUser(name: String?) {
    if name != nil {
        let unwrappedName = name!
        print("こんにちは、\(unwrappedName)さん")
        // さらに処理が続く...
    } else {
        print("名前が不正です")
        return
    }
}

guard文での書き方:

func processUser(name: String?) {
    guard let unwrappedName = name else {
        print("名前が不正です")
        return
    }

    print("こんにちは、\(unwrappedName)さん")
    // さらに処理が続く...
}

guard文の方が、「名前が不正な場合は早期に抜ける」という意図が明確だよね。

happyの表情
初心者

確かに!guard文の方が分かりやすい!

専門家

さらに重要なのは、guard文でアンラップした変数は、guard文の後でも使える んだ。

func greet(name: String?) {
    guard let name = name else {
        print("名前がありません")
        return
    }

    // ここでnameが使える!
    print("こんにちは、\(name)さん")
    print("\(name)さん、ようこそ!")
}

if letでアンラップした場合は、ifブロックの中でしか使えないんだよ。

func greet(name: String?) {
    if let name = name {
        // ここでしかnameが使えない
        print("こんにちは、\(name)さん")
    }

    // ここではnameは使えない
    // print("\(name)さん、ようこそ!")  // エラー
}

guard文が向いている場面

専門家

guard文は、特に 前提条件のチェック に向いているんだ。

1. 関数の引数チェック

func divide(a: Int, b: Int) -> Int? {
    guard b != 0 else {
        print("0で割ることはできません")
        return nil
    }

    return a / b
}

if let result = divide(a: 10, b: 2) {
    print("結果: \(result)")  // 結果: 5
}

if let result = divide(a: 10, b: 0) {
    print("結果: \(result)")
} else {
    print("計算できませんでした")  // これが表示される
}

2. 複数の条件チェック

専門家

複数の条件をチェックする場合、guard文を使うとネストが浅くなって読みやすくなるんだ。

if文での書き方(ネストが深い):

func processOrder(productId: String?, quantity: Int?, userId: String?) {
    if let productId = productId {
        if let quantity = quantity {
            if quantity > 0 {
                if let userId = userId {
                    print("注文処理: 商品\(productId)を\(quantity)個、ユーザー\(userId)が注文")
                } else {
                    print("エラー: ユーザーIDがありません")
                    return
                }
            } else {
                print("エラー: 数量が不正です")
                return
            }
        } else {
            print("エラー: 数量がありません")
            return
        }
    } else {
        print("エラー: 商品IDがありません")
        return
    }
}

guard文での書き方(フラットで読みやすい):

func processOrder(productId: String?, quantity: Int?, userId: String?) {
    guard let productId = productId else {
        print("エラー: 商品IDがありません")
        return
    }

    guard let quantity = quantity else {
        print("エラー: 数量がありません")
        return
    }

    guard quantity > 0 else {
        print("エラー: 数量が不正です")
        return
    }

    guard let userId = userId else {
        print("エラー: ユーザーIDがありません")
        return
    }

    print("注文処理: 商品\(productId)を\(quantity)個、ユーザー\(userId)が注文")
}

guard文を使うと、ネストが深くならず、条件チェックが段階的に行われるのが分かりやすいよね。

excitedの表情
初心者

すごい!全然読みやすさが違う!

3. 複数の条件を1つのguardで

専門家

複数の条件を1つのguard文にまとめることもできるんだ。

func login(username: String?, password: String?) {
    guard let username = username,
          let password = password,
          !username.isEmpty,
          !password.isEmpty else {
        print("ユーザー名とパスワードを入力してください")
        return
    }

    print("\(username)でログインしました")
}

login(username: "user1", password: "pass123")  // user1でログインしました
login(username: nil, password: "pass123")      // ユーザー名とパスワードを入力してください
login(username: "user1", password: "")         // ユーザー名とパスワードを入力してください

カンマで区切って、複数の条件を並べられるんだ。すべての条件がtrueの場合だけ、guard文を通過できるんだよ。

guard文とループ

専門家

guard文は、ループの中でも使えるんだ。ループの中では、continuebreak を使って処理を抜けることができるよ。

let numbers = [5, -2, 8, 0, -3, 12]

for number in numbers {
    guard number > 0 else {
        print("\(number)は正の数ではないのでスキップ")
        continue
    }

    print("\(number)の2倍は\(number * 2)")
}
// 5の2倍は10
// -2は正の数ではないのでスキップ
// 8の2倍は16
// 0は正の数ではないのでスキップ
// -3は正の数ではないのでスキップ
// 12の2倍は24

guard文で条件を満たさない場合は continue でスキップして、条件を満たす場合だけ処理を実行するんだ。

normalの表情
初心者

これは便利!条件に合うものだけ処理したいときに使えるね!

専門家

その通り!もう1つ例を見てみよう。

let optionalNumbers: [Int?] = [1, nil, 3, nil, 5]

for optionalNumber in optionalNumbers {
    guard let number = optionalNumber else {
        print("nilをスキップ")
        continue
    }

    print("数値: \(number)")
}
// 数値: 1
// nilをスキップ
// 数値: 3
// nilをスキップ
// 数値: 5

nilの要素をスキップして、値がある要素だけ処理できるんだよ。

if文とguard文の使い分け

confusedの表情
初心者

if文とguard文、どっちを使えばいいか迷っちゃう...

専門家

使い分けのガイドラインを教えるね。

guard文を使う場面:

  • 前提条件のチェック
  • 「この条件を満たさない場合は処理を続けられない」とき
  • オプショナルをアンラップして、その後も使いたいとき
  • ネストを浅くしたいとき

if文を使う場面:

  • 条件によって異なる処理を実行したいとき
  • 条件がtrueの場合の処理が中心のとき
  • アンラップした値をそのブロック内だけで使いたいとき
// guard文が向いている例
func validateUser(age: Int?) {
    guard let age = age else {
        print("年齢が入力されていません")
        return
    }

    guard age >= 0 else {
        print("年齢が不正です")
        return
    }

    // ここからメインの処理
    print("年齢: \(age)歳")
}

// if文が向いている例
func getAgeCategory(age: Int) -> String {
    if age < 18 {
        return "未成年"
    } else if age < 65 {
        return "成人"
    } else {
        return "高齢者"
    }
}

guard文は 早期リターン のため、if文は 条件分岐 のため、と覚えておくといいよ。

実践練習

専門家

じゃあ、練習問題をやってみよう。

問題1: ユーザー登録の関数を作ってみて。名前(name)、年齢(age)、メールアドレス(email)をオプショナルで受け取って、すべて有効な値があれば「登録完了」、どれか1つでもnilなら「入力エラー」と表示する関数を、guard文を使って書いてみて。

excitedの表情
初心者

やってみる!

func registerUser(name: String?, age: Int?, email: String?) {
    guard let name = name else {
        print("入力エラー: 名前がありません")
        return
    }

    guard let age = age else {
        print("入力エラー: 年齢がありません")
        return
    }

    guard let email = email else {
        print("入力エラー: メールアドレスがありません")
        return
    }

    print("登録完了: \(name)さん(\(age)歳、\(email))")
}

registerUser(name: "田中", age: 25, email: "tanaka@example.com")
// 登録完了: 田中さん(25歳、tanaka@example.com)

registerUser(name: "佐藤", age: nil, email: "sato@example.com")
// 入力エラー: 年齢がありません

できた!

専門家

完璧だね!guard文を使って、段階的に条件をチェックできているよ。

さらに、複数の条件を1つのguard文にまとめることもできるよ。

func registerUser(name: String?, age: Int?, email: String?) {
    guard let name = name,
          let age = age,
          let email = email else {
        print("入力エラー: すべての項目を入力してください")
        return
    }

    print("登録完了: \(name)さん(\(age)歳、\(email))")
}

どちらの書き方も正しいよ。エラーメッセージを細かく分けたいなら最初の書き方、シンプルにしたいなら2番目の書き方がいいね。

happyの表情
初心者

なるほど!目的に応じて使い分けられるんだね!

専門家

その通り!

問題2: 配列の中から正の偶数だけを取り出して2倍にする処理を、guard文とcontinueを使って書いてみて。

normalの表情
初心者

えーと...

let numbers = [3, 8, -2, 10, -5, 6, 0, 12]

for number in numbers {
    guard number > 0 else {
        continue
    }

    guard number % 2 == 0 else {
        continue
    }

    print("\(number)の2倍は\(number * 2)")
}
// 8の2倍は16
// 10の2倍は20
// 6の2倍は12
// 12の2倍は24

こう?

専門家

素晴らしい!guard文を使って、条件を満たさないものを早期にスキップできているね。

複数の条件を1つのguard文にまとめることもできるよ。

let numbers = [3, 8, -2, 10, -5, 6, 0, 12]

for number in numbers {
    guard number > 0, number % 2 == 0 else {
        continue
    }

    print("\(number)の2倍は\(number * 2)")
}

この場合は、「正の数かつ偶数」という2つの条件を1つのguard文でチェックしているんだ。どちらの書き方も正しいから、読みやすい方を選べばいいよ。

まとめ

この記事では、guard文について学びました。

guard文の基本:

  • guard 条件 else { 処理を抜ける }
  • 条件がfalseの場合に早期リターン
  • elseブロックで必ず処理を抜ける必要がある

guard文の特徴:

  • アンラップした変数をguard文の後でも使える
  • ネストを浅くできる
  • 前提条件のチェックに適している

if文との使い分け:

  • guard文:早期リターン、前提条件チェック
  • if文:条件分岐、異なる処理の実行

複数の条件:

  • カンマで区切って複数の条件を並べられる
  • すべての条件がtrueの場合だけ通過

ループとの組み合わせ:

  • continueでスキップ
  • breakでループを抜ける

重要なポイント:

  • コードの可読性を高める
  • 意図を明確にする
  • ネストを減らしてフラットな構造にする

次回は「nil安全」として、オプショナル型について詳しく学びます。Swiftの安全性を支える重要な機能を理解しましょう!

← ブログ一覧に戻る