Home
# Pointeurs intelligents --- ## Généralités - Un **pointeur** est une variable qui contient une adresse mémoire. - Cette adresse **pointe** vers des données. ![Illustration: tableau.](figs/mem_ref_vec.png) - Question: Quel type de pointeur avons-nous déjà rencontré? - Réponse: La référence. --- ## Smart pointers - Type abstrait qui rajoute des fonctionnalités au poiteur standard. - Management automatique de la mémoire. - Vérification de limites. - Permettent la désallocation de la mémoire de manière automatique: - Plusieurs pointeurs sur un espace mémoire. - Dernier pointeurs est détruit => espace mémoire est désalloué. - Empêche les fuites mémoires. --- ## Types de pointeurs - Différents types: - Pointeur unique, propriétaire de ses données (quand il est détruit les données aussi). - Comptage de références sur des données, quand ce nombre tombe à zéro on détruit tout. - Mutabilité intérieure: les règles de Rust sont imposées à l'exécution. - Accès atomiques: pas de lecture/écriture concurrente possible sur la valeur pointée. --- ## En Rust - Exemples: - `Vec
`, `String`: ces types possèdent de la mémoire et la manipulent eux-mêmes. - Les pointeurs intelligent doivent implémenter deux traits: - `Deref`: comment on déréférence le pointeur. - `Drop`: comment on détruit le pointeur. - Les trois cas les plus typiques (il y en a d'autres): - `Box
` pointeur unique qui alloue des données sur la pile. - `Rc
` "reference counted" type, qui permet de partager la propriété. - `Ref
` et `RefMut
` qui permet d'imposer les règles de propriété à l'exécution plutôt qu'à la compilation. --- # Le type `Box
` --- ## Généralités - Utile pour stocker des données sur le tas. - Sur la pile: pointeur sur les données du tas. - Cas typiques d'utilisation: - La taille d'un type est inconnue à la compilation. - On veut transférer la propriété de grandes quantités de données mais les données ne sont pas copiées (juste le pointeur). --- ## Utilisation ```rust [2|3-4|5-7|8-10|] fn main() { let num = Box::new(10); println!("num = {}", num); // déréférenciation automatique println!("num = {}", *num); // déréférenciation explicite let x = 10; // seule la déréférenciation explicite marche println!("Is {} equal to {}?. Answer: {}", num, x, *num == x); let y = &x; // La seule différence est qu'ici nous avons une référence println!("Is {} equal to {}?. Answer: {}", y, x, *y == x); } ``` --- ## Cas pratique: la liste chaînée ```rust compile_fail [1,4|2-3|1-4|] enum List { Elem(i32, List), Nil, } use List::{Elem, Nil}; fn main() { let list = Elem(1, Elem(2, Elem(3, Nil))); } ``` --- ## Cas pratique: la liste chaînée ```rust [1,4|2-3|1-4|] #[derive(Debug)] enum List { Elem(i32, Box
), Nil, } use List::{Elem, Nil}; fn main() { let list = Elem(1, Box::new(Elem(2, Box::new(Elem(3, Box::new(Nil)))))); println!("{:?}", list); } ```