スウィフト:ガードレットvsレットレット


130

私はSwiftのオプションについて読んでいて、 if let、オプションが値を保持しているかどうかをチェックするために使用される。

ただし、Swift 2.0ではキーワードguard letが主に使用されていることを確認しました。if letSwift 2.0から削除されたのか、それともまだ使えるのか。

を含むプログラムをに変更する必要if letがありguard letますか?

回答:


164

if letそして、guard let似ていますが異なる目的を果たします。

の「else」の場合はguard、現在のスコープを終了する必要があります。一般的にはreturn、プログラムを呼び出すか中止する必要があります。guard関数の残りの入れ子を必要とせずに早期復帰を提供するために使用されます。

if letスコープをネストし、特別なものを必要としません。それはできるreturnかどうか。

一般に、if-letブロックが関数の残りの部分になる場合、またはそのelse句にa returnまたはabortが含まれる場合は、guard代わりに使用する必要があります。これは多くの場合、(少なくとも私の経験では)疑わしい場合guardは通常、より良い答えです。しかし、if letそれでも適切な状況はたくさんあります。


37
ケースが有効な場合に使用if letnon-nilます。ケースが何らかのエラーを表す場合に使用guardnilます。
BallpointBen 2017年

4
@BallpointBen私はそれに同意しません。guardエラーがなくても適切な場合が多くあります。時々、それは何もする必要がないことを意味します。たとえば、positionTitleメソッドは次のようになりguard if let title = title else {return}ます。タイトルは省略可能ですが、その場合はエラーではありません。しかしguard let、それでも適切です。
Rob Napier

1
うん。私はコメントでガードを許可することを意味しました。
Rob Napier

1
言い換えると、コードがelse条件を使用しないことが99%確信している場合に「guard let」が使用されます。一方、コードが50〜50の場合の「if let」は、else条件を使用するための例です。
チノパン

1
バインドされた変数if letは、スコープ でのみ表示さif letます。バインドされた変数guard letは後で表示されます。したがって、オプションの値をバインドするためにガードを使用することも理にかなっています。
boweidmann

105

ガードは明快さを向上せることができます

ガードを使用すると、ガードが成功する可能性がはるかに高くなります。成功しない場合は、スコープを早期に終了することが重要です。配列がEmptyかどうかにかかわらず、ファイル/イメージが存在するかどうかを監視するように。

func icon() -> UIImage {
    guard let image = UIImage(named: "Photo") else {
        return UIImage(named: "Default")! //This is your fallback
    }
    return image //-----------------you're always expecting/hoping this to happen
}

上記のコードをif-letで記述した場合、それは50〜50程度であることが開発者に伝わります。しかし、ガードを使用する場合は、コードに明快さを追加し、95%の確率でこれが機能することを期待していることを意味します。それは非常にありそうにありません...しかし、代わりにこのデフォルトの画像を使用するか、おそらく何が問題かを説明する意味のあるメッセージでアサートしてください!

  • guard副作用が生じる場合はを避けてください。ガードは自然な流れとして使用されます。else句によって副作用が生じる場合は、ガードを避けてください。ガードはコードが適切に実行されるために必要な条件を確立し、早期終了を提供します

  • 正の分岐で重要な計算を実行するとき、from ifguardステートメントにリファクタリングし、else句の フォールバック値を返します

From: エリカサドゥンのスウィフトスタイルブック

また、上記の提案とクリーンなコードの結果として、失敗したガードステートメントにアサーションを追加する必要がある可能性高くなり、読みやすさが向上し、期待していたことが他の開発者に明確になります。

guard​ ​let​ image =UIImage(named: selectedImageName) else { // YESSSSSS
     assertionFailure("Missing ​​\(​selectedImageName​)​​ asset") 
     return
} 

guard​ ​let​ image =UIImage(named: selectedImageName) else { // NOOOOOOO
​     ​return 
}

From: エリカサドゥンのスウィフトスタイルブック +いくつかの変更

if-letsのアサート/前提条件は使用しません。正しくないようです)

ガードを使用すると、運命のピラミッド回避することにより、明快さ向上させることもできます。Nitinの回答を参照してください。


Guardは新しい変数を作成します

誰もうまく説明していないと私が信じている重要な違いが1つあります。

ただし、両方とも変数guard letif let アンラップします

guard letあなたが作成しているだろう、新たな変数が存在するの外にelse声明を。

ではif let、あなたが作成されていないすべての新しい可変後else文を、あなただけの入力したコードブロックを場合は、オプションが非nilです。新しく作成された変数は、コードブロックにのみ存在します。

guard let:

func someFunc(blog: String?) {

    guard let blogName = blog else {
        print("some ErrorMessage")
        print(blogName) // will create an error Because blogName isn't defined yet
        return
    }
    print(blogName) // You can access it here ie AFTER the guard statement!!

    //And if I decided to do 'another' guard let with the same name ie 'blogName' then I would create an error!
    guard let blogName = blog else { // errorLine: Definition Conflicts with previous value.
        print(" Some errorMessage")
        return
    }
    print(blogName)
}

