Swift: guard let vs if let

133

J'ai lu sur les options dans Swift, et j'ai vu des exemples où if letest utilisé pour vérifier si une option contient une valeur, et au cas où elle le ferait, faites quelque chose avec la valeur non emballée.

Cependant, j'ai vu que dans Swift 2.0, le mot guard let- clé est principalement utilisé. Je me demande si if leta été supprimé de Swift 2.0 ou s'il est toujours possible de l'utiliser.

Dois-je changer mes programmes qui contiennent if leten guard let?

lmiguelvargasf
la source

Réponses:

165

if letet guard letservent des objectifs similaires mais distincts.

Le cas "else" de guarddoit quitter la portée actuelle. En général, cela signifie qu'il doit appeler returnou abandonner le programme. guardest utilisé pour fournir un retour anticipé sans nécessiter l'imbrication du reste de la fonction.

if letemboîte sa portée, et ne nécessite rien de spécial. Cela peut returnou non.

En général, si le if-letbloc devait être le reste de la fonction, ou si sa elseclause contiendrait un returnor abort, alors vous devriez utiliser à la guardplace. Cela signifie souvent (du moins d'après mon expérience), en cas de doute, que guardc'est généralement la meilleure réponse. Mais il existe de nombreuses situations où cela if letest encore approprié.

Rob Napier
la source
39
À utiliser if letlorsque le non-nilcas est valide. À utiliser guardlorsque le nilcas représente une sorte d'erreur.
BallpointBen
4
@BallpointBen Je ne suis pas d'accord avec cela. Il existe de nombreux cas où cela guardest approprié même s'il n'y a pas d'erreur. Parfois, cela signifie simplement qu'il n'y a rien à faire. Par exemple, une positionTitleméthode pourrait guard if let title = title else {return}. Le titre peut être facultatif, auquel cas ce n'est pas une erreur. Mais guard letc'est toujours approprié.
Rob Napier
1
Ouais; Je voulais dire que le garde laisse entrer le commentaire.
Rob Napier
1
en d'autres termes, "guard let" est utilisé lorsque le code est sûr à 99% de ne pas utiliser le conditionnel else; d'autre part, "if let" lorsque le code est 50 - 50 (exemple) pour utiliser la condition else.
Chino Pan
1
La variable liée par if letn'est visible qu'à l' intérieur if let portée. La variable liée par guard letest visible par la suite. Il est donc logique d'utiliser guard pour lier également des valeurs facultatives.
boweidmann le
106

La garde peut améliorer la clarté

Lorsque vous utilisez guard, vous espérez beaucoup plus que le guard réussisse et il est quelque peu important que s'il ne réussit pas, vous souhaitiez simplement quitter la portée plus tôt . Comme vous vous gardez de voir si un fichier / image existe, si un tableau est vide ou non.

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
}

Si vous écrivez le code ci-dessus avec if-let, cela indique au développeur de lecture qu'il s'agit plus d'un 50-50. Mais si vous utilisez guard, vous ajoutez de la clarté à votre code et cela implique que je m'attends à ce que cela fonctionne 95% du temps ... si cela échouait, je ne sais pas pourquoi; c'est très peu probable ... mais utilisez simplement cette image par défaut à la place ou peut-être simplement affirmer avec un message significatif décrivant ce qui n'a pas fonctionné!

  • Évitez les guardeffets secondaires lorsqu'ils créent des effets secondaires, les protecteurs doivent être utilisés comme flux naturel . Évitez les gardes lorsque les elseclauses introduisent des effets secondaires. Les gardiens établissent les conditions requises pour que le code s'exécute correctement, offrant une sortie anticipée

  • Lorsque vous effectuez un calcul significatif dans la branche positive, refactoriser de ifà une guardinstruction et retourne la valeur de secours dans la elseclause

De: Le livre Swift Style d'Erica Sadun

De plus, en raison des suggestions ci-dessus et du code propre, il est plus probable que vous souhaitiez / devrez ajouter des assertions dans failed déclarations de garde , cela améliore simplement la lisibilité et indique clairement aux autres développeurs ce que vous attendiez.

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

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

De: le livre Swift Style d'Erica Sadun + quelques modifications

(vous n'utiliserez pas d'assertions / conditions préalables pour if-lets. Cela ne semble tout simplement pas correct)

L'utilisation de gardes vous aide également à améliorer la clarté en évitant la pyramide de malheur. Voir la réponse de Nitin .


Guard crée un nouvelle variable

Il y a une différence importante que je pense que personne n'a bien expliqué.

Les deux guard letetif let dérouler la variable cependant

Avec guard letvous créez une nouvelle variable qui existera dehors de l' elseinstruction.

Avec if letvous ne créez pas de nouvelle variable - après l'instruction else, vous n'entrez le bloc de code que si l'option facultative n'est pas nulle. La variable nouvellement créée n'existe qu'à l' intérieur du bloc de code pas après!

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)
    }
}

Pour plus d'informations, if letvoir: Pourquoi la redéclaration de liaison facultative ne crée pas d'erreur


La garde exige sortie de la lunette

(Également mentionné dans la réponse de Rob Napier):

Vous DEVEZ avoir guarddéfini à l' intérieur d' une fonction. Son objectif principal est d'abandonner / de renvoyer / de quitter la portée, si une condition n'est pas remplie:

var str : String?

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

Car if letvous n'avez pas besoin de l'avoir dans une fonction:

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

guard contre if

Il convient de noter qu'il est plus approprié de voir cette question comme guard letvs if letet guardvsif .

