# Ownership en Rust: quién es el dueño de cada valor y cuándo muere

Durante años usé Go para todo lo que no hacía con Rails, y en todo ese tiempo no me hice ni una vez una pregunta que en Rust te haces en cada línea: **¿quién es el dueño de este valor?** En Go no hace falta. Creas un slice, se lo pasas a tres funciones, lo guardas en un struct y lo devuelves en un canal, y el recolector de basura se encarga de liberarlo cuando nadie lo mire. Hoy ese hueco lo ocupa Rust, y la pregunta del dueño es la primera que hay que aprender a responder.

En [Go frente a Rust, fase a fase](/post/go-frente-a-rust-fase-a-fase) dije que el borrow checker es lo que hace lento el primer binario en Rust, y enseñé un error de préstamo. Pero el préstamo es la segunda mitad de la historia. La primera es la **propiedad** (*ownership*), y es lo que voy a contar aquí: las reglas, qué pasa en memoria cuando asignas, cuándo se libera cada cosa y qué haces cuando un valor necesita más de un dueño. El post sobre préstamos sale dentro de dos días.

Todos los fragmentos están compilados con Rust 1.98 y la edición 2024, y los errores son la salida real de `cargo`, recortada donde sobraba.

## Tres maneras de responder a «¿quién libera esto?»

Cualquier lenguaje que reserve memoria dinámica tiene que decidir quién la devuelve.

**C deja la respuesta en tus manos.** Llamas a `malloc`, llamas a `free`, y entre una cosa y otra el compilador no comprueba nada. Si liberas dos veces tienes un *double free*; si usas el puntero después de liberarlo, un *use-after-free*; si se te olvida, una fuga. Las dos primeras no son errores de estilo: son de las fuentes clásicas de vulnerabilidades.

**Go responde en tiempo de ejecución.** El recolector de basura recorre la memoria alcanzable y libera lo demás cuando le parece oportuno. Es cómodo y seguro, y cuesta dos cosas: algo de CPU y de latencia, y no saber **cuándo** se libera algo. Por eso los recursos que no son memoria se cierran a mano con `defer f.Close()`, y por eso la documentación de `runtime.SetFinalizer` y de `runtime.AddCleanup` (Go 1.24) advierte que no hay garantía de que se ejecuten antes de que el programa termine.

**Rust responde en tiempo de compilación.** Cada valor tiene un dueño, el compilador sabe en qué línea deja de existir ese dueño y en esa línea inserta la liberación. Sin recolector y sin `free` escrito a mano. A cambio, el compilador te obliga a que el dueño esté claro.

## Las tres reglas

El capítulo 4 de *The Rust Programming Language* las resume en tres frases:

1. Cada valor en Rust tiene un dueño (*owner*).
2. Solo puede haber un dueño a la vez.
3. Cuando el dueño sale de ámbito, el valor se libera (*dropped*).

Leídas así parecen obvias. La segunda es la que choca viniendo de Go, porque significa que una asignación normal no siempre copia:

```rust
fn main() {
    let s1 = String::from("hola");
    let s2 = s1; // la propiedad pasa a s2
    println!("{s1}, {s2}");
}
```

```text
error[E0382]: borrow of moved value: `s1`
 --> src/main.rs:4:16
  |
2 |     let s1 = String::from("hola");
  |         -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
3 |     let s2 = s1; // la propiedad pasa a s2
  |              -- value moved here
4 |     println!("{s1}, {s2}");
  |                ^^ value borrowed here after move
```

E0382 es el error de propiedad por excelencia, y el mensaje cuenta la historia completa en tres anotaciones: `s1` es un `String`, que no es `Copy`; en la línea 3 su valor se **movió** a `s2`, y en la línea 4 intentas usarlo. Tras el move, `s1` sigue existiendo como nombre, pero el compilador la da por vacía de contenido y no te deja leerla.

## Pila, montículo y por qué una asignación mueve

Para entender el move hay que mirar qué es un `String`. En la pila ocupa tres palabras: **un puntero** al búfer del montículo donde están los bytes, **la longitud** que se usa y **la capacidad** reservada. Se puede comprobar:

