https://lafor.ge/feed.xml

Reference Counting

2024-03-13

Bonjour à toutes et à tous 😀

Dans l'article sur le boxing, on a vu que le clonage était très loin d'être gratuit.

Aujourd'hui nous allons voir une manière de cloner presque gratuitement. 😉

Utilisons le même exemple.

Déclarons une structure Person

struct Person {
    name: String,
    age: u8
}

Elle contient deux champs:

  • name : une String donc une référence et une slice quelque part en Heap
  • age : un entier naturel

Déclarons une instance de Person et affectons-la à la variable p.

missing alt

Nous nous retrouvons donc à avoir un espace mémoire dans la Stack qui est référencé par p.

Mais ce n'est pas tout car p.name référence aussi sa slice qui vie dans la Heap. (ici initialisé à "toto")

Nous possédons également p.age qui réside dans la Stack.

missing alt

Nous allons utiliser un autre outil de la bibliothèque standard qui se nomme Rc pour Reference Counting.

Tout comme pour Box, la syntaxe est simple.

let rc = Rc::new(12);

12 est désormais dans la Heap, mais il va y avoir quelques différences avec le Box::new. 😁

Appliquons le à p.

missing alt

Et voyons ce que ça produit en mémoire.

Tout d'abord on déplace p dans le contexte de Rc::new.

missing alt

On vient wrapper nos données dans un conteneur qui possède deux champs:

  • data : nos données
  • count: un entier

Le compteur est initialisé à 1.

En réalité le conteneur est une RcBox et le champ count existe en deux version weak et strong. Mais pour les besoins de vulgarisations je ne rentrerai pas dans les détails.

On vient alors allouer avec un Box::new de la place dans la Heap

missing alt

On déplace alors vers la Heap notre RcBox.

missing alt

On récupère alors une référence vers la RcBox

missing alt

Que l'on peut ensuite renvoyé dans le contexte main.

missing alt

On sort de la méthode Rc::new ce qui a pour action de détruire la frame.

On se retrouve alors avec rc_p une référence vers une RcBox dans la Heap.

missing alt

Bon pour le moment, ça semble juste un Box::new avec des étapes supplémentaires, mais promis tout ça a un sens. 😇

Pour cela, clonons rc_p.

missing alt

Même combat que pour Box::clone, on créé une référence que l'on copie dans Rc::clone.

missing alt

Mais cette fois-ci pas de vrai clone.

On incrémente seulement le compteur de la RcBox.

Il passe de 1 à 2.

On créé une nouvelle référence qui pointe vers la même RcBox.

missing alt

Et on retourne la référence dans le contexte main.

missing alt

Nous avons maintenant dans main, deux références qui pointe vers la même RcBox.

Le compteur de la RcBox est de 2.

missing alt

Maintenant on décide de déplacer la Rc<Person>, rc_p_clone dans la méthode f1.

Voyons ce qui se passe.

missing alt

La référence est déplacée dans le contexte de f1.

missing alt

f1 termine.

missing alt

Le drop est déclenché.

Il a pour effet de décrémenter de 1 le compteur de la RcBox.

Mais comme le compteur est supérieur à 0, il ne se passe rien.

missing alt

f1 est terminé, mais la RcBox est toujours dans la Heap

missing alt

Appelons f1 avec rc_p cette fois-ci, la dernière référence existante de la RcBox.

missing alt

Même combat, on déplace dans f1.

missing alt

f1 se termine

missing alt

Le drop est déclenché, on décrémente le compteur.

missing alt

Mais cette fois-ci, le compteur vaut maintenant 0.

On libère la mémoire occupée par la RcBox et par la String également.

missing alt

f1 est terminée.

missing alt

On détruit la frame

missing alt

main se termine

missing alt

On détruit la frame, fin du programme.

missing alt

Alors à quoi tout ça sert?

En synchrone, à presque rien, en tout cas je n'ai jamais eu d'exemple vraiment parlant qui n'aurait pas pu se régler par une référence non-mutable et une lifetime adéquate.

Par contre son grand-frère Arc est la pierre angulaire de l'asynchronisme facile en Rust.

Il y a aussi le patterm de l'Interior Mutability qui utilise les Rc.

On verra ces deux aspects dans des prochains articles.

Tout ce que vous devez retenir c'est que cela permet à Rust de compter le nombre de référence active et de libérer la mémoire au moment propice.

C'est le même fonctionnement qu'un garbage collector, mais sans devoir arrêter le temps et l'espace pour compter vu que les objets se comptent eux-même.

avatar

Auteur: Akanoa

Je découvre, j'apprends, je comprends et j'explique ce que j'ai compris dans ce blog.

Ce travail est sous licence CC BY-NC-SA 4.0.