Lección 1
Ownership y movimientos
Comprende qué sucede cuando asignas, pasas o devuelves valores en Rust.
Por qué existe la propiedad
Un String reserva un buffer en el heap. Ese buffer debe liberarse una sola vez:
si no se libera, se desperdicia memoria; si se libera dos veces o demasiado pronto,
el programa puede acceder a memoria que ya no es válida.
Rust evita ese problema sin recolector de basura y sin exigir llamadas manuales a
free. Cada valor tiene una única dueña —la variable que lo posee— y, cuando esa
dueña sale de scope, Rust ejecuta drop y libera los recursos asociados.
Por eso let s2 = s1; no duplica el buffer de un String: transfiere su propiedad
a s2 e invalida s1. Eso es un movimiento. Los tipos Copy sí se duplican
de forma implícita; clone crea una duplicación explícita, y una referencia permite
usar el valor temporalmente sin convertirse en su dueña.
Qué cambia respecto a otros lenguajes
La misma asignación no expresa lo mismo en todos los lenguajes. Si a permite
acceder a un valor que usa memoria dinámica, esto es lo habitual:
| Lenguaje | Al ejecutar b = a |
Liberación del recurso |
|---|---|---|
| TypeScript / Java | Se copia una referencia gestionada. a y b permiten acceder al mismo objeto. |
El recolector de basura actúa cuando el objeto deja de ser alcanzable. |
| C++ | Un tipo como std::string se copia. Para permitir un movimiento puede usarse b = std::move(a); después, a sigue siendo válido, pero su estado puede haber cambiado. |
Cada objeto libera sus recursos desde su destructor siguiendo RAII; normalmente no se usa delete directamente. |
| Rust | Un tipo como String se mueve: b recibe el valor y a deja de poder utilizarse. Para duplicar también el buffer hay que escribir a.clone(). |
El recurso se libera mediante drop cuando sale de scope su dueña actual. |
Rust sí permite varias referencias compartidas &T. Lo que el préstamo impide
en código seguro es combinar acceso mutable con otros accesos al mismo valor:
puede haber varias referencias compartidas o una referencia mutable, pero no
ambas cosas a la vez. El compilador comprueba esta regla y también que ninguna
referencia sobreviva al valor al que apunta.
Ejercicio 01 · Ownership
Completa dos funciones sin consumir los valores de entrada.
En ambas funciones solo necesitas leer los argumentos. Recíbelos por referencia, completa el código y ejecuta las pruebas.
Objetivo
Implementa saludo_mas_largo para devolver uno de los textos recibidos por referencia. Implementa crear_saludo para generar un String nuevo sin consumir el argumento de entrada.
Implementa
Completa una función en cada editor.
Función 01
Seleccionar el texto más largo
Recibe dos &str y devuelve el que tenga mayor longitud.
/// Devuelve el saludo más largo sin tomar la propiedad.
pub fn saludo_mas_largo(a: String, b: String) -> String {
todo!("devuelve a o b")
}Función 02
Construir un String nuevo
Recibe un &str y crea un saludo sin consumir el valor de entrada.
/// Construye un saludo nuevo sin consumir el nombre.
pub fn crear_saludo(nombre: String) -> String {
todo!("construye un String nuevo")
}Comprueba
Ejecuta las pruebas y corrige el código a partir del diagnóstico de Rust.
Pruebas visibles
Modifica el código antes de ejecutar las pruebas.
Ayuda
Consulta una pista o compara tu implementación con la solución.
/// Devuelve el saludo más largo sin quedarse con la propiedad
/// de ninguno de los dos: solo pide prestado.
#[must_use]
pub fn saludo_mas_largo<'a>(a: &'a str, b: &'a str) -> &'a str {
if a.len() >= b.len() { a } else { b }
}
/// Crea un saludo propio a partir de un nombre prestado.
#[must_use]
pub fn crear_saludo(nombre: &str) -> String {
let mut saludo = String::from("hola, ");
saludo.push_str(nombre);
saludo
}
// Playground entry point for this example.
fn main() {
let corto = "hey";
let largo = "buenas tardes";
println!("{}", saludo_mas_largo(corto, largo));
println!("{}", crear_saludo("rusticiero"));
}Resumen
- Cada valor tiene una única dueña (la variable que lo posee); al salir de scope, el valor se libera automáticamente.
- Asignar o pasar un valor con datos en el heap mueve la propiedad; la variable anterior queda inválida.
clone()crea una copia profunda explícita cuando de verdad necesitas dos dueñas.- Los tipos
Copy(sin heap) se copian en la asignación y ambas variables siguen vivas. - El compilador convierte los errores de memoria en errores de compilación con mensajes accionables.