Comment transformer Option<String> en Option<&str> en Rust sans douleur

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 êtes-vous ici ? Avouez-le honnêtement

\n\n

Vous écrivez en Rust, et soudain vous vous heurtez à un mur. Vous avez un Option<String> — une valeur qui peut être présente ou non. Et la fonction que vous voulez appeler nécessite un Option<&str>. Et le compilateur vous regarde d'un air réprobateur, en affichant une erreur qui donne envie de fermer son ordinateur portable et d'aller boire du thé.

\n\n

Ne vous inquiétez pas. Cette situation est si courante que la question "Converting from Option<String> to Option<&str>" sur Stack Overflow a recueilli des milliers de vues. Vous n'êtes pas seul.

\n\n

Dans cet article, nous allons comprendre pourquoi cette conversion soulève tant de questions et — surtout — comment la faire correctement. Sans magie, sans copie mémoire inutile, avec une compréhension complète de ce qui se passe sous le capot.

\n\n

Le problème : pourquoi ne peut-on pas simplement convertir ?

\n\n

D'abord, un rapide coup d'œil aux types.

\n\n
    \n
  • String est un type possédant. Il possède les données dans le tas et est responsable de leur libération.
  • \n
  • &str est une référence à une tranche de chaîne. Il emprunte les données, sans les posséder.
  • \n
\n\n

Et c'est là que les choses deviennent intéressantes. Option<String> est une possible chaîne que vous possédez. Option<&str> est une possible référence à une chaîne.

\n\n

Le problème est que vous ne pouvez pas simplement prendre une référence à la valeur à l'intérieur d'une Option et la donner à l'extérieur, si l'Option est temporaire. La référence doit pointer vers quelque chose, et ce "quelque chose" doit vivre assez longtemps.

\n\n

Regardons l'erreur typique d'un débutant :

\n\n
fn process(value: Option<String>) {\n    // Erreur ! cannot return reference to temporary value\n    let ref_value: Option<&str> = value.as_ref().map(|s| s.as_str());\n    // ou encore plus directement :\n    let ref_value: Option<&str> = Some(value.unwrap().as_str()); // BOUM !\n}
\n\n

Le compilateur explose en rouge. Pourquoi ? Parce que value est le propriétaire de la chaîne. Lorsque vous essayez de prendre un &str de celle-ci, la référence ne doit pas vivre plus longtemps que value lui-même. Et si value est un objet temporaire (par exemple, le résultat d'un appel de fonction), la référence devient pendante (dangling reference).

\n\n

Solution n°1 : as_deref() — votre nouveau meilleur ami

\n\n

La manière la plus élégante de convertir Option<String> en Option<&str> est la méthode as_deref().

\n\n

Comment ça marche ? as_deref() fait deux choses :

\n
    \n
  1. Elle prend un &Option<T> et le transforme en Option<&T> (c'est ce que fait as_ref()).
  2. \n
  3. Ensuite, elle applique Deref à la valeur interne, transformant &String en &str.
  4. \n
\n\n

Voici à quoi cela ressemble dans le code :

\n\n
fn main() {\n    let my_string: Option<String> = Some(String::from("Bonjour, Rust !"));\n    \n    // La magie en une seule ligne :\n    let my_str_ref: Option<&str> = my_string.as_deref();\n    \n    match my_str_ref {\n        Some(s) => println!("Chaîne : {}", s),\n        None => println!("Pas de chaîne"),\n    }\n    // my_string est toujours vivant et en bonne santé !\n    println!("Chaîne d'origine : {:?}", my_string);\n}
\n\n

Notez bien : my_string reste le propriétaire des données. Nous avons simplement pris une référence à son contenu. Cela signifie que my_str_ref ne vit que tant que my_string vit.

\n\n

Et si l'Option est vide ?

\n

C'est simple :

\n\n
fn main() {\n    let empty: Option<String> = None;\n    let result: Option<&str> = empty.as_deref();\n    println!("{:?}", result); // None\n}
\n\n

Pas de panique, pas de crash. Ça fonctionne comme sur des roulettes.

Blogs

Book Recommendations