Leçons du module (2/2)
Smart Pointers: Box et Rc
Un smart pointer (pointeur intelligent) est une structure de données qui se comporte comme un pointeur, mais possède des métadonnées et des fonctionnalités supplémentaires (comme le comptage des références ou la gestion automatique de la mémoire).
En Rust, les références ordinaires (comme &) ne font qu'emprunter des données. Les smart pointers, quant à eux, possèdent souvent les données vers lesquelles ils pointent. Des exemples courants incluent String et Vec<T>, mais la bibliothèque standard en fournit d'autres plus spécialisés.
Box<T> pour allouer dans le Tas (Heap)
Le smart pointer le plus simple est Box<T>, qui stocke les données dans le tas plutôt que sur la pile. Sur la pile ne reste que le pointeur vers la donnée allouée dans le tas.
Il est principalement utilisé dans les cas suivants :
- Lorsque vous avez un type dont la taille ne peut pas être connue au moment de la compilation (comme un type récursif), et que vous souhaitez utiliser une valeur de ce type dans un contexte qui nécessite une taille exacte.
- Lorsque vous souhaitez transférer de grandes quantités de données sans les copier.
Exemple de base :
fn main() {
let b = Box::new(5); // alloca il valore 5 nello heap
println!("b = {}", *b); // de-referenzia con * per leggere il valore
}
Types Récursifs et Cons List
Un type récursif est un type qui peut contenir une valeur de lui-même en son sein. Rust doit connaître la taille exacte d'un type au moment de la compilation. Comme la profondeur d'un type récursif pourrait être infinie, le compilateur génère une erreur à moins d'utiliser un Box (qui a une taille fixe, puisqu'il s'agit d'un pointeur) :
enum List {
Cons(i32, Box<List>),
Nil,
}
Rc<T> : Comptage des Références (Reference Counting)
Il existe des cas où une seule valeur peut avoir plusieurs propriétaires. Le smart pointer Rc<T> (Reference Counted) suit le nombre de références à une valeur pour déterminer si celle-ci est toujours utilisée. Si le décompte tombe à zéro, la ressource est libérée en toute sécurité.
[!NOTE]
Rc<T>est utilisable exclusivement dans des scénarios mono-thread. Pour des scénarios multi-thread (concurrents), il faut utiliserArc<T>(Atomic Reference Counted).
use std::rc::Rc;
fn main() {
let a = Rc::new(5);
let b = Rc::clone(&a); // incrementa il contatore a 2
println!("Riferimenti: {}", Rc::strong_count(&a));
}
À toi de jouer
Exercice 1 : Allouer avec Box
Allouez une valeur entière égale à 42 dans le tas à l'aide de Box::new et assignez-la à une variable nommée val. Ensuite, affichez à l'écran la valeur stockée dans le Box en la déréférençant explicitement avec l'opérateur *.
Afficher l'indice
Utilisez `let val = Box::new(42);` pour stocker la valeur dans le tas et déréférencez-la avec `*val` à l'intérieur de `println!`.
Solution disponible après 3 tentatives
Exercice 2 : Structure de données récursive (Cons List)
Définissez un enum récursif nommé List contenant deux variantes : Cons qui renferme un tuple (i32, Box<List>), et Nil qui représente la fin de la liste. Dans le main, instanciez une liste chaînée contenant l'élément 1 suivi de Nil.
Afficher l'indice
L'enum se déclare ainsi `enum List { Cons(i32, Box<List>), Nil }`. Instanciez la liste avec `List::Cons(1, Box::new(List::Nil))`.
Solution disponible après 3 tentatives
Exercice 3 : Partager la propriété avec Rc
Utilisez std::rc::Rc pour créer un entier partagé contenant la valeur 100 au sein d'une variable a. Clonez la référence dans une variable b en utilisant Rc::clone. Enfin, affichez à l'écran le compteur de références actives à l'aide de la fonction Rc::strong_count.
Afficher l'indice
Créez la valeur avec `Rc::new(100)`. Clonez avec `Rc::clone(&a)`et affichez le nombre de propriétaires avec`Rc::strong_count(&a)`.
Solution disponible après 3 tentatives