Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Emprunts et références (borrowing)

Pour éviter de devoir transférer la propriété (ownership) d’une valeur et la restituer à chaque fois qu’on la passe à une fonction, Rust utilise les références. Ce mécanisme s’appelle l’emprunt (borrowing).

Une référence est un pointeur garanti comme valide, car le compilateur vérifie qu’il pointe toujours vers une mémoire allouée.

Références immuables (&T)

Une référence immuable permet de lire une donnée sans la modifier. On la crée avec le symbole &.

fn main() {
    let s1 = String::from("hello");

    // On passe une référence (&s1). s1 reste propriétaire.
    let longueur = calculer_longueur(&s1); 

    println!("La longueur de '{s1}' est de {longueur}."); // Valide !
}

fn calculer_longueur(s: &String) -> usize {
    s.len()
} // `s` sort de la portée, mais rien n'est libéré car la fonction n'a pas la propriété.

Références mutables (&mut T)

Pour modifier une valeur empruntée, il faut utiliser une référence mutable avec &mut.

La variable d’origine doit elle aussi être déclarée avec mut.

fn main() {
    let mut s = String::from("Hello");
    modifier(&mut s);
    println!("{s}");
}

fn modifier(s: &mut String) {
    s.push_str(" World!");
}

Les deux règles d’or de l’emprunt

Pour empêcher les accès concurrents invalides et les conflits de données (data races), Rust impose une règle stricte à tout instant dans la portée d’une ressource :

  1. Nous pouvons utiliser autant de références immuables (&T) que nous souhaitons,
  2. OU (XOR) nous pouvons avoir une seule référence mutable (&mut T),

mais pas les deux en même temps.

Exemple d’interdiction de cumul mutable / immuable

    fn main() {
    let mut score = 10;

    let r1 = &score; // Emprunt immuable : OK
    let r2 = &score; // Deuxième emprunt immuable : OK
    // let r3 = &mut score; // ERREUR DE COMPILATION ! Impossible d'emprunter en mutable.
    println!("{r1} et {r2}");
}

Exemple de cumul de mutables interdit

    fn main() {
    let mut score = 10;

    let r1 = &mut score; // Premier emprunt mutable : OK
    // let r2 = &mut score; // ERREUR DE COMPILATION ! Exclusivité rompue.

    println!("{r1}");
}

Portée d’une référence (Non-lexical lifetime)

La portée d’une référence commence là où elle est créée et se termine à sa dernière utilisation, et non pas obligatoirement à la fin du bloc {}. Cela rend le code beaucoup plus flexible :

fn main() {
    let mut valeur = 5;

    let r1 = &valeur; 
    let r2 = &valeur; 
    println!("{r1} et {r2}"); // Fin d'utilisation de r1 et r2

    let r3 = &mut valeur; // Valide ! Les emprunts précédents ne sont plus actifs.
    *r3 += 1;
}

Les références sans donnée sont impossibles

En Rust, le compilateur garantit qu’il n’y aura jamais de référence dans donnée (dangling reference), c’est-à-dire un pointeur qui pointe vers une adresse mémoire libérée.

fn main() {
    // let reference_invalide = créer_reference_pendante();
}

// fn créer_reference_pendante() -> &String {
//     let s = String::from("hello");
//     &s // ERREUR ! s est détruite ici, la référence pointerait vers du vide.
// }