Un standalone ifne fait aucun déballage, pas plus qu'un standalone guard. Voir l'exemple ci-dessous. Il ne se termine pas tôt si une valeur est nil. Il n'y a AUCUNE valeur facultative. Il sort juste tôt si une condition n'est pas remplie.

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]
}
Mon chéri
la source
46

Quand utiliser if-letet quand utiliserguard est souvent une question de style.

Supposons que vous ayez func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Intet un tableau facultatif d'éléments ( var optionalArray: [SomeType]?), et que vous devez renvoyer soit 0si le tableau est nil(non défini), soitcount si le tableau a une valeur (est défini).

Vous pouvez l'implémenter comme ceci en utilisant if-let:

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

ou comme ceci en utilisant guard:

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

Les exemples sont fonctionnellement identiques.

guard qui brille vraiment, c'est lorsque vous avez une tâche comme la validation des données et que vous voulez que la fonction échoue tôt si quelque chose ne va pas.

Au lieu d'imbriquer un tas de if-lets au fur et à mesure que vous vous rapprochez de la fin de la validation, le "chemin de réussite" et les options désormais liées avec succès sont tous dans la portée principale de la méthode, car les chemins d'échec sont déjà tous retournés.

divergio
la source
30

Je vais essayer d'expliquer l'utilité des déclarations de garde avec du code (non optimisé).

Vous disposez d'une interface utilisateur dans laquelle vous validez les champs de texte pour l'enregistrement de l'utilisateur avec le prénom, le nom, l'e-mail, le téléphone et le mot de passe.

Si un textField ne contient pas de texte valide, il doit faire de ce champ firstResponder.

voici le code non optimisé:

//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()
    }
}

Vous pouvez voir ci-dessus, que toutes les chaînes (firstNameString, lastNameString, etc.) ne sont accessibles que dans la portée de l'instruction if. donc il crée cette "pyramide de malheur" et a de nombreux problèmes avec elle, y compris la lisibilité et la facilité de déplacer les choses (si l'ordre des champs est modifié, vous devez réécrire la plupart de ce code)

Avec l'instruction de garde (dans le code ci-dessous), vous pouvez voir que ces chaînes sont disponibles en dehors de {}et sont utilisées, si tous les champs sont valides.

// 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)
}

Si l'ordre des champs change, déplacez simplement les lignes de code respectives vers le haut ou vers le bas, et vous êtes prêt à partir.

Ceci est une explication très simple et un cas d'utilisation. J'espère que cela t'aides!

Nitin Alabur
la source
14

Différence de base

Garde laisser

  1. Processus d'existence précoce de la portée
  2. Exiger un score existant comme retour, lancer, etc.
  3. Créez une nouvelle variable à laquelle accéder hors de la portée.

si laissez

  1. Impossible d'accéder à la portée.
  2. pas besoin de retourner la déclaration. Mais on peut écrire

REMARQUE: les deux sont utilisés pour dérouler la variable facultative.

Kiran K
la source
2

L'explication la plus claire que j'ai vue était dans le guide de style Github Swift :

if ajoute un niveau de profondeur:

if n.isNumber {
    // Use n here
} else {
    return
}

guard ne:

guard n.isNumber else {
    return
}
// Use n here
pics sauvages
la source
2

garde

  • Une guardinstruction est utilisée pour transférer le contrôle du programme hors d'une portée si une ou plusieurs conditions ne sont pas remplies.

  • La valeur de toute condition dans une guardinstruction doit être de type Bool ou de type ponté Bool. La condition peut également être une déclaration de liaison facultative.

Une déclaration de garde a la forme suivante:

guard condition else {
    //Generally return
}

si laissez

  • Aussi populaire comme reliure optionnelle .
  • Pour accéder aux objets optionnels que nous utilisons if let.
if let roomCount = optionalValue {
    print("roomCount available")
} else {
    print("roomCount is nil")
}
Akshay
la source
1

J'ai appris cela de Swift avec Bob.

Else-If typique

 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")
          }
     }

Problèmes avec Else-If

  1. Crochets imbriqués
  2. Il faut lire chaque ligne pour repérer le message d'erreur

Déclaration de garde Un bloc de garde ne s'exécute que si la condition est fausse, et il sortira de la fonction par retour. Si la condition est vraie, Swift ignore le bloc de garde. Il fournit une sortie anticipée et moins de parenthèses. +

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.
       }

Développer les options avec Else-If

Une instruction de garde n'est pas seulement utile pour remplacer un bloc conditionnel typique par une instruction else-if, mais aussi pour déballer les options en minimisant le nombre de crochets. Pour comparer, commençons par déballer plusieurs options avec else-if. Tout d'abord, créons trois options qui seront déballées.

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

Le pire cauchemar

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")
                         }
                  }

Le code ci-dessus fonctionne certainement mais viole le principe DRY. C'est atroce. Décomposons-le +.

Légèrement mieux Le code ci-dessous est plus lisible que ci-dessus. +

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
                           }
     }

Unwrap with Guard Les instructions else-if peuvent être remplacées par 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)
         }

Développer plusieurs options avec Else-If Jusqu'à présent, vous avez déballé les options une par une. Swift nous permet de déplier plusieurs options à la fois. Si l'un d'eux contient nil, il exécutera le bloc 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")
  }
}

Sachez que lorsque vous déballez plusieurs options à la fois, vous ne pouvez pas identifier celle qui contient nil

Développer plusieurs options avec Guard Bien sûr, nous devrions utiliser guard sur 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
}
Ourang-Zeb Khan
la source
Veuillez revenir en arrière et corriger le formatage / l'indentation de votre code!
pkamb