https://lafor.ge/feed.xml

Boxing

2024-03-11

Bonjour à toutes et à tous 😀

Si l'on se limitait aux références, le langage Rust serait bien pauvre.

Heureusement, des mécanismes existent et enrichissent de manières considérables les possibilités offertes.

Dans cet article, nous allons expliquer la manière dont Rust à implémenté le mécanisme largement répandu de boxing.

Dans la section sur la heap nous avons déclaré une String, nous avons déclaré qu'une partie vivait dans une frame de la stack et les données dans la heap.

Mais nous n'avons pas trop été dans le détails du fonctionnement, ni comment, nous nous pourrions mimer ce fonctionnement.

C'est ce que nous allons faire tout de suite en nous créant des données qui au lieu de vivre dans la mémoire stack vont être stockées dans la heap.

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 désirons maintenant envoyer notre instance de Person dans la Heap.

Pour cela nous allons utiliser l'un des outils que la bibliothèque standard de Rust nous fourni.

Il s'agit des Box.

La syntaxe est simple.

Box::new(12);

Nous avons maintenant le nombre 12 qui est stocké dans la Heap et non dans la Stack.

Appliquons cela à notre p.

missing alt

Lq signature de Box::new est

fn new(x: T) -> Box<T>;

Ce qui signifie que new prend l'ownership de x, autrement dit ce qui est copiable est copié sinon on déplace.

Person n'est pas Copy, on déplace donc p dans Box::new.

missing alt

Maintenant que p a quitté main, il faut de la place pour l'accueillir quelque part, car dans le cas contraire, à l'issue de l'exécution de Box::new, l'instance de Person sera détruite.

Pour cela, nous allons allouer suffisamment de place dans la Heap pour recevoir notre instance Person.

missing alt

Puis nous allons déplacer le contenu référencé précédemment par p dans cette zone allouée dans la Heap.

missing alt

Créer une référence pour ne pas perdre nos données dans la Heap.

missing alt

Maintenant que Box::new s'est exécutée, il est temps de récupérer ce qui a été produit dans une variable box.

missing alt

On déplace alors la référence du contexte de Box::new vers celui de main.

Nous avons maintenant dans main, une variable box qui référence un objet dans la stack qui référence un objet dans la Heap.

missing alt

La suite vous la connaissez, la frame de Box::new est détruite.

missing alt

Maintenant, pourquoi s'ennuyer avec ce type Box<T> et pas juste faire &T mais vers la Heap ?

Et bien parce que la structure Box possède, outre sa faculté à allouer de la Heap, la capacité de se cloner.

Et c'est ce que nous allons voir tout de suite.

On appelle la méthode .clone car Box<T> implémente Clone.

Attention

À condition que T soit lui-même Clone

missing alt

Ce simple box.clone, déclenche toute une série d'évènements.

Premièrement, on borrow boxen &box que l'on copie ensuite dans Box::clone.

missing alt

Puis l'on appelle la méthode T::Clone de ce que l'on box en copiant la référence qui est à l'intérieur de la Box que l'on souhaite cloner.

Cela va avoir pour effet de réaliser l'allocation dans Heap de l'espace requis.

missing alt

Puis l'on clone réellement la structure et la String qui est associée.

On récupère une référence vers la zone mémoire allouée.

Que l'on vient stocker dans data.

missing alt

On créé alors une nouvelle Box que l'on initialise avec une copie de la référence data et donc vers le "bon endroit" en Heap.

missing alt

La méthode T::clone se termine, on nettoie la frame

missing alt

On déplace la nouvelle Box dans le contexte main et on l'associe à la variable box_clone.

missing alt

La méthode Box::clone se termine, on nettoie la frame

missing alt

Nous nous retrouvons alors avec deux Box qui pointent vers deux structures indépendantes.

Et maintenant regardons ce qui se passe lors de la libération mémoire.

missing alt

Disons que c'est box qui meurt en premier.

Le drop est déclenché et la mémoire Heap alloué est libérée.

Mais pas celle de box_clone qui est totalement indépendante.

missing alt

Il faudra que drop se déclenche également dessus.

missing alt

Finalement la méthode main se termine.

missing alt

Et la frame est détruite avec toutes les références vers la Heap.

missing alt
 

Observation

Comme vous avez pu l'observer, le clone d'une Box est loin d'être gratuit.

Il est a utiliser comme tout clone si vous savez ce que vous clonez

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.