FAQ sur la programmation en langage RustConsultez toutes les FAQ
Nombre d'auteurs : 1, nombre de questions : 95, dernière mise à jour : 21 juillet 2018 Ajouter une question
Cette FAQ a été réalisée pour répondre aux questions les plus fréquemment posées sur la programmation en langage Rust. Nous vous recommandons de la consulter avant de poser vos questions sur le forum Rust.
Nous tenons à souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle contient sont correctes ; les auteurs font le maximum, mais l'erreur est humaine. Si vous trouvez une erreur, ou que vous souhaitez devenir rédacteur, lisez ceci.
Toute nouvelle question/réponse est la bienvenue, vous pouvez proposer vos questions réponses par e-mail (voir le lien en bas de page) au(x) responsable(s) de la rubrique Rust ou mieux en postant votre proposition à la publication.
Vous pouvez participer à l'amélioration des questions réponses en postant directement dans cette FAQ. Une validation par le(s) responsable(s) de la rubrique Rust sera requise avant que vos contributions ne soient visibles.
Sur ce, nous vous souhaitons une bonne lecture.
- Comment s'effectue la gestion des erreurs avec Rust ?
- Est-il possible de créer des assertions ?
- Comment créer un type spécifique d'exceptions ?
- À quoi sert la macro panic! ?
- À quoi sert la méthode unwrap ?
- À quoi sert la méthode unwrap_or ?
- À quoi sert la méthode unwrap_or_else ?
- À quoi sert la méthode map ?
- À quoi sert la méthode and_then ?
- À quoi sert la macro try! ?
- Comment utiliser la macro assert! ?
- Comment utiliser la macro assert_eq! ?
- Comment utiliser la macro debug_assert! ?
- Qu'est-ce que l'énumération Option<T> ?
- Comment utiliser l'énumération Option<T> ?
- Qu'est-ce que l'énumération Result<T, E> ?
- Comment utiliser l'énumération Result<T, E> ?
Tout comme les langages impératifs classiques (e.g. C), Rust ne gère pas les erreurs grâce à un système « d'exceptions » comme nous pourrions le retrouver dans des langages plus orientés objet, mais grâce au contenu renvoyé en sortie de fonction.
Plusieurs fonctions (et macros) sont d'ailleurs dédiées à cette gestion (e.g. panic!, unwrap (et ses dérivés), and_then) permettant ainsi de rattraper (d'une manière plus ou moins fine) la situation lorsque les conditions imposées par vos soins ne sont pas respectées.
Cette section regroupe donc un certain nombre de Q/R qui pourraient vous aider à mieux cerner ce système de gestion :
Toutes les Q/R taguées avec l'icône sont considérées comme « en cours de rédaction ». Si vous rencontrez un problème de lecture avec ces Q/R inachevées, ne remontez pas le problème tant qu'elles disposent de ce tag. Merci.
- À quoi sert la macro panic! ?
- À quoi sert la méthode unwrap ?
- À quoi sert la méthode unwrap_or ?
- À quoi sert la fonction unwrap_or_else ?
- À quoi sert la méthode map ?
- À quoi sert la méthode and_then ?
- À quoi sert la macro try! ?
- Comment utiliser la macro assert! ?
- Comment utiliser la macro assert_eq! ?
- Comment utiliser la macro debug_assert! ?
- Qu'est-ce que la structure Option<T> ?
- Comment utiliser la structure Option<T> ?
- Qu'est-ce que la structure Result<T, E> ?
- Comment utiliser la structure Result<T, E> ?
Oui, bien entendu.
Il existe trois assertions différentes en Rust (toutes encapsulées par une macro) :
1. assert!;
2. assert_eq!;
3. debug_assert!.
Il n'est pas possible de créer une structure censée représenter un type d'erreur, comme nous pourrions le faire en Java; Rust ne gère pas les potentielles erreurs de cette manière.
La macro panic! pourrait être comparée aux exceptions RuntimeException en Java qui sont, à coup sûr, des erreurs bloquantes.
Code Java : | Sélectionner tout |
1 2 3 4 5 6 7 8 | public class MyClass { public static void main(String[] args) { throw new RuntimeException("Error !"); System.out.println("Dead code."); } } |
Elle est donc la macro ayant le plus bas niveau parmi les macros et/ou fonctions proposées par la bibliothèque standard ; elle ne prend rien en compte mis à part l'arrêt du programme et l'affichage de la trace de la pile.
Code Rust : | Sélectionner tout |
1 2 3 4 5 | fn main() { panic!("Error !"); println!("Dead code"); } |
La méthode unwrap() permet de récupérer la donnée contenue par son wrapper et de faire abstraction des « cas d'analyse » avant de la délivrer.
Autrement dit, la méthode unwrap() délivre la donnée enveloppée si l'instance vaut Some() ou Ok(), sinon plante le programme si elle vaut None ou Err().
Code Rust : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | fn main() { let foo : Option<String> = Some("ça passe!".to_string()); let bar : Option<String> = None; let baz : Result<String, String> = Ok("ça passe!".to_string()); let bing : Result<String, String> = Err("ça casse!".to_string()); println!("{} {} {} {}", foo.unwrap(), bar.unwrap(), baz.unwrap(), bing.unwrap()); } |
Tester l'exemple (pensez à isoler les appels de la méthode si vous ne souhaitez pas faire planter votre programme.)
Qu'est-ce que la structure Option<T> ?
Qu'est-ce que la structure Result<T, E> ?
La méthode unwrap_or() fonctionne exactement comme la méthode originelle, mais elle permet d'éviter de faire « paniquer » le programme, et donc l'arrêt de l'exécution, en nous permettant de passer une valeur par défaut à renvoyer si le wrapper visé ne contient rien initialement.
Code Rust : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 | fn main() { let foo : Option<String> = Some("ça passe!".to_string()); let bar : Option<String> = None; let baz : Result<String, String> = Ok("ça passe!".to_string()); let bing : Result<String, String> = Err("ça casse!".to_string()); println!("{} {} {} {}", foo.unwrap(), bar.unwrap_or(String::from("ça passe, mais de justesse !")), baz.unwrap(), bing.unwrap_or(String::from("On évite de faire planter le programme."))); /* Pensez à isoler les appels de la méthode si vous ne souhaitez pas faire planter votre programme. */ } |
Tester l'exemple
La méthode unwrap_or_else fonctionne exactement comme unwrap_or, mais proposera de passer en paramètre une fonction à la place d'une simple donnée.
Attention, Option<T> et Result<T, E> l'implémentent toutes les deux, mais pas de la même manière.
L'une l'implémente de manière à passer en paramètre une closure, l'autre à passer une fonction déclarée et identifiée dans un premier temps.
Code Rust : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | fn bang(arg: String) -> String { return "Chef, on a eu une erreur: ".to_string() + arg.as_str(); } fn main() { let foo : Option<String> = Some("ça passe!".to_string()); let bar : Option<String> = None; let baz : Result<String, String> = Ok("ça passe!".to_string()); let bing : Result<String, String> = Err("ça casse!".to_string()); bar.unwrap_or_else(|| { return "On évite la casse !".to_string(); }); println!("{}", bing.unwrap_or_else(bang)); } |
Note : le paramètre que reçoit la fonction bang n'est ni plus ni moins ce que vous avez renseigné dans le constructeur de l'instance Err() bing. Gardez cela en tête lorsque vous souhaiterez effectuer des opérations sur ce paramètre dans le corps de votre fonction.
À quoi sert la fonction unwrap_or ?
Elle permet de modifier la donnée contenue. Exemple :
Code Rust : | Sélectionner tout |
1 2 3 4 5 6 7 8 | fn main() { let foo = vec![1, 2, 3]; let foo2: Vec<_> = foo.iter().map(|entry| format!("> {}", entry)).collect(); for entry in foo2 { println!("-> \"{}\"", entry); } } |
Ce qui affichera :
Code : | Sélectionner tout |
1 2 3 | -> "> 1" -> "> 2" -> "> 3" |
La méthode and_then() permet d'effectuer des opérations sur la structure qui l'implémente, puis renvoie une nouvelle instance de cette dernière.
Code Rust : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | fn concat(arg: &str) -> Option<String> { Some(arg.to_string() + "world!") } fn main() { let foo = Some("Hello "); println!("{}", foo.and_then(concat).unwrap()); } |
Actuellement, les structures qui implémentent la méthode and_then() sont :
- Option<T>;
- Result<T, E>;
À quoi sert la méthode unwrap() ?
Qu'est-ce que la structure Result<T, E> ?
Qu'est-ce que la structure Option<T> ?
La macro try! permet de s'assurer de l'intégrité de la ressource.
Si la ressource enveloppée par la macro try! est intègre, elle sera bindée à l'identificateur qui lui est assigné.
Sinon, try! effectue un retour, renvoi prématuré.
Note
Attention toutefois à ne pas oublier qu'une fonction usant de cette macro doit forcément renvoyer une instance de Result<(), io::Error> (le type de la valeur renvoyée en cas de succès est arbitaire).
Code Rust : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 | fn foo(string: &String) -> Result<(), std::io::Error> { try!(std::fs::File::create("my_file.txt")); println!("Une chance sur deux pour que je sois du code mort !"); Ok(()) } fn bar(string: &String) -> std::io::Result<()> { // fonctionne également avec l'alias de Result<T, E> try!(std::fs::File::create("my_file.txt")); println!("Une chance sur deux pour que je sois du code mort !"); Ok(()) } |
Note(bis)
Depuis la version 1.13, la macro try! a été plus ou moins remplacée par l'opérateur ?.
Elle peut toujours être utilisée, toutefois, privilégiez cet opérateur autant que possible.
L'exemple ci-dessus peut donc être transposé de cette manière:
Code Rust : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | use std::io::Error; fn foo(string: &String) -> Result<(), Error> { std::fs::File::create("my_file.txt")?; println!("Une chance sur deux pour que je sois du code mort !"); Ok(()) } fn bar(string: &String) -> std::io::Result<()> { // fonctionne également avec l'alias de Result<T, E> std::fs::File::create("my_file.txt")?; println!("Une chance sur deux pour que je sois du code mort !"); Ok(()) } |
La macro assert! capture deux types « d'expressions » différents :
Les expressions à proprement parler, qui pourraient être illustrées par les exemples suivants :
Code Rust : | Sélectionner tout |
2 * 2, if … else …, foo() ;
Les « tokens tree » qui pourraient être illustrés par n'importe quoi d'autres figurant dans la syntaxe du langage. (puisque, dans l'absolu, le compilateur représente tout ce qui est rédigé dans les fichiers sources grâce à une nomenclature bien à lui)
Donc si nous récupérons le code source raccourci de la documentation, cela donne ceci :
Code Rust : | Sélectionner tout |
1 2 3 4 5 | macro_rules! assert { ( $ cond : expr ) => { ... }; ( $ cond : expr , $ ( $ arg : tt ) + ) => { ... }; } |
Si certaines choses vous échappent, n'hésitez pas à vous rendre sur les liens proposés en bas de cette Q/R.
À quoi sert le second paramètre ?
Le second peut, par exemple, accueillir un message personnalisé pour la macro panic! facilitant ainsi le débogage.
Code Rust : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 | fn foo(arg: Option<String>) -> () { let bar : String = String::from("Hello world!"); let mut some : Option<String> = None; assert!(!arg.is_none(), "Arg is None"); assert!(arg.unwrap().eq(&bar), "arg n'est pas égal à bar"); } fn main() -> () { foo(Some("Ok".to_string())); foo(None); } |
Visionner le résultat de l'exemple (requiert une connexion internet) ;
Comment utiliser une macro ?
« assert_eq! » est un dérivé de la macro « assert! » et permet de tester directement l'égalité de deux objets1.
Bien entendu, elle hérite également du message personnalisé pour la macro « panic! ».
Code Rust : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 | fn foo(arg: Option<String>) -> () { let bar : String = String::from("Hello world!"); let mut some : Option<String> = None; assert!(!arg.is_none(), "Arg is None"); assert_eq!(arg.unwrap(), bar, "arg n'est pas égal à bar"); } fn main() -> () { foo(Some("Ok".to_string())); foo(None); } |
1. Le terme « objet » est ici utilisé pour désigner toutes les entités pouvant être comparées à d'autres (cela ne concerne donc pas que les instances des structures).
Visionner le résultat de l'exemple (requiert une connexion internet) ;
Comment utiliser une macro ?
Où puis-je l'utiliser ?
« debug_assert! » ainsi que ses dérivées (« debug_assert_eq! ») ne sont compilées que lorsque le code source est compilé en mode débogage (mode par défaut de rustc).
Vous ne devez pas compter sur ces assertions pour contrôler le flux de votre programme en production, assurez-vous toujours d'avoir une assertion compilée en mode release.
Si vous souhaitez toutefois les utiliser dans un binaire optimisé, vous devez passer l'argument -C debug-assertions au compilateur.
En dehors du contexte dans lequel ces assertions doivent être déclarées, la manière dont elles sont utilisées ne change pas.
Option est une énumération contenant deux constructeurs différents : Some(T) et None.
Option est en quelque sorte un wrapper, conteneur permettant de vérifier l'intégrité des données contenues.
Pour utiliser les variantes de l'énumération, il faut savoir à quoi elles correspondent.
- Some(T) représente un binding valide.
- None représente un binding invalide.
Code Rust : | Sélectionner tout |
1 2 3 4 5 | fn main() { let foo : Option<String> = Some(String::from("Binding valide")); let bar : Option<String> = None; //binding invalide, ne contient rien } |
Result<T, E> est une énumération contenant deux variantes :
1. Ok(T),
2. Err(E).
Elle permet de gérer convenablement les cas où l'entrée T ne correspond pas à nos attentes et ainsi le communiquer au reste du programme pour que l'incident soit rattrapé plus loin si besoin.
L'utilisation de cette énumération requiert quelques notions quant à la gestion des erreurs avec Rust; Ce dernier ne permettant pas l'utilisation des exceptions, cette structure vous permettra de conserver l'entrée si elle correspond à vos attentes, ou le message d'erreur si quelque chose ne s'est pas passé correctement.
Voici un exemple simple de gestion d'erreur :
Code Rust : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | fn foo<'a, 'b>(arg: Option<&'a str>) -> Result<String, &'b str> { if let Some(content) = arg { let unwrapping = arg.unwrap(); Ok(unwrapping.to_string()) } else { Err("L'argument ne contient rien.") } } fn main() { match foo(None) { Ok(content) => println!("Ok: {}", content), Err(err) => println!("Error: {}", err.to_string()), } } |
Proposer une nouvelle réponse sur la FAQ
Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour çaLes sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2024 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.