if-let:

func someFunc(blog: String?) {


    if let blogName1 = blog {
        print(blogName1) // You can only access it inside the code block. Outside code block it doesn't exist!
    }
    if let blogName1 = blog { // No Error at this line! Because blogName only exists inside the code block ie {}
        print(blogName1)
    }
}

詳細についてif letは、「オプションのバインディングを再宣言してもエラーが発生しない理由」を参照してください。


ガードにはスコープの終了が必要です

(Rob Napierの回答にも記載されています):

func 内でguard定義しておく必要があります。主な目的は、条件が満たされない場合にスコープを中止/返却/終了することです。

var str : String?

guard let blogName1 = str else {
    print("some error")
    return // Error: Return invalid outside of a func
}
print (blogName1)

if letあなたはどのFUNC内側に持っている必要はありません。

var str : String?    
if let blogName1 = str {
   print(blogName1) // You don't get any errors!
}

guardif

この質問をguard letvs if letおよびguardvs として見る方が適切であることは注目に値しifます。

スタンドアロンifはアンラップを行わず、スタンドアロンも行いませんguard。以下の例を参照してください。値がの場合、早期終了しませんnil。オプションの値はありません。条件が満たされない場合は、ただちに終了します。

let array = ["a", "b", "c"]
func subscript(at index: Int) -> String?{
   guard index > 0, index < array.count  else { return nil} // exit early with bad index
   return array[index]
}

46

いつ使用するかif-let、いつ使用するかguardは、しばしばスタイルの問題です。

たとえばfunc collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int、アイテムのオプションの配列(var optionalArray: [SomeType]?)があり0、配列がnil(設定されていない)かcount、配列に値が設定されている(設定されている)かのいずれかを返す必要があるとします。

次のようにして、このように実装できますif-let

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
    {
        if let array = optionalArray {
            return array.count
        }
        return 0
    }

またはこれを使用してこれを好きになるguard

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
    {
        guard let array = optionalArray else {
            return 0
        }
        return array.count
    }

例は機能的に同じです。

どこ guardあなたがデータを検証などのタスクを持っている、とあなたは何が間違っている場合、関数は早期に失敗したいときには本当に輝いています。

if-let検証の終了に近づくと、一連のs をネストする代わりに、「成功パス」と現在正常にバインドされたオプションはすべて、メソッドのメインスコープにあります。これは、失敗パスがすべて返されているためです。


30

いくつかの(最適化されていない)コードを使用して、ガードステートメントの有用性を説明します。

姓、名、電子メール、電話番号、パスワードを使用してユーザー登録するためのテキストフィールドを検証するUIがあります。

textFieldに有効なテキストが含まれていない場合は、そのフィールドをfirstResponderにする必要があります。

ここに最適化されていないコードがあります:

//pyramid of doom

func validateFieldsAndContinueRegistration() {
    if let firstNameString = firstName.text where firstNameString.characters.count > 0{
        if let lastNameString = lastName.text where lastNameString.characters.count > 0{
            if let emailString = email.text where emailString.characters.count > 3 && emailString.containsString("@") && emailString.containsString(".") {
                if let passwordString = password.text where passwordString.characters.count > 7{
                    // all text fields have valid text
                    let accountModel = AccountModel()
                    accountModel.firstName = firstNameString
                    accountModel.lastName = lastNameString
                    accountModel.email = emailString
                    accountModel.password = passwordString
                    APIHandler.sharedInstance.registerUser(accountModel)
                } else {
                    password.becomeFirstResponder()
                }
            } else {
                email.becomeFirstResponder()
            }
        } else {
            lastName.becomeFirstResponder()
        }
    } else {
        firstName.becomeFirstResponder()
    }
}

上記のとおり、すべての文字列(firstNameString、lastNameStringなど)には、ifステートメントのスコープ内でのみアクセスできます。そのため、この「運命のピラミッド」が作成され、可読性や移動のしやすさなど、多くの問題があります(フィールドの順序が変更された場合は、このコードのほとんどを書き直す必要があります)

(以下のコードの)ガードステートメントを{}使用すると、すべてのフィールドが有効な場合に、これらの文字列がの外部で使用可能であり、使用されていることがわかります。

// guard let no pyramid of doom
func validateFieldsAndContinueRegistration() {

guard let firstNameString = firstName.text where firstNameString.characters.count > 0 else {
            firstName.becomeFirstResponder()
            return
        }
guard let lastNameString = lastName.text where lastNameString.characters.count > 0 else {
            lastName.becomeFirstResponder()
            return
        }
guard let emailString = email.text where 
        emailString.characters.count > 3 &&
        emailString.containsString("@") && 
        emailString.containsString(".") else {
            email.becomeFirstResponder()
            return
        }
guard let passwordString = password.text where passwordString.characters.count > 7 else {
            password.becomeFirstResponder()
            return
        }

// all text fields have valid text
    let accountModel = AccountModel()
    accountModel.firstName = firstNameString
    accountModel.lastName = lastNameString
    accountModel.email = emailString
    accountModel.password = passwordString
    APIHandler.sharedInstance.registerUser(accountModel)
}

