---
title: Cours de programmation séquentielle
subtitle: Pointeurs intelligents
author: Orestis Malaspinas
sansfont: Sans Serif
---

# Pointeurs

## 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.svg){width=50%}

- Question: Quel type de pointeur avons-nous déjà rencontré?
	- Réponse: La référence.

## Smart pointers

- Un **pointeur intelligent** est un type abstrait qui rajoute des fonctionnalités au poiteur standard.
	- Management automatique de la mémoire.
	- Vérification de limites.
- En particulier, ils permettent la désallocation de la mémoire de manière automatique:
	- On peut avoir plusieurs pointeurs sur un espace mémoire.
	- Quand le dernier pointeurs est détruit, l'espace mémoire est désalloué.
	- Permet d'empêcher les fuites mémoires.
- Il existe différents types: 
	- Un poiteur unique est le propriétaire de ses données (quand il est détruit les données aussi).
	- On compte le nombre de références sur des données, quand ce nombre tombe à zéro on détruit tout.

## En Rust

- Exemples: 
	- `Vec<T>`{.rust}, `String`{.rust}: ces types possèdent de la mémoire et la manipule eux-mêmes.
- Les pointeurs intelligent doivent implémenter deux traits:
	- `Deref`{.ruist}: comment on déréférence le pointeur.
	- `Drop`{.rust}: comment on détruit le pointeur.
- Les trois cas les plus typiques (il y en a d'autres):
	- `Box<T>`{.rust} pointeur unique qui alloue des données sur la pile.
	- `Rc<T>`{.rust} "reference counted" type, qui permet de partager la propriété.
	- `Ref<T>`{.rust} et `RefMut<T>`{.rust} qui permet d'imposer les règles de propriété à l'exécution plutôt qu'à la compilation.

# Le type `Box<T>`{.rust}

## Généralités

- Utile pour stocker des données sur le tas.
- La seule chose stockée sur la pile est le pointeur sur les données du tas.
- Cas typiques d'utilisation:
	- Quand la taille d'un type est inconnu à la compilation.
	- Quand on veut transférer la propriété de grandes quantité de données mais s'assurer que les données ne seront pas copiées (juste le pointeur).

## Utilisation

<pre><code data-trim="hljs rust" class="lang-rust">
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);
} 
</code></pre>

- Dans l'appel de fonctions/méthodes la déréférenciation se fait automatiquement.
- Sinon elle doit être explicite.

## Car pratique: la liste chaînée

- Rust doit connaître la taille d'un type **à la compilation**.
- Ici nous avons à faire à un type **récursif**: sa taille est potentiellement infinie.

<pre><code data-trim="hljs rust" class="lang-rust">
enum List {
	Elem(i32, List),
	Nil,
}

use List::{Elem, Nil};

fn main() 
{
    let list = Elem(1, Elem(2, Elem(3, Nil)));
} 
</code></pre>

## Car pratique: la liste chaînée

- Avec un `Box<T>`{.rust} sa taille est connue.

<pre><code data-trim="hljs rust" class="lang-rust">
#[derive(Debug)]
enum List {
	Elem(i32, Box&lt;List&gt;),
	Nil,
}

use List::{Elem, Nil};

fn main() 
{
    let list = Elem(1, Box::new(Elem(2, Box::new(Elem(3, Box::new(Nil))))));
    println!("{:?}", list);
} 
</code></pre>
