De Option à Result en Rust : tutoriel sans macros

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Pourquoi en avez-vous assez d'écrire des macros pour Option en Result ?

Vous codez en Rust, et soudain vous réalisez : vous avez un Option<T>, mais il vous faut un Result<T, E>. Cela semble anodin. Mais si vous êtes débutant, votre première pensée est : « Il me faut probablement une macro ! » Ou bien vous êtes un développeur expérimenté, mais vous écrivez toujours la même chose : match option { Some(v) => Ok(v), None => Err("error") }. Avouez que cela devient lassant.

La question sur Stack Overflow « Existe-t-il un moyen de simplifier la conversion d'un Option en Result sans macro ? » a recueilli des milliers de vues. Ce n'est pas un hasard : ce problème est familier à tous ceux qui travaillent avec Rust. Mais la bonne nouvelle, c'est qu'il existe une solution, et elle est étonnamment simple. Dans ce tutoriel, nous allons explorer toutes les méthodes, des plus basiques aux plus élégantes, sans une seule macro.

Nous ne nous contenterons pas de donner la réponse — nous vous montrerons comment penser comme un développeur Rust. C'est parti !



Que sont Option et Result en Rust ?

Avant de convertir, assurons-nous d'être sur la même longueur d'onde.

  • Option<T> — est un type qui peut être soit Some(T) (une valeur est présente), soit None (aucune valeur). Il est utilisé lorsque l'absence de valeur est une situation normale (par exemple, la recherche d'un élément dans une collection).
  • Result<T, E> — est un type qui peut être soit Ok(T) (tout va bien), soit Err(E) (une erreur s'est produite). Il est utilisé pour les opérations qui peuvent échouer (par exemple, la lecture d'un fichier).

Le problème : parfois vous avez un Option, mais selon la logique du programme, l'absence de valeur est une erreur. C'est là que la conversion est nécessaire.



Méthode de base : match (mais vous le savez déjà)

La méthode la plus évidente et souvent utilisée par les débutants est match. Oui, elle fonctionne, mais elle est verbeuse :

fn get_user_name(id: u32) -> Option<String> {    // ... une certaine logique    if id == 1 { Some("Alice".to_string()) } else { None }}

fn main() { let user_id = 2; let name = match get_user_name(user_id) { Some(name) => Ok(name), None => Err("User not found"), }; println!("{:?}", name); // Err("User not found")}

Ce code est correct, mais si vous avez des dizaines de conversions de ce type dans votre projet, vous vous lasserez d'écrire match. De plus, il est facile de faire une erreur et d'oublier de traiter le cas None. Il nous faut quelque chose de plus concis.



La méthode ok_or : votre nouveau meilleur ami

La bibliothèque standard de Rust fournit la méthode ok_or pour Option. Elle fait exactement ce dont nous avons besoin : elle convertit Some(v) en Ok(v), et None en Err(error), où error est la valeur que vous passez.

fn main() {    let user_id = 2;    let name = get_user_name(user_id).ok_or("User not found");    println!("{:?}", name); // Err("User not found")}

Une seule ligne ! Mais il y a un détail : ok_or prend une valeur par possession (owned value). Si vous passez un littéral de chaîne, comme dans l'exemple, cela fonctionne car &str implémente Into<String>. Mais si vous devez passer une erreur déjà stockée ailleurs, cela peut entraîner une copie inutile.

Pour ces cas, il existe ok_or_else — il prend une fermeture qui n'est appelée que si Option est None. Cela économise des ressources :

fn main() {    let user_id = 2;    let error_msg = format!("User with id {} not found", user_id);    let name = get_user_name(user_id).ok_or_else(|| error_msg.clone());    // ou encore mieux :    let name = get_user_name(user_id).ok_or_else(|| format!("User {} not found", user_id));    println!("{:?}", name);}

Conseil : utilisez ok_or si le message d'erreur est une constante. Utilisez ok_

Blogs

Book Recommendations