```rust
use std::mem::size_of;

fn main() {
    let mut s = String::with_capacity(16);
    s.push_str("hola");
    // Lo que vive en la pila: puntero, longitud y capacidad.
    println!("size_of::<String>() = {} bytes", size_of::<String>());
    println!("len = {}, capacity = {}", s.len(), s.capacity());

    let n: i64 = 42;
    let m = n; // i64 es Copy: n sigue siendo válido
    println!("{n} {m}");
}
```

```text
size_of::<String>() = 24 bytes
len = 4, capacity = 16
42 42
```

Veinticuatro bytes en una máquina de 64 bits: tres veces ocho. Cuando escribes `let s2 = s1`, Rust copia esos 24 bytes, no los del montículo. Si después dejara vivas las dos variables, habría **dos punteros al mismo búfer**, y al salir de ámbito los dos intentarían liberarlo: un double free de manual. La regla del dueño único lo resuelve por las bravas: tras copiar la cabecera, la variable de origen queda invalidada. Eso es un move. No hay copia profunda ni recolector, solo 24 bytes copiados y un nombre que deja de valer.

Go hace la misma copia superficial, pero deja vivos los dos nombres:

```go
package main

import "fmt"

func main() {
	s1 := []string{"rust", "go"}
	s2 := s1 // se copia la cabecera; el array de debajo es el mismo
	s2[0] = "c"
	fmt.Println(s1[0], s2[0]) // c c: dos nombres, un solo array, y el GC lo vigila
}
```

En Go esto es legal y seguro en cuanto a memoria: el recolector solo liberará el array cuando no lo alcance ninguna de las dos cabeceras. El precio es que el aliasing es invisible, y cualquiera que tenga una copia del slice puede cambiar lo que ve el otro. Rust prefiere prohibirlo por defecto y que lo pidas de forma explícita.

## Copy y Clone: la copia barata y la cara

En el ejemplo anterior `let m = n` no movió nada. `i64` implementa el trait `Copy`, y para los tipos `Copy` la asignación es una copia bit a bit tras la que el original sigue siendo válido. El libro enumera los casos de la biblioteca estándar: los enteros, los flotantes, `bool`, `char` y las tuplas cuyos elementos sean todos `Copy`. A eso hay que sumar los arrays de elementos `Copy` y las referencias compartidas `&T`. Las referencias mutables `&mut T` no lo son, porque copiarlas crearía dos caminos para mutar lo mismo.

`Clone` es otra cosa: una copia **explícita**, con `.clone()`, que puede hacer todo el trabajo que haga falta. Clonar un `String` reserva otro búfer y copia los bytes. La documentación de `Copy` lo dice sin rodeos: una copia bit a bit de un `String` solo duplicaría el puntero y acabaría en un double free, «*for this reason, String is Clone but not Copy*».

Tus propios tipos pueden ser `Copy` si todos sus campos lo son:

```rust
#[derive(Clone, Copy)]
struct Punto {
    x: i32,
    y: i32,
}

fn main() {
    let a = Punto { x: 1, y: 2 };
    let b = a; // copia bit a bit: a sigue siendo válido
    println!("a = ({}, {}), b = ({}, {})", a.x, a.y, b.x, b.y);

    let s = String::from("hola");
    let t = s.clone(); // copia profunda, y se ve en el código
    println!("{s} {t}");
}
```

Si un campo no es `Copy`, el derive falla con E0204:

```text
error[E0204]: the trait `Copy` cannot be implemented for this type
 --> src/main.rs:2:8
  |
1 | #[derive(Clone, Copy)]
  |                 ---- in this derive macro expansion
2 | struct Post {
  |        ^^^^
3 |     titulo: String,
  |     -------------- this field does not implement `Copy`
```

Y hay una incompatibilidad que conviene conocer: **un tipo que implementa `Drop` no puede ser `Copy`** (E0184, «*`Copy` not allowed on types with destructors*»). Tiene lógica: si un tipo necesita hacer algo al morir, copiarlo en silencio duplicaría esa responsabilidad.