フィールドの順序が変更された場合は、それぞれのコード行を上下に移動するだけで問題ありません。

これは非常に単純な説明であり、使用例です。お役に立てれば!


14

基本的な違い

ガードレット

  1. スコープからの早期存在プロセス
  2. リターン、スローなどのスコアが存在する必要があります
  3. スコープからアクセスできる新しい変数を作成します。

させて

  1. スコープにアクセスできません。
  2. ステートメントを返す必要はありません。しかし、私たちは書くことができます

注:どちらもOptional変数のラップを解除するために使用されます。



2

ガード

  • guard1つ以上の条件が満たされていない場合のステートメントは、スコープの外に転送プログラム制御に使用されています。

  • guardステートメント内の条件の値は、タイプBool またはブリッジされるタイプである必要がありますBool。条件は、オプションのバインディング宣言にすることもできます。

ガードステートメントの形式は次のとおりです。

guard condition else {
    //Generally return
}

させて

if let roomCount = optionalValue {
    print("roomCount available")
} else {
    print("roomCount is nil")
}

1

私はこれをボブと一緒に素早く学んだ。

典型的なElse-If

 func checkDrinkingAge() {
      let canDrink = true

     if canDrink {
        print("You may enter")
       // More Code
        // More Code
      // More Code

         } else {
         // More Code
    // More Code
    // More Code
    print("Let me take you to the jail")
          }
     }

Else-Ifの問題

  1. ネストされたブラケット
  2. エラーメッセージを見つけるためにすべての行を読む必要があります

ガードステートメント ガードブロックは、条件がfalseの場合にのみ実行され、リターンによって関数から抜けます。条件が真の場合、Swiftはガードブロックを無視します。それは早期の終了とより少ない括弧を提供します。+

func checkDrinkProgram() {
       let iCanDrink = true

           guard iCanDrink else {
        // if iCanDrink == false, run this block
         print("Let's me take you to the jail")
          return
        }

         print("You may drink")
           // You may move on
                  // Come on.
                 // You may leave
                // You don't need to read this.
                 // Only one bracket on the bottom: feeling zen.
       }

Else-Ifでオプションをアンラップ

ガードステートメントは、一般的な条件付きブロックをelse-ifステートメントで置き換えるのに役立つだけでなく、角かっこの数を最小限にしてオプションをアンラップするのにも最適です。比較のために、まず、else-ifを使用して複数のオプションをアンラップする方法から始めましょう。まず、アンラップされる3つのオプションを作成します。

var publicName: String? = "Bob Lee"
var publicPhoto: String? = "Bob's Face"
var publicAge: Int? = nil

最悪の悪夢

func unwrapOneByOne() {
         if let name = publicName {
              if let photo = publicPhoto {
                     if let age = publicAge {
                        print("Bob: \(name), \(photo), \(age)")
                                  } else {
                          print("age is mising")
                           }
                  } else {
                      print("photo is missing")
                         }
                  } else {
                        print("name is missing")
                         }
                  }

上記のコードは確かに機能しますが、DRYの原則に違反しています。それはひどいです。分解してみましょう+。

少し良い 以下のコードは、上記よりも読みやすくなっています。+

func unwrapBetter() {
         if let name = publicName {
       print("Yes name")
                   } else {
               print("No name")
        return
      }

         if let photo = publicPhoto {
             print("Yes photo")
            } else {
           print("No photo")
       return
             }

        if let age = publicAge {
            print("Yes age")
                      } else {
                print("No age")
            return
                           }
     }

Guardでアンラップ else-ifステートメントをguardで置き換えることができます。+

 func unwrapOneByOneWithGuard() {
             guard let name = publicName else {
                  print("Name missing")
              return
                                        }

              guard let photo = publicPhoto else {
              print("Photo missing")
                return
                                            }

                  guard let age = publicAge else {
                   print("Age missing")
                                     return
                                                 }
                 print(name)
                 print(photo)
                 print(age)
         }

Else-Ifを使用して複数のオプションをアンラップする これまで、オプションを1つずつアンラップしてきました。Swiftを使用すると、複数のオプションを一度にアンラップできます。それらのいずれかにnilが含まれている場合は、elseブロックが実行されます。

func unwrap() {
  if let name = publicName, let photo = publicPhoto, let age = publicAge {
    print("Your name is \(name). I see your face right here, \(photo), you are \(age)")
  } else {
    // if any one of those is missing
    print("Something is missing")
  }
}

一度に複数のオプションを展開すると、nilを含むものを特定できないことに注意してください

ガードを使用して複数のオプションをアンラップするもちろん、else-ifの場合はガードを使用する必要があります。+

func unwrapWithGuard() {
  guard let name = publicName, let photo = publicPhoto, let age = publicAge else {
    // if one or two of the variables contain "nil"
    print("Something is missing")
    return
  }

  print("Your name is \(name). I see your, \(photo). You are \(age).")
  // Animation Logic
  // Networking
  // More Code, but still zen
}

戻ってコードのフォーマット/インデントを修正してください!
pkamb
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.