« Rust : Error handling » : différence entre les versions
(Page créée avec « <nowiki> fn main() { //Panic ! (Erreurs irrécupérables) //On peut causer une panique en faisant une action qui est une erreur, //ou avec la macro panic!(). //abort ou unwind //Lors d'une panique, par défaut, Rust va remonter la stack pour //libérer la mémoire (unwind). //On peut modifier ça en choisissant d'abort (ne rien faire) à la place //; c'est utile pour diminuer la taille du binaire. //Pour ça, ajouter la... ») |
Aucun résumé des modifications |
||
Ligne 1 : | Ligne 1 : | ||
<syntaxhighlight lang='rust'> | |||
fn main() { | fn main() { | ||
//Panic ! (Erreurs irrécupérables) | //Panic ! (Erreurs irrécupérables) | ||
Ligne 154 : | Ligne 154 : | ||
</ | </syntaxhighlight> |
Version du 3 novembre 2022 à 09:20
<syntaxhighlight lang='rust'> fn main() {
//Panic ! (Erreurs irrécupérables) //On peut causer une panique en faisant une action qui est une erreur, //ou avec la macro panic!(). //abort ou unwind //Lors d'une panique, par défaut, Rust va remonter la stack pour //libérer la mémoire (unwind). //On peut modifier ça en choisissant d'abort (ne rien faire) à la place //; c'est utile pour diminuer la taille du binaire. //Pour ça, ajouter la section suivante dans Cargo.toml //[profile.release] //panic = 'abort'
//Panique panic!("Crash and burn");
//On peut agir sur la backtrace d'erreur via la variable d'environnement //RUST_BACKTRACE (en la mettant à 1).
//Result (Erreurs récupérables) // //Pour rappel, l'enum Result a la forme suivante: //enum Result<T, E> { // Ok(T), // Err(E) //} //T et E sont des types génériques. T est la valeur qui sera retournée //sur un succès, E le type d'erreur.
}
use std::fs::File;
fn open_file() {
let greeting_file_result = File::open("hello.txt"); //Le retour de File::open est un Result<T, E>. T sera ici un handler de //fichier std::fs:File, E sera un std::io::Error.
//On rajoute du code de gestion d'erreur: let greeting_file = match greeting_file_result { Ok(file) => file, Err(error) => panic!("Problem opening the file : {:?}", error), };
}
//On peut vouloir régir différement en fonction des types d'erreurs.
use std::io::ErrorKind;
fn open_file_2() {
let greeting_file_result = File::open("hello.txt");
let greeting_file = match greeting_file_result { Ok(file) => file, Err(error) => match error.kind() { ErrorKind::NotFound => match File::create("hello.txt") { Ok(fc) => fc, Err(e) => panic!("Coulnd not create the file : {:?}", e) }, //Ici, on est sur le bras catch-all : other_error est juste //un nom de variable au pif... other_error => { panic!("Problem opening the file {:?}", other_error) } } };
}
//match c'est cool, mais en fait pas tellement. //On peut utiliser unwrap(), qui signifie : //Si Ok, renvoie la valeur associée //Si Err, panic!
fn blablabla() {
let greeting_file = File::open("hello.txt").unwrap();
}
//On a aussi expect. Expect fait un peu la même chose mais permet //de choisir le message de panic (Super, ça valait vraiment le coup //de faire 2 méthodes ?). C'est très souvent le meilleur choix.
fn monculsurlacommode() {
let greeting_file = File::open("hello.txt") .expect("hello.txt should be included in this project");
}
//Propagation d'erreurs. //Quand une fonction rencontre une erreur, elle peut renvoyer son erreur //au lieu de la traiter elle-même; c'est la propagation. //On peut se contenter de renvoyer un result depuis la fonction si on veut: //Ainsi on renvoie soit un result(Ok) avec une valeur, soit Result<Err> //avec une erreur. // //Mais on a un shortcut : le ? // //Exemple SANS le ?
fn read_username_from_file() -> Result<String, io::Error> {
let username_file_result = File::open("hello.txt");
let mut username_file = match username_file_result { Ok(file) => file, Err(e) => return Err(e), };
let mut username = String::new();
match username_file.read_to_string(&mut username) { Ok(_) => Ok(username), Err(e) => Err(e), }
}
//Exemple avec le ? //Le résultat est le même qu'avec les match de la fonction au-dessus. //Quand l'opérateur ? est utilisé, le type d'erreur reçu est converti //en fonction du type d'erreur donné dans la signature de la fonction. fn tropsuperlagestionderreur() -> Result<String, io::Error> {
//Ici, le ? signifie: //Si OK : Renvoyer la valeur V dans Ok(V) à username_file; //Si Err : Renvoyer l'erreur au code appellant la fonction. //? ne peut être utilisé que si la fonction renvoie un result //ou un Option<T> let mut username_file = File::open("hello.txt")?; let mut username = String::new(); username_file.read_to_string(&mut username)?; Ok(username) //On peut même enchaîner derrière un ? //File::open("hello.txt")?.read_to_string(&mut username)?; //Ok(username)
}
//Et encore plus court fn read_username_from_file2() -> Result<String, io::Error> {
fs::read_to_string("hello.txt")
}
//Fonctionne aussi avec un Option<T> (Some, None, tout ça) fn last_char_of_first_line(text: &str) -> Option<char> {
//Ici next() renvoie un Option<str> //Va renvoyer None si il n'ya rien (sans prendre en compte //chars().last() et donc pas d'erreur), //Va extraire le str et le filer à la méthode d'après (chars()) //sinon text.lines().next()?.chars().last()
}
//On peut donc utiliser ? sur un Result ou une Option, mais //il ne va pas convertir l'un en l'autre : on doit respecter la //signature de la fonction. On peut alors utiliser ok() sur un result ou //ok_or() sur une option. // //BTW, une fonction main peut renvoyer des erreurs, que l'on peut d'ailleurs gérer //avec ?; si c'est le cas, l'exécutable renverra 0 si Ok et autre chose si erreur.
</syntaxhighlight>