Les auteurs du livre font partie de l'équipe de développement du langage, ce qui signifie que vous recevrez toutes les informations de première main - de l'installation du langage à la création de programmes fiables et évolutifs. De la création de fonctions, du choix des types de données et de la liaison des variables, vous passerez à des concepts plus complexes:
- Propriété et emprunt, cycle de vie et types.
- Sécurité logicielle garantie.
- Test, gestion des erreurs et refactoring efficace.
- Génériques, pointeurs intelligents, multithreading, objets échangeables et mappages.
- Travaillez avec le gestionnaire de packages intégré Cargo pour créer, tester, documenter le code et gérer les dépendances.
- Outils avancés pour travailler avec Unsafe Rust.
Vous trouverez de nombreux exemples de code, ainsi que trois chapitres sur la création de projets complets pour solidifier les connaissances: deviner des jeux, créer un outil de ligne de commande et un serveur multithread.
Pour qui est ce livre
Nous supposons que vous avez écrit votre code dans un langage de programmation différent, mais nous ne faisons aucune hypothèse sur lequel. Nous avons essayé de rendre ce matériel accessible à ceux qui possèdent un large éventail de compétences en programmation. Nous ne perdrons pas de temps à parler de ce qu'est la programmation. Si vous êtes un débutant absolu en programmation, lisez d'abord l'introduction à la programmation.
Comment utiliser ce livre
-, , , . , ; .
: . . , , . 2, 12 20 , — .
1 , Rust, «Hello, World!» Cargo. 2 Rust. , . , . 3, Rust, , 4 Rust. , , 2, 3, 2, . , .
5 , 6 , match if let. Rust .
7 (API). 8 , , , -. 9 .
10 , , , . 11 , Rust . 12 grep, . , .
13 — , . 14 Cargo . 15 , , , .
16 , Rust . 17 Rust - , , , .
18 , Rust. 19 , , Rust, , , .
20 , !
, . Rust, Rust, , , , Rust.
: -, ! - , , , . , , .
Rust — , : . , , . , , ! , , , , . , .
: . . , , . 2, 12 20 , — .
1 , Rust, «Hello, World!» Cargo. 2 Rust. , . , . 3, Rust, , 4 Rust. , , 2, 3, 2, . , .
5 , 6 , match if let. Rust .
7 (API). 8 , , , -. 9 .
10 , , , . 11 , Rust . 12 grep, . , .
13 — , . 14 Cargo . 15 , , , .
16 , Rust . 17 Rust - , , , .
18 , Rust. 19 , , Rust, , , .
20 , !
, . Rust, Rust, , , , Rust.
: -, ! - , , , . , , .
Rust — , : . , , . , , ! , , , , . , .
Où les modèles peuvent être utilisés
Dans Rust, les motifs apparaissent à de nombreux endroits, et vous les avez beaucoup utilisés sans même vous en rendre compte! Cette section traite des situations dans lesquelles les modèles sont valides.
Faire correspondre les branches d'expression
Comme indiqué dans le chapitre 6, nous utilisons des modèles dans les branches des expressions de correspondance. Formellement, les expressions de correspondance sont définies comme la correspondance de mot-clé, puis la valeur à mettre en correspondance et une ou plusieurs branches de la correspondance constituées du modèle et de l'expression à exécuter si la valeur correspond au modèle de cette branche, par exemple:
match {
=> ,
=> ,
=> ,
}
L'une des conditions requises pour les expressions de correspondance est qu'elles doivent être complètes, en ce sens que toutes les valeurs possibles doivent être prises en compte dans la correspondance. Pour que vous considériez toutes les options possibles, vous devez avoir un modèle global dans la dernière branche: par exemple, un nom de variable qui correspond à n'importe quelle valeur fonctionnera toujours et couvrira donc tous les cas restants.
Le motif spécial _ correspondra à tout, mais il ne se lie pas à une variable et est donc souvent utilisé dans le dernier manchon de correspondance. Le modèle _ est utile, par exemple, si vous souhaitez ignorer toute valeur non spécifiée. Nous examinerons le modèle plus en détail dans la section «Ignorer les valeurs d'un modèle».
Si laissez conditionnelles
Dans le chapitre 6, nous avons discuté des instructions if let principalement comme un moyen plus court d'écrire l'équivalent d'une expression de correspondance qui ne correspond qu'à un seul cas. Sinon, if let peut avoir une correspondance else contenant le code à exécuter si le modèle dans if let ne correspond pas.
Le listing 18.1 montre qu'il est également possible de mélanger et de faire correspondre les instructions if let, else if et else if let. Cela nous donne plus de flexibilité que d'utiliser une expression de correspondance, qui ne peut exprimer qu'une seule valeur à comparer aux modèles. De plus, les conditions dans une série d'instructions if let, else if et else if let n'ont pas besoin de se référer les unes aux autres.
Le code du Listing 18.1 montre une série de tests pour plusieurs conditions qui décident de la couleur d'arrière-plan. Dans cet exemple, nous avons créé des variables avec des valeurs codées en dur qu'un programme réel pourrait récupérer à partir de l'entrée utilisateur.
Liste 18.1. Mélanger if let, else if, else if let et instructions else
src/main.rs
fn main() {
let favorite_color: Option<&str> = None;
let is_tuesday = false;
let age: Result<u8, _> = "34".parse();
(1) if let Some(color) = favorite_color {
(2) println!(" , {}, ", color);
(3) } else if is_tuesday {
(4) println!(" - !");
(5) } else if let Ok(age) = age {
(6) if age > 30 {
(7) println!(" ");
} else {
(8) println!(" ");
}
(9) } else {
(10) println!(" ");
}
}
Si l'utilisateur spécifie une couleur préférée (1), il s'agit de la couleur d'arrière-plan (2). Si aujourd'hui est mardi (3), alors la couleur de fond est verte (4). Si l'utilisateur spécifie son âge sous forme de chaîne et que nous pouvons l'analyser avec succès sous forme de nombre (5), la couleur sera alors violette (7) ou orange (8) en fonction de la valeur du nombre (6). Si aucune de ces conditions ne s'applique (9), la couleur d'arrière-plan est le bleu (10).
Cette structure conditionnelle permet des exigences complexes. Avec les valeurs codées en dur ici, cet exemple afficherait
.
Vous pouvez voir qu'une expression if let peut également introduire des variables ombrées de la même manière que les manchons d'une expression de correspondance: la ligne de code if let Ok (age) = age (5) introduit une nouvelle variable ombrée age contenant la valeur dans la variante Ok. Cela signifie que nous devons mettre la condition si age> 30 dans ce bloc (6): nous ne pouvons pas combiner ces deux conditions dans l'instruction si laissez Ok (age) = age && age> 30. La variable ombrée age que nous voulons comparer avec 30 , sera invalide jusqu'à ce que la nouvelle portée commence par une accolade.
L'inconvénient de l'utilisation des instructions if let est que le compilateur ne vérifie pas l'exhaustivité, contrairement aux instructions match. Si nous avions ignoré le dernier bloc else (9) et, par conséquent, la gestion de certains cas, le compilateur ne nous aurait pas avertis d'une éventuelle erreur logique.
Alors que laissez les boucles conditionnelles
De conception similaire à l'instruction if let, la boucle conditionnelle while let permet à la boucle while de s'exécuter tant que le modèle correspond. L'exemple du Listing 18.2 montre une boucle while let qui utilise un vecteur comme pile et renvoie les valeurs du vecteur dans l'ordre inverse de l'ordre dans lequel elles ont été ajoutées.
Liste 18.2. Utilisation d'une boucle while let pour afficher les valeurs tandis que stack.pop () renvoie Some
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
while let Some(top) = stack.pop() {
println!("{}", top);
}
Cet exemple imprime 3, 2, puis 1. La méthode pop prend le dernier élément du vecteur et renvoie Some (valeur). Si le vecteur est vide, alors pop renvoie None. La boucle while continue d'exécuter le code dans son bloc jusqu'à ce que pop renvoie Some. Lorsque pop renvoie None, la boucle s'arrête. Nous pouvons utiliser une boucle conditionnelle while let pour supprimer chaque élément de la pile.
Pour les boucles
Dans le chapitre 3, nous avons mentionné que la boucle for est la construction de boucle la plus courante dans le code Rust, mais nous n'avons pas encore discuté du modèle que pour prend. Dans une boucle for, le modèle est la valeur qui suit immédiatement le mot-clé for, donc pour x dans y, le modèle est x.
Le Listing 18.3 montre l'utilisation d'un motif dans une boucle for pour déstructurer ou décomposer un tuple dans un for.
Liste 18.3. Utilisation d'un modèle dans une boucle for pour déstructurer un tuple
let v = vec!['a', 'b', 'c'];
for (index, value) in v.iter().enumerate() {
println!("{} {}", value, index);
}
Le code du Listing 18.3 affiche ce qui suit:
0
b 1
2
Nous utilisons la méthode enumerate pour réécrire l'itérateur afin de produire la valeur et l'index de cette valeur dans l'itérateur, placé dans un tuple. Le premier appel à la méthode enumerate produit un tuple (0, 'a'). Lorsque cette valeur est combinée avec le modèle (index, valeur), alors l'index est 0 et la valeur est «a», la première ligne de données est sortie.
Let déclarations
Avant ce chapitre, nous avons directement discuté de l'utilisation de modèles uniquement avec les instructions match et if let, mais en fait nous avons utilisé des modèles ailleurs, y compris dans les instructions let. Considérez un moyen simple de passer la valeur d'une variable en utilisant let:
let x = 5;
Nous avons utilisé ce genre de déclarations let des centaines de fois tout au long de ce livre, et même si vous ne l'avez peut-être pas réalisé, vous avez utilisé des modèles! Plus formellement, une instruction let ressemble à ceci:
let = ;
Dans des instructions telles que let x = 5; avec un nom de variable dans l'emplacement PATTERN, le nom de la variable n'est qu'une forme simple du modèle. Rust compare l'expression au modèle et attribue les noms qu'il trouve. Par conséquent, dans l'exemple, laissez x = 5; le motif est x, ce qui signifie "associer ce qui correspond ici à la variable x". Puisque le nom x représente le modèle entier, ce modèle signifie en fait «lier tout à la variable x, quelle que soit la valeur».
Pour voir plus clairement le mappage avec le modèle d'instruction let, considérez l'extrait 18.4, qui utilise le modèle let pour déstructurer un tuple.
Liste 18.4. Utiliser un modèle pour déstructurer un tuple et créer trois variables à la fois
let (x, y, z) = (1, 2, 3);
Ici, nous mappons un tuple à un modèle. Rust compare (1, 2, 3) avec (x, y, z) et voit que cette valeur correspond au modèle, donc Rust associe 1 à x, 2 à y et 3 à z. Vous pouvez considérer ce modèle de tuple comme l'imbrication de trois modèles de variables distincts dans celui-ci.
Si le nombre d'éléments du modèle ne correspond pas au nombre d'éléments du tuple, le type d'agrégat ne correspondra pas et nous obtiendrons une erreur de compilation. Par exemple, le Listing 18.5 montre une tentative de déstructurer un triplet en deux variables, ce qui ne fonctionnera pas.
Liste 18.5. Construction incorrecte du motif dont les variables ne coïncident pas avec le nombre d'éléments dans le tuple
let (x, y) = (1, 2, 3);
Tenter de compiler ce code entraîne une erreur comme:
error[E0308]: mismatched types
--> src/main.rs:2:9
|
2 | let (x, y) = (1, 2, 3);
| ^^^^^^ expected a tuple with 3 elements, found one with 2 elements
|
= note: expected type `({integer}, {integer}, {integer})`
found type `(_, _)`
Si nous voulions ignorer une ou plusieurs valeurs dans un tuple, nous pourrions utiliser _ ou .., comme vous le verrez dans la section «Ignorer les valeurs dans un modèle». Si le problème est qu'il y a trop de variables dans le modèle, vous devez faire correspondre les types en supprimant les variables afin que le nombre de variables soit égal au nombre d'éléments dans le tuple.
Paramètres de fonction
Les paramètres de fonction peuvent également être des modèles. Le code du Listing 18.6 qui déclare une fonction foo qui prend un paramètre x de type i32 vous est maintenant familier.
Liste 18.6. La signature de fonction utilise des modèles dans les paramètres
fn foo(x: i32) {
//
}
La partie x est un motif! Comme avec let, nous pouvons faire correspondre le tuple dans les arguments de la fonction au modèle. Le listing 18.7 décompose les valeurs dans le tuple lorsque nous le passons à l'intérieur de la fonction.
Liste 18.7. Fonction avec des paramètres qui déstructurent le tuple
src/main.rs
fn print_coordinates(&(x, y): &(i32, i32)) {
println!(" : ({}, {})", x, y);
}
fn main() {
let point = (3, 5);
print_coordinates(&point);
}
Ce code sort
: (3, 5)
Les valeurs & (3, 5) correspondent au motif & (x, y), donc x vaut 3 et y vaut 5.
De plus, nous pouvons utiliser des motifs dans les listes de paramètres de fermeture de la même manière que dans les listes de paramètres de fonction, car les fermetures sont similaires aux fonctions, comme décrit dans le chapitre 13.
Vous avez déjà vu plusieurs façons d'utiliser les patterns, mais elles ne fonctionnent pas de la même manière partout où vous les utilisez. Dans certaines situations, ces modèles doivent être irréfutables; dans d'autres, ils peuvent être réfutables. Nous discuterons de ces deux concepts ci-dessous.
Réfutabilité: possibilité d'incohérence de modèle
Les motifs se déclinent en deux saveurs: réfutable et irréfutable. Les modèles qui correspondent à toute valeur possible transmise sont irréfutables. Un exemple est le x dans l'instruction let x = 5;, car x correspond absolument à tout, et ne peut donc pas ne pas correspondre. Les modèles qui ne correspondent pas à certaines des significations possibles sont réfutables. Un exemple est Some (x) dans l'instruction if let Some (x) = a_value, car si la valeur de a_value est None et non Some, alors Some (x) ne correspondra pas.
Les paramètres de fonction, les instructions let et les boucles for ne peuvent accepter que des modèles irréfutables car le programme ne peut rien faire de significatif lorsque les valeurs ne correspondent pas. Les expressions if let et while let n'acceptent que des modèles réfragables, car, par définition, elles sont conçues pour gérer une erreur possible: la fonctionnalité d'une expression conditionnelle est sa capacité à effectuer différentes actions en fonction du succès ou de l'échec.
En général, vous ne devriez pas vous préoccuper de la distinction entre les modèles réfutables et irréfutables. Cependant, vous devez toujours être conscient du concept de réfutabilité afin de réagir lorsque vous le voyez dans un message d'erreur. Dans ces cas, vous devrez modifier le modèle ou la construction avec laquelle vous utilisez le modèle, en fonction du comportement prévu du code.
Jetons un coup d'œil à ce qui se passe lorsque nous essayons d'utiliser un motif irréfutable dans un endroit où Rust nécessite un motif irréfutable, et vice versa. Le listing 18.8 montre une instruction let, mais pour le modèle, nous avons spécifié Some (x), un modèle réfutable. Comme vous vous en doutez, ce code ne se compile pas.
Annonce 18.8. Essayer d'utiliser un modèle réfutable avec let
let Some(x) = some_option_value;
Si some_option_value était égal à None, alors cela ne coïnciderait pas avec le modèle Some (x), c'est-à-dire que le modèle est réfutable. Cependant, une instruction let ne peut accepter qu'un modèle irréfutable, car le code ne peut rien faire de valide avec la valeur None. Au moment de la compilation, Rust se plaindra que nous avons essayé d'utiliser un modèle réfutable où un modèle irréfutable est requis:
error[E0005]: refutable pattern in local binding: `None` not covered
-->
|
3 | let Some(x) = some_option_value;
| ^^^^^^^ pattern `None` not covered
Puisque nous n'avons pas (et n'aurions pas pu) couvrir chaque valeur valide avec le modèle Some (x), Rust lève à juste titre une erreur de compilation.
Pour résoudre le problème lorsque nous avons un modèle réfutable au lieu d'un modèle irréfutable, nous pouvons changer le code qui utilise le modèle: au lieu de let, nous pouvons utiliser if let. Ensuite, si le modèle ne correspond pas, le code entre accolades sera ignoré et le travail se poursuivra correctement. Le Listing 18.9 montre comment corriger le code du Listing 18.8.
Annonce 18.9. Utiliser une instruction if let et un bloc de modèle réfutable au lieu de let
if let Some(x) = some_option_value {
println!("{}", x);
}
Le code est prêt! C'est un code absolument correct, bien que cela signifie que nous ne pouvons pas utiliser un modèle irréfutable sans erreur. Si nous donnons à l'expression if let un modèle qui correspondra toujours, tel que x, comme indiqué dans l'extrait 18-10, alors il ne se compilera pas.
Annonce 18.10. Essayer d'utiliser un modèle irréfutable avec une instruction if let
if let x = 5 {
println!("{}", x);
};
Le compilateur se plaint qu'il n'a pas de sens d'utiliser une expression if let avec un modèle irréfutable:
error[E0162]: irrefutable if-let pattern
--> <anon>:2:8
|
2 | if let x = 5 {
| ^ irrefutable pattern
Pour cette raison, les manches d'une expression de correspondance doivent utiliser des motifs réfutables, à l'exception du dernier manchon, qui doit correspondre à toutes les valeurs restantes par rapport à un motif irréfutable. Rust permet d'utiliser le motif irréfutable dans une expression de correspondance avec un seul manchon, mais cette syntaxe n'est pas particulièrement utile et peut être remplacée par une instruction let plus simple.
Maintenant que vous savez où les modèles sont utilisés et en quoi les modèles réfutables et irréfutables diffèrent, familiarisons-nous avec la syntaxe que nous pouvons utiliser pour créer des modèles.
À propos des auteurs
Steve Klabnik dirige l'équipe de documentation Rust et est l'un des principaux développeurs du langage. Il est un conférencier régulier et écrit beaucoup de code open source. Auparavant travaillé sur des projets tels que Ruby et Ruby on Rails.
Carol Nichols est membre de l'équipe de développement de Rust Core et cofondatrice d'Integer 32, LLC, la première société de conseil en développement de logiciels axée sur Rust au monde. Nichols est l'organisateur de la conférence Rust Belt sur le langage Rust.
»Plus de détails sur le livre peuvent être trouvés sur le site de l'éditeur
» Table des matières
» Extrait
Pour les Habitants un rabais de 25% sur le coupon - Rouille
Lors du paiement de la version papier du livre, un e-book est envoyé à l'e-mail.