¿Por qué estás aquí? Sé honesto
\n\nEstás escribiendo en Rust y de repente te topas con un muro. Tienes un Option<String> — algún valor que puede estar presente o no. Y la función que quieres llamar requiere Option<&str>. El compilador te mira con reproche, mostrando un error que te dan ganas de cerrar la laptop e irte a tomar un té.
No te preocupes. Esta situación es tan común que la pregunta "Converting from Option<String> to Option<&str>" en Stack Overflow ha acumulado miles de visitas. No estás solo.
\n\nEn este artículo entenderemos por qué esta conversión genera tantas preguntas y — lo más importante — cómo hacerla correctamente. Sin magia, sin copias innecesarias de memoria, con una comprensión total de lo que sucede bajo el capó.
\n\nEl problema: ¿por qué no se puede simplemente convertir?
\n\nPrimero, un vistazo rápido a los tipos.
\n\n- \n
String— es un tipo propietario. Posee los datos en el montón y es responsable de liberarlos. \n &str— es una referencia a un segmento de cadena. Toma prestados los datos, sin poseerlos. \n
Y aquí empieza la diversión. Option<String> es posiblemente una cadena que posees. Option<&str> es posiblemente una referencia a una cadena.
El problema es que no puedes simplemente tomar una referencia al valor dentro de un Option y devolverla al exterior si el Option es temporal. La referencia debe apuntar a algo, y ese "algo" debe vivir el tiempo suficiente.
Veamos un error típico de principiante:
\n\nfn process(value: Option<String>) {\n // ¡Error! cannot return reference to temporary value\n let ref_value: Option<&str> = value.as_ref().map(|s| s.as_str());\n // o aún más directamente:\n let ref_value: Option<&str> = Some(value.unwrap().as_str()); // ¡BUM!\n}\n\nEl compilador explota en rojo. ¿Por qué? Porque value es el propietario de la cadena. Cuando intentas tomar un &str de ella, la referencia debe vivir no más que el propio value. Y si value es un objeto temporal (por ejemplo, el resultado de una llamada a función), la referencia se convierte en una referencia colgante (dangling reference).
Solución n.º 1: as_deref() — tu nuevo mejor amigo
\n\nLa forma más elegante de convertir Option<String> en Option<&str> es el método as_deref().
¿Cómo funciona? as_deref() hace dos cosas:
- \n
- Toma
&Option<T>y lo convierte enOption<&T>(esto lo haceas_ref()). \n - Luego aplica
Derefal valor interno, convirtiendo&Stringen&str. \n
Así se ve en código:
\n\nfn main() {\n let my_string: Option<String> = Some(String::from("¡Hola, Rust!"));\n \n // Magia en una línea:\n let my_str_ref: Option<&str> = my_string.as_deref();\n \n match my_str_ref {\n Some(s) => println!("Cadena: {}", s),\n None => println!("No hay cadena"),\n }\n // ¡my_string sigue vivo y coleando!\n println!("Cadena original: {:?}", my_string);\n}\n\nObserva: my_string sigue siendo el propietario de los datos. Simplemente tomamos una referencia a su contenido. Esto significa que my_str_ref solo vive mientras vive my_string.
¿Y si Option está vacío?
\nMuy simple:
\n\nfn main() {\n let empty: Option<String> = None;\n let result: Option<&str> = empty.as_deref();\n println!("{:?}", result); // None\n}\n\nSin pánicos, sin fallos. Funciona como un reloj.