---
title: Cours de programmation séquentielle
subtitle: Propriété (Ownership)
author: Orestis Malaspinas
<!-- date: 25 avril 2018 -->
sansfont: Sans Serif
---

# Généralités

- Les règles sur la `propriété` sont ce qui rend Rust unique.
- La `propriété` est un ensemble de règles vérifiées à la compilation.
- Garantit la bonne gestion de la mémoire sans `garbage collector`.
- Oblige à réfléchir à ce que fait notre programme quand on le compile (et que le compilateur râle).
- Spoiler alert: simple à expliquer, difficile à appliquer.

# Pile et tas (Stack and heap)

## La pile

- Partie de la mémoire réservée pour **chaque** thread.
- Lorsqu'une fonction est appelée, un bloc est réservé pour stocker les variables locales.
- Lorsque la fonction retourne, la mémoire est libérée et peut être réutilisée.
- C'est une structure LIFO (Last in first out): le dernier ajout sera libéré en premier.
- Stocke typiquement les objets dont la taille est connue à la compilation (entiers, nombres à virgule flottante, caractères, booléens, ...).

## Le tas

- Partie de la mémoire réservée est **commune** tous les threads.
- Utilisée pour l'allocation dynamique (la taille des objets peut varier en cours d'exécution).
- Peut de la mémoire non-locale à une fonction.
- Pas de pattern particulier pour l'allocation/désallocation.
- Plus compliqué de gérer l'allocation/déasllocation.
- Typiquement plus lent que la pile (il faut chercher de la place pour l'allocation et "sauter" en mémoire pour la retrouver).

# La propriété (Ownership)

## Les règles de la propriété

1. Chaque valeur a une variable qui est son propriétaire (`owner`).
2. Une valeur ne peut avoir qu'un seul propriétaire à chaque instant.
3. Quand le programme sort de la portée du propriétaire, la valeur est détruite (`dropped`).

<pre><code data-trim="hljs rust" class="lang-rust">
fn main() 
{
	let x = 5; // x est propriétaire de la mémoire contenant 5
	{
        let y = 6; // y est propriétaire de la mémoire contenant 5

        println!("La valeur de (x,y) est: ({}, {}).", x, y);
    } // y sort de la portée et est détruite avec la valeur 6

    println!("La valeur de x est: {}", x);
} // x sort de la portée et sa valeur est détruite
</code></pre>

## Allocation de la mémoire (1/3)

- Deux façon principales d'allouer/désallouer dynamiquement de la mémoire.
    1. Manuellement (C/C++, ...): on ordonne au système d'exploitation d'allouer/désallouer de la mémoire sur le tas. 
        - Problèmes: 
            a. On peut oublier de désallouer la mémoire allouée (fuite mémoire/memory leak).
            b. On peut désallouer de la mémoire trop tôt (dangling pointer et comportement indéfini).
            c. Libérer la mémoire à double.
    2. Automatiquement: on a un "garbage collector" (java, scala, ...).
        - Problèmes:
            a. Consomme des ressources.
            b. Est une "boîte magique": il fait ce qu'il veut.
- En Rust c'est complètement différent: on contrôle complètement où et quand on alloue/désalloue:
    - Problème: il faut suivre des règles **très** strictes.

## Allocation de mémoire (2/3)

- Les types vus jusque là sont stockés dans la pile: leur *taille est connue* à la compilation.
- Que se passe-t-il lorsque la taille est *inconnue à la compilation*?
- Exemple:
<pre><code data-trim="hljs rust" class="lang-rust">
fn main() 
{
    let x = [1, 2, 3, 4]; // x de type [i32; 4], sur la pile
    // on ne peut pas augmenter sa taille

    println!("La valeur de x est: {:?}", x);

    let mut y = Vec::new(); // un vecteur dont la taille est variable
    // On rajoute des éléments au vecteur avec push(elem)
    y.push(1); // rust infère que le type des éléments est entier
    y.push(2);
    y.push(3);
    y.push(4); // on lui a rajouté 4 éléments
    println!("La valeur de y est: {:?}", y);
} // y sort de la portée, il est détruit et la mémoire est libérée
</code></pre>
- Que se passe-t-il passé ici?

## Allocation de mémoire (3/3)

![](figs/mem_vec.svg){height=70%}

- La représentation en mémoire du vecteur $\vec v = (1,2,3,4)$.
- Sur le tas: 1 pointeur vers le tas, et 2 entiers (longueur et capacité).
- Sur la pile les 4 nombres: 1, 2, 3, 4.
- Lorsque la variable (proprétaire) sort de la portée, la mémoire est **automatiquement libérée**.
- Simple, mais très contraignant (comment peut-on partager les données?).

## Extension de la propriété

Flexibiliser la propriété:

1. Donner la propriété à un autre propriétaire: `move`.
2. Emprunter pour un temps les données: `borrow`.
3. Être co-propriétaires dans des structures avancées: `Rc` et `Arc`.

# Move

## Changement de propriétaire (1/3)

- Le propriétaire des données devient une autre variable

<pre><code data-trim="hljs rust" class="lang-rust">
fn main() 
{
    let mut y = Vec::new(); // un vecteur dont la taille est variable
    // On rajoute des éléments au vecteur avec push(elem)
    y.push(1); // rust infère que le type des éléments est entier
    y.push(2);
    y.push(3);
    y.push(4); // on lui a rajouté 4 éléments
    println!("La valeur de y est: {:?}", y);

    let z = y; // le vecteur (1,2,3,4) est maintenant propriété de z
    		       // y est  une variable non initialisée
   println!("La valeur de z est: {:?}", z);
} // z sort de la portée, il est détruit et la mémoire est libérée
</code></pre>

## Changement de propriétaire (2/3)

- Lorsqu'elle donne ses données à un autre propriétaire, la variable devient invalidée et non-initialisée

<pre><code data-trim="hljs rust" class="lang-rust">
fn main() 
{
    let mut y = Vec::new(); // un vecteur dont la taille est variable
    // On rajoute des éléments au vecteur avec push(elem)
    y.push(1); // rust infère que le type des éléments est entier
    y.push(2);
    y.push(3);
    y.push(4); // on lui a rajouté 4 éléments
    println!("La valeur de y est: {:?}", y);

    let z = y; // le vecteur (1,2,3,4) est maintenant propriété de z
    		       // y est  une variable non initialisée
   println!("La valeur de y est: {:?}", y);
} // Ce code ne compilera pas.
</code></pre>

## Changement de propriétaire (3/3)

- La variable $y$ et copiée dans la variable $z$.
- En ne faisant rien on a **deux** propriétaires des données.
- Illégal: on invalide $y$.

![](figs/mem_mv.svg){height=70%}

## Exception au `move`

- Les types de base de Rust sont stockés sur la pile (entiers, flottants, énumérés, ...).
- Une copie des données est donc rapide.
- Lorsqu'on effectuera une assignation de ces types, on effectuera d'abord une copie, puis l'assignation (le type est dit `Copy`{.lang-rust}).

<pre><code data-trim="hljs rust" class="lang-rust">
fn main() 
{
    let y = 1;
    let mut z = y; // ici si y avait été de type vecteur ont aurait fait un move
    				// Comme y est un i32, on en fait d'abord une copie puis 
    				// on assigne la valeur à z

    println!("Les valeurs de y et z sont : ({}, {})",  y, z);

    z = 2; // comme la valeur est copiée modifier z ne modifie pas y
    println!("Les valeurs de y et z sont : ({}, {})",  y, z);
} // Ce code compilera et s'exécutera.
</code></pre>

## Différence entre Copie et Move

- Lors du `move`{.lang-rust} on copie uniquement la variable, pas les données et le propriétaire des données **change**.
- Lors de la copie on duplique la variable **et** les données.
![](figs/mem_cp.svg){height=70%}
- Pour effectuer une copie on utilise `.clone()`{.lang-rust}.

## Quand interviennent les `move`{.lang-rust}?

- Si le type de la variable **n'est pas** `Copy`{.lang-rust}:
	1. Lors d'une assignation.
	2. Lors du passage en paramètre à une fonction.
	3. Lors du retour d'une fonction.

## Lors du passage en paramètre à une fonction

<pre><code data-trim="hljs rust" class="lang-rust">
fn take_own(_v: Vec&lt;i32&gt;) {
	// on fait des choses
}

fn main() 
{
    let mut y = Vec::new(); // un vecteur dont la taille est variable
    // On rajoute des éléments au vecteur avec push(elem)
    y.push(1); y.push(2); y.push(3); y.push(4);

    take_own(y);

	println!("La valeur de y est: {:?}", y);
} // A votre avis que se passe-t-il?
</code></pre>

## Lors du retour d'une fonction

<pre><code data-trim="hljs rust" class="lang-rust">
fn give_own() -> Vec&lt;i32&gt; {
	let mut y = Vec::new(); // un vecteur dont la taille est variable
    // On rajoute des éléments au vecteur avec push(elem)
    y.push(1); y.push(2); y.push(3); y.push(4);

	v // on retourne v
}

fn main() 
{
	let y = give_own();

	println!("La valeur de y est: {:?}", y);
} // A votre avis que se passe-t-il?
</code></pre>

## Un mélange des deux

<pre><code data-trim="hljs rust" class="lang-rust">
fn get_len(v: Vec&lt;i32&gt;) -> (Vec&lt;i32&gt;, usize) {
	let length = v.len();     // on ajoute 2 au vecteur

	(v, length) // on retourne v et sa longueur
}

fn main() 
{
	let mut y = Vec::new();
	y.push(1); y.push(2); y.push(3); y.push(4);

	let (y, length) = get_len(y);

	println!("La valeur de y est: {:?} et sa longueur {}", y, length);
} // A votre avis que se passe-t-il?
</code></pre>

# L'emprunt (Borrowing)

## La référence

- Le `move`{.lang-rust} est trop contraignant, la copie lente (et pas toujours ce qu'on veut).
- Il serait donc pratique de pouvoir **emprunter** les objets.
- Le **borrowing** permet d'accéder aux données sans avoir la propriété de l'objet.
- Cela se fait à l'aide d'une **référence** sur l'objet qu'on souhaite emprunter.
- Si `y` est une variable, `&y`est la référence vers la variable (le pointeur vers cette variable).
- La référence permet **l'emprunt** de données **sans** en prendre la **propriété**.

![](figs/mem_ref_vec.svg){height=70%}

## Exemple 1

<pre><code data-trim="hljs rust" class="lang-rust">
fn get_len(v: &Vec&lt;i32&gt;) -> usize {
	v.len()
} 
// on sort de la prtée de la fonction, 
// la propriété des données dans v est rendue.

fn main() 
{
	let mut y = Vec::new();
	y.push(1); y.push(2); y.push(3); y.push(4);

	let length = get_len(&y); // la référence vers y est passée

	println!("La valeur de y est: {:?} et sa longueur {}", y, length);
}
</code></pre>

## Exemple 2

<pre><code data-trim="hljs rust" class="lang-rust">
fn get_len(v: &Vec&lt;i32&gt;) -> usize {
	v.push(2); // on ajoute 2 à v
	v.len()
} 
// on sort de la prtée de la fonction, 
// la propriété des données dans v est rendue.

fn main() 
{
	let mut y = Vec::new();
	y.push(1); y.push(2); y.push(3); y.push(4);

	let length = get_len(&y); // la référence vers y est passée

	println!("La valeur de y est: {:?} et sa longueur {}", y, length);
}
</code></pre>

## Référence mutable

- Pour modifier les données lors d'un emprunt:
	1. La variable doit être mutable.
	2. La référence doit être mutable.

<pre><code data-trim="hljs rust" class="lang-rust">
fn get_len(v: &mut Vec&lt;i32&gt;) -> usize {
    v.push(2); // on ajoute 2 à v
    v.len()
} 
// on sort de la prtée de la fonction, 
// la propriété des données dans v est rendue.

fn main() 
{
    let mut y = Vec::new();
    y.push(1); y.push(2); y.push(3); y.push(4);

    let length = get_len(&mut y); // la référence vers y est passée

    println!("La valeur de y est: {:?} et sa longueur {}", y, length);
}
</code></pre>

## Règles pour les références

- Sur une variable on peut avoir:
    1. Autant de **références immutables** qu'on veut.
    2. Une seule **référence mutable**.
- La référence doit toujours être **valide**.

## Dangling pointer ("pointeur pendouillant")

- En désallouant de la mémoire mais en ne détruisant pas le pointeur vers cette mémoire on crée un **dangling pointer**.
- Très dangereux, car on ne sait pas du tout ce qui se pourrait se passer si on essaie de suivre le pointeur pendouillant.
- Tout un tas de langages autorise ce comportement indéfini.
- En Rust cela est **impossible**.
- Exemple :

<pre><code data-trim="hljs rust" class="lang-rust">
fn dangling() -> &Vec&lt;i32&gt; { // la fonction retourne une référence vers un pointeur
	let mut v = Vec::new();
	v.push(1); v.push(2); // on a créé un vec avec 1,2 dedans.

	&v; // on retourne une réf vers v
} // v sort de la portée de la fonction et est détruit:
  // la mémoire est libérée.

fn main() {
    let dangling_reference = dangling();
}
</code></pre>



## Exemples (1/2)

<pre><code data-trim="hljs rust" class="lang-rust">
fn main() 
{
    let mut y = Vec::new();
    y.push(1); y.push(2); y.push(3); y.push(4);

    let y1 = &y;
    let y2 = &y;

    println!("La valeur de y1 et y2 sont: {:?}, {:?}.", y1, y2);
}
</code></pre>

## Exemples (2/2)

<pre><code data-trim="hljs rust" class="lang-rust">
fn main() 
{
    let mut y = Vec::new();
    y.push(1); y.push(2); y.push(3); y.push(4);

    {
        let mut y1 = &mut y;
        y1.push(7);
    }

    println!("La valeur de y est: {:?}.", y);
}
</code></pre>

