Lecciones del módulo (1/2)
Las reglas de Ownership
El Ownership (propiedad) es la característica más distintiva de Rust. Permite al lenguaje garantizar la seguridad de la memoria sin necesidad de un Garbage Collector ni de obligarte a desasignar manualmente la memoria heap (como en C o C++).
Las Tres Reglas del Ownership
La gestión de la memoria en Rust está gobernada por tres simples reglas controladas rigurosamente por el compilador:
- Cada valor en Rust tiene una variable llamada su propietario (owner).
- Solo puede haber un propietario a la vez.
- Cuando el propietario sale del ámbito (scope), el valor se elimina automáticamente.
Stack vs Heap
Para comprender el ownership, es importante saber dónde residen los datos:
- Stack (Pila): almacena datos de tamaño fijo y conocido en el momento de la compilación (ej. enteros, booleanos). El acceso es extremadamente rápido.
- Heap (Montículo): almacena datos de tamaño dinámico y desconocido en el momento de la compilación (ej.
String). La asignación es más lenta y requiere un puntero almacenado en el Stack.
El concepto de "Move" (Desplazamiento)
Cuando asignamos una variable almacenada en el Heap a otra, Rust mueve (o transfiere) la propiedad del valor, invalidando la primera variable:
let s1 = String::from("ciao");
let s2 = s1; // La proprieta del testo si sposta a s2. s1 NON e piu utilizzabile!
// println!("{}", s1); // ERRORE DI COMPILAZIONE! s1 e "borrow of moved value"
println!("{}", s2); // Valido!
Este comportamiento evita el problema del "double free error" (intentar liberar la misma memoria dos veces al salir del ámbito), ya que solo s2 liberará la memoria.
El concepto de "Clone" (Copia Profunda)
Si tenemos la necesidad explícita de copiar todo el contenido de una String (tanto los punteros en la pila como los datos textuales reales en el montículo), podemos recurrir al método .clone(). Esto duplica completamente los datos en el heap, manteniendo válidas tanto la variable original como la nueva, a costa de un rendimiento debido a la nueva asignación de memoria:
let s1 = String::from("ciao");
let s2 = s1.clone(); // Copia profonda. Entrambe le variabili rimangono valide!
println!("s1: {}, s2: {}", s1, s2);
El concepto de "Copy"
Para los tipos simples guardados completamente en el Stack (como enteros i32, booleanos bool, caracteres char), la asignación realiza una verdadera copia superficial automática, por lo que ambas variables siguen siendo válidas:
let x = 5;
let y = x; // Copia il valore 5 nello stack. Entrambe le variabili sono utilizzabili!
println!("x: {}, y: {}", x, y); // Valido!
Pruébalo tú
Declara una String s1 que contenga el texto 'hello'. Asigna s1 a s2 (para mover la propiedad). Por último, imprime s2 en pantalla usando println!.
Mostrar pista
Usa `let s2 = s1;` para transferir la propiedad a `s2` y imprímela con `println!('{}', s2);`.
Solución disponible después de 3 intentos
Declara una variable entera x con el valor 5. Asigna x a y (realizando una copia). Por último, imprime tanto x como y en la misma instrucción println!.
Mostrar pista
Haz `let y = x;` y luego imprime ambos valores, ej. `println!('{} {}', x, y);`.
Solución disponible después de 3 intentos
Declara una String s1 con el valor "Rust". Crea una copia profunda de s1 en una nueva variable s2 usando el método clone. Por último, imprime tanto s1 como s2 separados por un espacio usando la macro println!.
Mostrar pista
Usa `let s2 = s1.clone();` para copiar en profundidad el valor de `s1` en `s2`, luego imprímelos con `println!("{} {}", s1, s2);`.
Solución disponible después de 3 intentos