Cómo convertir Option<String> en Option<&str> en Rust sin dolor

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

¿Por qué estás aquí? Sé honesto

\n\n

Está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é.

\n\n

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\n

En 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\n

El problema: ¿por qué no se puede simplemente convertir?

\n\n

Primero, 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
\n\n

Y aquí empieza la diversión. Option<String> es posiblemente una cadena que posees. Option<&str> es posiblemente una referencia a una cadena.

\n\n

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.

\n\n

Veamos un error típico de principiante:

\n\n
fn 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\n

El 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).

\n\n

Solución n.º 1: as_deref() — tu nuevo mejor amigo

\n\n

La forma más elegante de convertir Option<String> en Option<&str> es el método as_deref().

\n\n

¿Cómo funciona? as_deref() hace dos cosas:

\n
    \n
  1. Toma &Option<T> y lo convierte en Option<&T> (esto lo hace as_ref()).
  2. \n
  3. Luego aplica Deref al valor interno, convirtiendo &String en &str.
  4. \n
\n\n

Así se ve en código:

\n\n
fn 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\n

Observa: 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.

\n\n

¿Y si Option está vacío?

\n

Muy 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

Sin pánicos, sin fallos. Funciona como un reloj.

Blogs

Book Recommendations