La consecuencia práctica es que en Rust **el coste de copiar está a la vista**. Si ves un `.clone()`, ahí hay una reserva de memoria. Si no lo ves, la asignación es un move o una copia de pocos bytes.

## Drop: la memoria se libera donde termina el ámbito

La tercera regla dice que el valor se libera cuando su dueño sale de ámbito. En la práctica, en la llave de cierre, el compilador inserta una llamada a `drop`. Si el tipo implementa el trait `Drop`, se ejecuta tu código; después se liberan sus campos. Es el patrón **RAII** de C++ con el compilador como garante. Un tipo que anuncia su muerte enseña el orden:

```rust
struct Ruidoso(&'static str);

impl Drop for Ruidoso {
    fn drop(&mut self) {
        println!("drop {}", self.0);
    }
}

fn main() {
    let _a = Ruidoso("a");
    let b = Ruidoso("b");
    {
        let _c = Ruidoso("c");
        println!("fin del bloque interior");
    }
    drop(b); // std::mem::drop: toma la propiedad y la deja morir
    let _ = Ruidoso("x"); // ojo: sin nombre, muere en esta misma línea
    let _d = Ruidoso("d");
    println!("fin de main");
}
```

```text
fin del bloque interior
drop c
drop b
drop x
fin de main
drop d
drop a
```

Tres cosas que leer en esa salida. Primera: `c` muere al cerrar su bloque, no al final de la función. Segunda: las variables de un ámbito se liberan **en orden inverso al de su declaración**, `d` antes que `a`, como dice la referencia del lenguaje; los campos de un struct, en cambio, se liberan en el orden en que se declaran. Tercera, y es una trampa clásica: `let _ = valor` no liga el valor a ningún nombre, así que muere en esa misma sentencia. Si lo que guardas es el guard de un `Mutex` o un fichero temporal, `let _guard =` y `let _ =` hacen cosas muy distintas.

Para liberar antes de tiempo existe `drop(b)`, que no es magia: es una función de la biblioteca estándar con el cuerpo vacío que recibe el valor por propiedad y lo deja morir al terminar. Lo que no se puede es llamar al método directamente, porque entonces el compilador volvería a liberarlo al cerrar el ámbito:

```text
error[E0040]: explicit use of destructor method
  --> src/main.rs:11:7
   |
11 |     a.drop();
   |       ^^^^ explicit destructor calls not allowed
```

Frente a Go, la diferencia es el **determinismo**. En Go sabes que la memoria se liberará en algún momento; en Rust sabes en qué línea. Y como `Drop` sirve para cualquier recurso, el mismo mecanismo cierra ficheros, sockets y locks sin `defer`: el `File` se cierra al salir de ámbito y el guard de un `Mutex` lo libera al morir.

## Lo que Rust garantiza y lo que no

En código seguro, sin `unsafe`, el sistema de propiedad garantiza que **no hay double free ni use-after-free**: un valor tiene un solo dueño, se libera una sola vez y nadie puede usarlo después de moverlo.

Lo que no garantiza es que no haya fugas. Suena raro, pero está escrito. La documentación de `std::mem::forget` explica por qué esa función no es `unsafe`: «*Rust's safety guarantees do not include a guarantee that destructors will always run*». Fugar memoria es seguro en Rust, en el sentido técnico de la palabra: no corrompe nada, solo la desperdicia. Hay formas deliberadas de hacerlo. `Box::leak` renuncia a la propiedad a cambio de una referencia que vive hasta el final del programa, útil para configuración que se carga una vez:

```rust
fn main() {
    // Box::leak renuncia a la propiedad a cambio de una referencia 'static.
    let modo: &'static str = Box::leak(String::from("producción").into_boxed_str());
    println!("modo: {modo}");
}
```

Y hay formas accidentales, como un ciclo de `Rc`, que veremos enseguida.

## Funciones: pasar un valor es regalarlo

Pasar un argumento a una función funciona igual que asignar: si el tipo no es `Copy`, se mueve. La función pasa a ser dueña del valor y lo libera al terminar, salvo que lo devuelva.

