Il conteggio delle referenze (Rc) in Rust

Ci sono casi in cui è necessario che diverse parti del programma gestiscano lo stesso dato. Ad esempio in una struttura gerarchica dove due variabili fanno entrambe riferimento al valore di un’altra nello stesso intervallo di tempo. E qui che in Rust entarno in gioco i Reference Counting, in italiano conteggio delle referenze o riferimenti. Il tipo di dato usato in questo caso è Rc<T> dove T indica un tipo generico.

Nell’esempio che segue, poniamo il caso di avere un nodo che rappresenta un annuncio da inserire in tutti i post del nostro blog. Dobbiamo quindi condividere la proprietà per ciascuno senza avere errori.

In un progetto chiamato rc-tutorial, creiamo un enumeratore contenente dati ricorsivi. Infatti ciascun nodo può avere dei figli. Questi sono rappresentati da un vettore di nodi. Aggiungiamo una funzione che stampa il nome del nodo e rispetti la gerarchia.

//lib.rs
use std::rc::Rc;

pub enum Node {
    El(String, Rc<Vec<Node>>),
    Nil,
}

pub fn print_node(node: &Node, indent: usize) {
    match node {
        Node::El(name, children) => {
            println!("{:indent$}{}", "", name);
            for child in children.iter() {
                print_node(child, indent + 2);
            }
        },
        Node::Nil => (),
    }
}

pub fn nil() -> Rc<Vec<Node>>  {
    Rc::new(vec![Node::Nil])
}

pub fn el(name: String , children: Rc<Vec<Node>>) -> Node {
    Node::El(name, children)
}

A questo punto, per aggiungere il blocco di nodi come figli ad un altro vettore, useremo Rc::clone() invece di Rc::new(). Il risultato della compilazione viene mostrato nell’immagine.

//main.rs
use std::{rc::Rc, vec};

use rc_tutorial::{
    el, nil, print_node
};

fn main() {
    let node_shared = vec![el(
        "Adv".to_string(),
        Rc::new(vec![
            el("image-adv".to_string(), nil()),
            el("text-adv".to_string(), nil()),
        ]),
    )];

    let rc_node_shared = Rc::new(node_shared);

    let post_1 = el(
        "post_1".to_string(),
        Rc::new(vec![
            el("title_1".to_string(), nil()),
            el("node_shared".to_string(), Rc::clone(&rc_node_shared)),
        ]),
    );
    
    let post_2 = el(
        "post_2".to_string(),
        Rc::new(vec![
            el("title_2".to_string(), nil()),
            el("node_shared".to_string(), Rc::clone(&rc_node_shared)),
        ]),
    );

    print_node(&post_1, 0);
    print_node(&post_2, 0);
    
}