Pourquoi êtes-vous ici ? Avouez-le honnêtement
\n\nVous é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é.
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\nDans 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\nLe problème : pourquoi ne peut-on pas simplement convertir ?
\n\nD'abord, un rapide coup d'œil aux types.
\n\n- \n
Stringest un type possédant. Il possède les données dans le tas et est responsable de leur libération. \n &strest une référence à une tranche de chaîne. Il emprunte les données, sans les posséder. \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.
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.
Regardons l'erreur typique d'un débutant :
\n\nfn 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\nLe 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).
Solution n°1 : as_deref() — votre nouveau meilleur ami
\n\nLa manière la plus élégante de convertir Option<String> en Option<&str> est la méthode as_deref().
Comment ça marche ? as_deref() fait deux choses :
- \n
- Elle prend un
&Option<T>et le transforme enOption<&T>(c'est ce que faitas_ref()). \n - Ensuite, elle applique
Derefà la valeur interne, transformant&Stringen&str. \n
Voici à quoi cela ressemble dans le code :
\n\nfn 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\nNotez 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.
Et si l'Option est vide ?
\nC'est simple :
\n\nfn main() {\n let empty: Option<String> = None;\n let result: Option<&str> = empty.as_deref();\n println!("{:?}", result); // None\n}\n\nPas de panique, pas de crash. Ça fonctionne comme sur des roulettes.