```rust
fn publicar(titulo: String) {
    println!("publicado: {titulo}");
} // aquí muere titulo y se libera su memoria

fn main() {
    let t = String::from("Ownership en Rust");
    publicar(t);
    println!("{t}");
}
```

```text
error[E0382]: borrow of moved value: `t`
 --> src/main.rs:8:16
  |
7 |     publicar(t);
  |              - value moved here
8 |     println!("{t}");
  |                ^ value borrowed here after move
  |
note: consider changing this parameter type in function `publicar` to borrow instead if owning the value isn't necessary
 --> src/main.rs:1:21
  |
1 | fn publicar(titulo: String) {
  |    --------         ^^^^^^ this parameter takes ownership of the value
```

Fíjate en la nota: el compilador ya sugiere la solución del próximo post, que el parámetro **tome prestado** en lugar de quedarse el valor. Pero con lo que sabemos hasta aquí, la única forma de que el llamador recupere el `String` es que la función se lo devuelva:

```rust
fn contar_palabras(texto: String) -> (String, usize) {
    let n = texto.split_whitespace().count();
    (texto, n) // devuelvo el String para que el llamador lo recupere
}

fn main() {
    let cuerpo = String::from("cada valor tiene un dueño");
    let (cuerpo, n) = contar_palabras(cuerpo);
    println!("{n} palabras en «{cuerpo}»");
}
```

Funciona, y es exactamente el ejemplo que usa el libro antes de presentar las referencias, para admitir a continuación que es demasiada ceremonia. Imagina una función que necesita leer tres valores y devolver un resultado: tendría que devolver una tupla de cuatro. Ese incordio es la motivación del préstamo.

## Dos errores más que verás pronto

El primero es E0382 dentro de un bucle. El valor se mueve en la primera vuelta y en la segunda ya no está:

```text
error[E0382]: use of moved value: `tags`
 --> src/main.rs:8:42
  |
6 |     let tags = vec![String::from("rust"), String::from("go")];
  |         ---- move occurs because `tags` has type `Vec<String>`, which does not implement the `Copy` trait
7 |     for idioma in ["es", "en"] {
  |     -------------------------- inside of this loop
8 |         println!("{idioma}: {}", indexar(tags));
  |                                          ^^^^ value moved here, in previous iteration of loop
```

El segundo sorprende mucho viniendo de Go: no puedes sacar un elemento de un vector por índice si no es `Copy`.

```rust
fn main() {
    let tags = vec![String::from("rust"), String::from("go")];
    let primera = tags[0];
    println!("{primera}");
}
```

```text
error[E0507]: cannot move out of index of `Vec<String>`
 --> src/main.rs:3:19
  |
3 |     let primera = tags[0];
  |                   ^^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait
  |
help: consider borrowing here
  |
3 |     let primera = &tags[0];
  |                   +
```

Si te dejara moverlo, el vector seguiría siendo dueño de un hueco con un `String` que ya no le pertenece, y al liberar el vector se liberaría dos veces. Las salidas son prestar (`&tags[0]`), clonar, o sacar el elemento de verdad con métodos que dejan el vector coherente, como `remove`, `swap_remove` o `std::mem::take`.

## Cuando un valor necesita varios dueños: Rc y Arc

Hay estructuras en las que un dueño único no encaja: un nodo de un grafo al que apuntan varios, o una configuración que comparten muchos objetos sin que ninguno sea claramente el responsable. Para eso está `Rc<T>`, un puntero con **contador de referencias**. Clonar un `Rc` no copia el valor: suma un dueño. Cuando muere el último, se libera.

```rust
use std::rc::Rc;

fn main() {
    let tema = Rc::new(String::from("github"));
    let post_a = Rc::clone(&tema); // no copia el String: suma un dueño
    {
        let _post_b = Rc::clone(&tema);
        println!("dueños: {}", Rc::strong_count(&tema));
    }
    println!("dueños: {}", Rc::strong_count(&tema));
    println!("tema de post_a: {post_a}");
} // el último Rc que muere libera el String
```

