Rc

Rc significa "contador de referencias". Ya se ha visto que en Rust cada valor solo puede ter un dueño. Por eso, el siguiente código no funciona:

fn takes_a_string(input: String) {
    println!("It is: {}", input)
}

fn also_takes_a_string(input: String) {
    println!("It is: {}", input)
}

fn main() {
    let user_name = String::from("User MacUserson");

    takes_a_string(user_name);
    also_takes_a_string(user_name); // ⚠️
}

Después de que takes_a_string reciba user_name, no se puede volver a usar. En este caso, se podría solventar utilizando user_name.clone(). Pero en ocasiones, un valor forma parte de un struct y puede que no se pueda clonar ese struct. O puede que sea un valor de gran tamaño que no sea eficiente clonar. Por estas razones existe Rc, que sirve para permitir que un valor tenga más de un dueño de forma simultánea. Rc anota quienes tienen la propiedad y cuántos. Posteriormente, cuando el número de dueños baja a cero, el valor asociado se liberará.

En el siguiente ejemplo se usa Rc. Se crean dos struct: uno denominado City y otro CityData. City contiene la información de una ciudad y CityData reúne todas las ciudades usando Vec.

#[derive(Debug)]
struct City {
    name: String,
    population: u32,
    city_history: String,
}

#[derive(Debug)]
struct CityData {
    names: Vec<String>,
    histories: Vec<String>,
}

fn main() {
    let calgary = City {
        name: "Calgary".to_string(),
        population: 1_200_000,
           // Pretend that this string is very very long
        city_history: "Calgary began as a fort called Fort Calgary that...".to_string(),
    };

    let canada_cities = CityData {
        names: vec![calgary.name], // This is using calgary.name, which is short
        histories: vec![calgary.city_history], // But this String is very long
    };

    println!("Calgary's history is: {}", calgary.city_history);  // ⚠️
}

Esto no funciona debido a que canada_cities es el dueño de los datos al final y calgary ya no lo es. Por lo que el error es el siguiente:

error[E0382]: borrow of moved value: `calgary.city_history`
  --> src/main.rs:27:42
   |
24 |         histories: vec![calgary.city_history], // But this String is very long
   |                         -------------------- value moved here
...
27 |     println!("Calgary's history is: {}", calgary.city_history);  // ⚠️
   |                                          ^^^^^^^^^^^^^^^^^^^^ value borrowed here after move
   |
   = note: move occurs because `calgary.city_history` has type `String`, which does not implement the `Copy` trait

Se podría clonar el nombre con names: vec![calgary.name.clone()], pero no se desea con city_history debido a que puede ser un texto muy largo. Por ello, se usa Rc. Para ello, se debe declarar su uso previamente:

use std::rc::Rc;

fn main() {}

Y después, usar Rc como tipo de la variable que se quiera usar:

´´´rust use std::rc::Rc;

#[derive(Debug)] struct City { name: String, population: u32, city_history: Rc, }

#[derive(Debug)] struct CityData { names: Vec, histories: Vec<Rc>, }

fn main() {} ´´´

Para añadir una nueva referencia, se debe usar clone sobre el elemento Rc. En los Rc, esta función solo clona el puntero y no duplica la variable contenida. Por ello, su coste es mínimo.

En el ejemplo anterior, al clonar la historia de una ciudad, esta tendrá dos dueños. Se puede comprobar el número de dueños en un momento dado mediante Rc::strong_count(&item). El código quedará así:

use std::rc::Rc;

#[derive(Debug)]
struct City {
    name: String,
    population: u32,
    city_history: Rc<String>, // String dentro de un Rc
}

#[derive(Debug)]
struct CityData {
    names: Vec<String>,
    histories: Vec<Rc<String>>, // Un Vec de Strings dentro de Rcs
}

fn main() {
    let calgary = City {
        name: "Calgary".to_string(),
        population: 1_200_000,
        city_history: Rc::new("Calgary began as a fort called Fort Calgary that...".to_string()), // Rc::new() para crear el Rc
    };

    let canada_cities = CityData {
        names: vec![calgary.name],
        histories: vec![calgary.city_history.clone()], // .clone() para incrementar la cuenta
    };

    println!("Calgary's history is: {}", calgary.city_history);
    println!("{}", Rc::strong_count(&calgary.city_history));
    let new_owner = calgary.city_history.clone();
}

Este código imprime 2 como el número de dueños de la historia de Calgary. Como la última línea de código añade otro dueño, new_owner, si se imprimiera después la cuenta de dueños, se mostraría ya 3.

Se observa que esta función habla de strong pointers (punteros fuertes). ¿Existen los punteros débiles? Sí, existen. Se utilizan cuando se quiere evitar referencias circulares entre elementos. Por ejemplo, si dos Rc se apuntan entre sí, si ambos punteros son fuertes, nunca alcanzarán la cuenta de cero dueños, ya que cada uno es dueño del otro, y no se podrán liberar. Rc mantiene la cuenta de los punteros débiles, pero no la tiene en cuenta para liberar la memoria. Es decir, cuando un elemento Rc no tiene punteros fuertes, se libera su espacio aunque la cuenta de punteros débiles no sea cero.

Se puede utilizar Rc::downgrade(&item) en lugar de Rc::clone(&item) para construir una referencia débil. Para ver la cuenta de referencias débiles se usa Rc::weak_count(&item).