Enums et gestion d'erreurs (Result et Option)
Rust exige toujours de gérer les cas où il y aurait une erreur d'une manière ou d'une autre. Toute commmande qui pourrait donner une erreur rendera un type Result<> et toute commande qui pourrait ne donner rien retournera un type Option<>.
Il y a plusieurs manières de gérer ce genre de types :
- Avec
.unwrap()
, non-recommendé. Fait planter le programme quand il y a une erreur (pour les Result) ou quand il n'y a rien (dans les Option) - Avec
.expect("message")
, fait également planter le programme, mais indique un message d'erreur personalisé. - Avec
.unwrap_or(valeur)
ou encore.unwrap_or_else(|c| { valeur })
, dans le cas d'un None ou d'une erreur, il va retourner une valeur par défault à la place - Avec
match
ouif let
pour faire des choses pour chaque cas ou pour définir une valeur par défault - Avec
?
dans une fonction et va donc créer une erreur au niveau du retour de la fonction en elle même
// Cette fonction peut retourner un booléen si tout se passe bien et peut planter avec un &str
fn beverage_checker(beverage: &str) -> Result<bool, &str> {
if beverage == "water" {
Ok(true)
} else if beverage == "coffee" {
Err("Wait, you actually drink that???")
} else {
Ok(true)
}
}
fn main() {
let mut status = beverage_checker("water").expect("Le développeur de ce programme n'aimais vraiment pas cette boisson");
println!("Le status de 'water' est {}", status);
// Ici on va remplacer l'erreur par un false avec unwrap_or
status = beverage_checker("coffee").unwrap_or(false);
println!("Le status de 'coffee' est {}", status);
// On peut aussi utiliser unwrap_or_else pour exécuter du code dans une closure en plus de retourner une autre valeur
status = beverage_checker("coffee").unwrap_or_else(|err| {
println!("Message: {}", err);
false
});
println!("Le status de 'coffee' est {}", status);
// Sinon on peut encore utiliser un match dans lequel on exécute du code
match beverage_checker("coffee") {
Ok(s) => println!("Le status de 'coffee' est {}", s),
Err(e) => println!("Voici le message du développeur : {}", e)
}
// On peut aussi faire similaire à précédemment avec un if let
if let Ok(s) = beverage_checker("coffee") {
println!("Le status de 'coffee' est {}", s);
} else {
println!("Le développeur n'aime pas le café");
}
// Ou encore utiliser match comme on la fait avec unwrap_or ou unwrap_or_else
status = match beverage_checker("coffee") {
Ok(s) => s,
Err(e) => {
println!("Voici le message du développeur : {}", e);
false
}
};
println!("Le status de 'coffee' est {}", status);
// Cette ligne va planter le programme :
status = beverage_checker("coffee").expect("Le développeur de ce programme n'aimais vraiment pas cette boisson");
println!("Le status de 'coffee' est {}", status);
// Cette ligne planterais le programme sans message d'erreur
status = beverage_checker("coffee").unwrap();
println!("Le status de 'coffee' est {}", status);
}
Enum
Result et Option sont tous les deux des enums que voici :
pub enum Result<T, E> {
Ok(T),
Err(E),
}
pub enum Option<T> {
None,
Some(T),
}
Un enum permet d'énumérer plusieurs variantes. Voici un exemple d'enum personalisé
enum IpAddrKind {
V4,
V6,
}
fn main() {
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
}
Maintenant on peut aussi avoir des valeurs dans un enum
enum IpAddrKind {
V4(String),
V6(String),
}
fn main() {
let four = IpAddrKind::V4("127.0.0.1".to_string());
let six = IpAddrKind::V6("::1".to_string());
}
Et quand on ne sait pas encore les types qui vont être compris dans notre enum on peut utiliser cette notation :
enum IpAddrKind<A, B> {
V4(A),
V6(B),
}
fn main() {
let four: IpAddrKind<String, String> = IpAddrKind::V4("127.0.0.1".to_string());
let six: IpAddrKind<String, String> = IpAddrKind::V6("::1".to_string());
}
On peut aussi gérer les enums avec des match
ou encore avec des if let
enum IpAddrKind<A, B> {
V4(A),
V6(B),
}
fn main() {
let four: IpAddrKind<&str, &str> = IpAddrKind::V4("127.0.0.1");
let six: IpAddrKind<&str, &str> = IpAddrKind::V6("::1");
// En utilisant match
match four {
IpAddrKind::V4(ip) => println!("ipv4: {}", ip),
IpAddrKind::V6(ip) => println!("ipv6: {}", ip)
}
match six {
IpAddrKind::V4(ip) => println!("ipv4: {}", ip),
IpAddrKind::V6(ip) => println!("ipv6: {}", ip)
}
// En utilisant if let
if let IpAddrKind::V4(ip) = four {
println!("ipv4: {}", ip);
}
if let IpAddrKind::V6(ip) = six {
println!("ipv6: {}", ip);
}
}
No Comments