```text
dueños: 3
dueños: 2
tema de post_a: github
```

`Rc` usa un contador normal, sin operaciones atómicas, así que no puede cruzar hilos. El compilador lo sabe porque `Rc` no implementa `Send`:

```text
error[E0277]: `Rc<String>` cannot be sent between threads safely
 --> src/main.rs:7:27
  |
  = help: within `{closure@src/main.rs:7:27: 7:34}`, the trait `Send` is not implemented for `Rc<String>`
```

Para eso está `Arc<T>`, la versión con contador atómico. Algo más cara, y segura entre hilos.

Hay un matiz importante: tanto `Rc` como `Arc` solo dan acceso **de lectura** al valor compartido. Si varios dueños necesitan modificarlo, hace falta mutabilidad interior: `RefCell<T>` en un solo hilo, que comprueba las reglas de préstamo en tiempo de ejecución, y `Mutex<T>` o `RwLock<T>` entre hilos. El `Arc<Mutex<T>>` es la combinación que más verás:

```rust
use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let visitas = Arc::new(Mutex::new(0u64));
    let mut hilos = Vec::new();
    for _ in 0..4 {
        let visitas = Arc::clone(&visitas);
        hilos.push(thread::spawn(move || {
            *visitas.lock().unwrap() += 1;
        }));
    }
    for h in hilos {
        h.join().unwrap();
    }
    println!("visitas: {}", visitas.lock().unwrap());
}
```

Y aquí está la fuga accidental que prometía. Si dos valores con `Rc` se apuntan entre sí, ninguno de los dos contadores llega nunca a cero:

```rust
use std::cell::RefCell;
use std::rc::Rc;

struct Nodo {
    nombre: &'static str,
    siguiente: RefCell<Option<Rc<Nodo>>>,
}

impl Drop for Nodo {
    fn drop(&mut self) {
        println!("drop {}", self.nombre);
    }
}

fn main() {
    let a = Rc::new(Nodo { nombre: "a", siguiente: RefCell::new(None) });
    let b = Rc::new(Nodo { nombre: "b", siguiente: RefCell::new(Some(Rc::clone(&a))) });
    *a.siguiente.borrow_mut() = Some(Rc::clone(&b)); // a -> b -> a
    println!("a: {}, b: {}", Rc::strong_count(&a), Rc::strong_count(&b));
} // a y b salen de ámbito, los contadores bajan a 1 y nadie imprime "drop"
```

La salida es una sola línea, `a: 2, b: 2`, y ningún `drop`. Compila sin avisos y no viola ninguna garantía: es la fuga segura de la que habla la documentación de `forget`. El recolector de Go sí libera un ciclo así, porque razona sobre lo alcanzable y no sobre contadores. En Rust se rompe con `Weak<T>`, una referencia que no cuenta como dueña, y es lo que se usa para el puntero al padre en un árbol.

Dicho esto, mi consejo es el mismo que di en el post comparativo sobre `.clone()`: **`Rc` y `Arc` son herramientas, no muletas**. Si cada struct de tu programa acaba envuelto en `Arc<Mutex<...>>`, es probable que el diseño esté pidiendo un dueño claro y préstamos, no más contadores.

## Lo que viene

Resumiendo: cada valor tiene un dueño; asignar o pasar a una función un tipo que no es `Copy` lo **mueve**; copiar caro se escribe `.clone()`; el valor se libera en la llave de cierre del ámbito de su dueño, en orden inverso de declaración; y cuando de verdad hacen falta varios dueños, `Rc` en un hilo y `Arc` entre varios. Con eso, Rust te da lo que en C es disciplina y en Go es trabajo del runtime: memoria liberada una sola vez, en un momento que conoces, sin que nadie pueda tocarla después.

Lo que queda pendiente es el incordio de la sección de funciones: tener que devolver un valor para recuperarlo. La solución es dejar de moverlo y empezar a **prestarlo**, con `&` y `&mut`, y aceptar las reglas con las que el compilador vigila esos préstamos. De eso va [Borrowing en Rust](/post/borrowing-en-rust), dentro de dos días.
