Les pointeurs intelligents
Un pointeur est un concept général pour une variable qui contient une adresse vers la mémoire. Cette adresse pointe vers d’autres données. Le type de pointeur le plus courant en Rust est la référence, que vous avez apprise au chapitre 4. Les références sont marquées par le symbole & et empruntent la valeur sur laquelle elles pointent. Elles n’ont pas d’autres fonctionnalités que celle de pointer sur une donnée, et elles n’ont aucun surcoût sur les performances.
Les pointeurs intelligents, d’un autre côté, sont des structures de données qui se comportent comme un pointeur mais ont aussi des fonctionnalités et métadonnées supplémentaires. Le concept de pointeur intelligent n’est pas propre à Rust : les pointeurs intelligents sont originaires du C++ et existent aussi dans d’autres langages. Rust dispose de toute une gamme de pointeurs intelligents définis dans la bibliothèque standard, qui fournissent des fonctionnalités allant au-delà de celles qui sont fournies par les références. Pour approfondir ce concept général, nous allons examiner quelques exemples de pointeurs intelligents, notamment un type de pointeur intelligent avec un compteur de références. Ce pointeur vous permet d’autoriser une donnée à avoir plusieurs propriétaires tout en gardant une trace de leur nombre et, lorsqu’il n’y a plus de propriétaire, de nettoyer cette donnée et libérer la mémoire.
En Rust, avec son concept de propriété et d’emprunt, il y a une différence supplémentaire entre les références et les pointeurs intelligents : alors que les références ne font qu’emprunter la donnée, les pointeurs intelligents sont, dans de nombreux cas, propriétaires des données sur lesquelles ils pointent.
Les pointeurs intelligents sont souvent implémentés en utilisant des structures. Au contraire d’une structure classique, les pointeurs intelligents implémentent les traits Deref et Drop. Le trait Deref permet à une instance d’un pointeur intelligent de se comporter comme une référence afin que vous puissiez écrire du code qui fonctionne aussi bien avec des références qu’avec des pointeurs intelligents. Le trait Drop vous permet de personnaliser le code qui est exécuté lorsqu’une instance d’un pointeur intelligent sort de la portée. Dans ce chapitre, nous verrons ces deux traits et expliquerons pourquoi ils sont importants pour les pointeurs intelligents.
Vu que le motif des pointeurs intelligents est un motif de conception général fréquemment utilisé en Rust, ce chapitre ne couvrira pas tous les pointeurs intelligents existants. De nombreuses bibliothèques ont leurs propres pointeurs intelligents, et vous pouvez même écrire le vôtre. Nous allons voir les pointeurs intelligents les plus courants de la bibliothèque standard :
Box<T>, pour l’allocation de valeurs sur le tas ;Rc<T>, un type comptant les références, qui permet d’avoir plusieurs propriétaires ;Ref<T>etRefMut<T>, auxquels on accède viaRefCell<T>, un type qui permet d’appliquer les règles d’emprunt au moment de l’exécution plutôt qu’au moment de la compilation.
En outre, nous allons voir le motif de mutabilité interne dans lequel un type immuable propose une API pour modifier une valeur interne. Nous allons aussi parler des boucles de références : comment elles peuvent provoquer des fuites de mémoire et comment les éviter.
Allons-y !