El sistema de ownership (propiedad) es el núcleo que permite a Rust garantizar la seguridad de la memoria sin necesidad de un recolector de basura (Garbage Collector). Este sistema se rige por tres reglas fundamentales que el compilador verifica estrictamente mediante un análisis estático en tiempo de compilación:
- Cada valor tiene exactamente un dueño (owner): En cualquier momento dado, hay una única variable responsable de ese valor. No puede haber múltiples dueños para un mismo recurso en memoria.
- Solo puede haber un dueño a la vez: Cuando asignas un valor a una nueva variable, la propiedad se transfiere (se realiza un move). La variable original deja de ser válida. Esto evita el error de double free (intentar liberar la misma memoria dos veces).
- Cuando el dueño sale de su ámbito (scope), el valor es destruido: Al finalizar el bloque de código
{}donde reside el dueño, Rust llama automáticamente al métododrop(mediante el traitDrop), liberando la memoria asociada.
Si estas reglas no se cumplieran, ocurrirían errores críticos como memory leaks (fugas de memoria por no liberar recursos) o use-after-free (intentar usar una dirección de memoria que ya fue liberada). Gracias a este modelo, Rust elimina estos errores en la fase de compilación sin penalizar el rendimiento en ejecución.
fn main() {
// Regla 1: s1 es la única dueña de este String
let s1 = String::from("Hola Rust");
// Regla 2: El ownership se mueve de s1 a s2
// s1 ya no es válida y no puede ser usada
let s2 = s1;
println!("s2 tiene el valor: {}", s2);
// Creamos un nuevo ámbito para demostrar la Regla 3
{
// Regla 1: s3 es la dueña de este nuevo String
let s3 = String::from("Ámbito interno");
println!("s3 vive aquí: {}", s3);
// Al llegar aquí, s3 sale de scope y se ejecuta su destructor
}
// s3 ya no existe en este punto
// Regla 2: Pasamos el ownership de s2 a la función
take_ownership(s2);
// println!("{}", s2); // ERROR: s2 ya no tiene el ownership
}
fn take_ownership(un_valor: String) {
// un_valor es la dueña de la cadena aquí
println!("La función recibió: {}", un_valor);
} // Aquí un_valor sale de scope y su memoria se libera automáticamente
Explicación del Código
let s1 = String::from("Hola Rust");: Se crea un objetoStringen el heap. En este punto,s1es la única dueña (Regla 1).let s2 = s1;: Se produce un movimiento (move). El ownership de la cadena pasa des1as2. Como resultado,s1queda invalidada para evitar que ambas intenten liberar la misma memoria al terminar.println!("{}", s2);: Accedemos al valor a través des2, su nuevo y único dueño.let s3 = String::from("Ámbito interno");:s3se declara dentro de un bloque de código delimitado por llaves{}.- Al cerrar la llave
},s3sale de su ámbito. Se aplica la Regla 3: el compilador libera automáticamente la memoria des3. take_ownership(s2);: Llamamos a la función pasandos2. El ownership se transfiere de la variables2(enmain) a la variableun_valor(en la función).fn take_ownership(un_valor: String): La variableun_valorasume la propiedad del dato. Al finalizar la ejecución de esta función,un_valorsale de su scope y su memoria es liberada.// println!("{}", s2);: Este código fallaría al compilar porques2ya no es la dueña del valor; la propiedad fue movida haciaun_valor.
N° 24