Boxing
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
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
.

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.

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;
Nous avons maintenant le nombre 12 qui est stocké dans la Heap et non dans la Stack.
Appliquons cela à notre p
.

Lq signature de Box::new
est
;
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
.

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
.

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

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

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
.

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.

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

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êmeClone

Ce simple box.clone
, déclenche toute une série d'évènements.
Premièrement, on borrow box
en &box
que l'on copie ensuite dans Box::clone
.

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.

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
.

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.

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

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

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

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.

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.

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

Finalement la méthode main
se termine.

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

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
Ce travail est sous licence CC BY-NC-SA 4.0.