«Use of moved value» en Rust: cómo solucionarlo y no volverse loco

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

«Use of moved value» en Rust: por qué ocurre y cómo vivir con ello

\n\n

Si acabas de empezar a aprender Rust, seguramente ya te has topado con el misterioso mensaje: error[E0382]: use of moved value. El compilador se queja, te rascas la cabeza y el código, que lógicamente es absolutamente correcto, se niega a compilar. ¿Te suena? Bienvenido al mundo de la propiedad y el préstamo: conceptos clave de Rust que lo hacen seguro, pero que a veces desconciertan incluso a desarrolladores experimentados.

\n\n

Este artículo no es documentación árida, sino un análisis vivo del problema. Veremos por qué ocurre el error use of moved value con ejemplos de código reales y, lo más importante, qué hacer al respecto. Aprenderás a pensar como el borrow checker y dejarás de luchar contra él. ¡Allá vamos!

\n\n

¿Qué es un «move» en Rust y por qué el compilador es tan estricto?

\n\n

En Rust, cada valor tiene exactamente un propietario (owner). Cuando asignas un valor a otra variable o lo pasas a una función, la propiedad se mueve (move). Después del movimiento, la variable original deja de ser válida: el compilador prohíbe acceder a ella. Esta es la raíz del error use of moved value.

\n\n

Observa el ejemplo más simple:

\n\n
let s1 = String::from("hola");\nlet s2 = s1; // La propiedad se mueve de s1 a s2\nprintln!("{}", s1); // ERROR: use of moved value: `s1`
\n\n

¿Por qué ocurre esto? String no es una simple copia en la pila. Almacena datos en el montón (heap). Si Rust simplemente copiara el puntero, al liberar la memoria ambas variables intentarían eliminarla: double free. En su lugar, Rust «mueve» la propiedad: ahora s2 es responsable de la memoria y s1 ya no existe desde el punto de vista del compilador. Esta es la idea principal de la semántica de movimiento en Rust.

\n\n

Escenarios típicos que provocan el error

\n\n

El error use of moved value aparece con mayor frecuencia en tres situaciones:

\n\n
    \n
  • Pasar un valor a una función
  • \n
  • Asignar un valor a otra variable
  • \n
  • Usar un valor en un bucle o iterador
  • \n
\n\n

Analicemos cada caso en detalle.

\n\n

1. Pasar a una función sin retorno

\n\n
fn print_string(s: String) {\n    println!("{}", s);\n}\n\nfn main() {\n    let my_string = String::from("Rust");\n    print_string(my_string); // la propiedad se mueve\n    println!("{}", my_string); // ¡error! use of moved value\n}
\n\n

Después de llamar a print_string, la propiedad de my_string se transfiere a la función. Cuando la función termina, s se elimina y my_string ya no es válida. El compilador te protege de usar un objeto muerto.

\n\n

2. Asignación sin copia

\n\n

Ya vimos este ejemplo con String. Pero es importante: no todos los tipos se comportan así. Los tipos simples como i32, bool, f64 implementan el trait Copy. No se mueven, sino que se copian automáticamente.

\n\n
let x = 5;\nlet y = x; // copia, no movimiento\nprintln!("{}", x); // ¡funciona!
\n\n

Los tipos que almacenan datos en el montón (String, Vec, Box) no implementan Copy, solo Clone. Por lo tanto, se mueven.

\n\n

3. Iteración sobre una colección

\n\n

Otra fuente común de errores son los bucles for:

\n\n
let v = vec![1, 2, 3];\nfor x in v { // v se mueve al iterador\n    println!("{}", x);\n}\nprintln!("{:?}", v); // ¡error! use of moved value
\n\n

Cuando escribes for x in v, en realidad estás llamando a into_iter(), que consume el vector. Después del bucle, v ya no existe.

\n\n

Cómo solucionarlo: 4 formas probadas

\n\n

Aho

Blogs

Book Recommendations