August 9, 20257 min

Partie 3 : Rust: Memory safety sans garabe collector

m
mayo

Rust n'a pas de GC. Il n'en a pas besoin.

let msg = String::from("hello");

Cela alloue de la mémoire—mais Rust track l'ownership déjà à la compilation avec l'ownership.

La Révolution "Ownership"

Gestion Automatique de la Mémoire

fn greet() {
    let s = String::from("hello");
    // Utiliser s...
} // s est dropped ici automatiquement - pas besoin de free() manuel

Ce qui se passe :

  1. Mémoire allouée quand s est créé
  2. Mémoire automatiquement libérée quand s sort du scope
  3. Pas de thread GC qui tourne en background
  4. Pas d'overhead runtime

Fini les Use-After-Free

fn main() {
    let r;
    {
        let s = String::from("hello");
        r = &s;  // Borrow s
    } // s sort du scope ici
    
    println!("{}", r); // ❌ Erreur compile: s doesn't live long enough
}

Message du compilateur :

error[E0597]: `s` does not live long enough
  --> src/main.rs:5:13
   |
5  |         r = &s;
   |             ^^ borrowed value does not live long enough
6  |     }
   |     - `s` dropped here while still borrowed

Le bug est detecté au moment de la compilation, pas au runtime.

Borrowing: manipulation de référence sans danger

Immutable Borrowing

fn calculate_length(s: &String) -> usize {
    s.len()  // Peut lire s, mais pas le modifier
} // s sort du scope, mais ne drop pas le String (c'est juste une reference)

fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1);  // Passer une reference
    println!("Length of '{}' is {}.", s1, len);  // s1 encore valide
}

Mutable Borrowing est soumis à certaines règles

fn main() {
    let mut s = String::from("hello");
    
    let r1 = &mut s;  // Mutable borrow
    // let r2 = &mut s;  // ❌ Cannot have two mutable borrows
    // let r3 = &s;      // ❌ Cannot have immutable borrow while mutable exists
    
    r1.push_str(", world");
    println!("{}", r1);
}

Les règles de borrowing préviennent :

  • Data races au moment de la compilation
  • Dangling pointers
  • Iterator invalidation
  • Problèmes de thread safety

Comparaison Réelle

La Même Logique dans Différents Langages

Version C (unsafe) :

char* process_data(char* input) {
    char* result = malloc(strlen(input) + 10);
    strcpy(result, input);
    strcat(result, " processed");
    return result;  // L'appelant doit se rappeler de free !
}

int main() {
    char* data = "hello";
    char* processed = process_data(data);
    printf("%s\n", processed);
    // Facile d'oublier: free(processed);
    return 0;
}

Version Java (overhead GC) :

public String processData(String input) {
    return input + " processed";  // Crée des objets temporaires
}

public static void main(String[] args) {
    String data = "hello";
    String processed = processData(data);
    System.out.println(processed);
    // Le GC va finalement collecter les objets temporaires
}

Version Rust (safe + fast) :

fn process_data(input: &str) -> String {
    format!("{} processed", input)  // Mémoire gérée automatiquement
}

fn main() {
    let data = "hello";
    let processed = process_data(data);
    println!("{}", processed);
    // la variable processed est automatiquement supprimé à la fin du scope
}

Caractéristiques Performance

Zero-Cost Abstractions

Un code de haut niveau en apparence est traduit en code de bas niveau à la compilation.

// Code haut niveau...
let numbers: Vec<i32> = (0..1_000_000).collect();
let sum: i32 = numbers.iter().sum();

// ...compile vers le même code assembleur que:
let mut sum = 0;
for i in 0..1_000_000 {
    sum += i;
}

Et il est même possible de contrôler l'emprunte mémoire

#[repr(C)]  // Même emprunte mémoire qu'un struct en C
struct Point {
    x: f32,
    y: f32,
    z: f32,
}

let points = vec![Point { x: 1.0, y: 2.0, z: 3.0 }; 1000];
// Emprunte mémoire contigu, pas d'overhead GC

Sécurité niveau thread

Prévention des Data Race (2 thread qui tentent d'accéder à la même ressource dont un en écriture et qui ne sont pas synchronisés)

use std::thread;

fn main() {
    let data = vec![1, 2, 3, 4, 5];
    
    thread::spawn(move || {
        println!("Data: {:?}", data);  // data moved vers le thread
    });
    
    // println!("{:?}", data);  // ❌ Erreur compile: data was moved
}

Accès Concurrent Sécurisé

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Result: {}", *counter.lock().unwrap());
}

Pas de data races possibles - vérifié au moment de la compilation.

Comparaison des Features

Feature Rust C Java Python
Free manuel
Thread GC
Memory safety compile-time
Garanties thread safety
Zero runtime overhead
Contrôle memory layout
Prévient use-after-free
Prévient double-free
Prévient memory leaks ✅* ✅*

*Les langages GC peuvent avoir des memory leaks via les references

La Garantie Rust

Ce que Rust Élimine

Memory leaks - cleanup automatique
Use-after-free - tracking d'ownership
Double-free - ownership unique
Dangling pointers - analyse des lifetimes
Buffer overflows - bounds checking
Data races - règles de borrowing
Iterator invalidation - vérifications compile-time

Ce que tu obtiens

🚀 Performance niveau C
🛡️ Memory safety
Zero runtime overhead
🔒 Thread safety
🔧 Capacités systems programming

Success Stories Réelles

Dropbox Magic Pocket

  • Ils ont remplacé Python par Rust pour le système de stockage
  • Performance : 10x d'amélioration en efficacité CPU
  • Mémoire : Usage prévisible, pas de pauses GC
  • Fiabilité : Éliminé des classes entières de bugs

Discord Chat Service

  • Ils ont remplacé Go par Rust pour la gestion des messages
  • Latence : Temps de réponse constants sub-milliseconde
  • Mémoire : Réduction de 40% de l'usage mémoire
  • Scaling : Gère des millions de connexions concurrentes

Mozilla Firefox

  • Composants Rust dans le moteur de navigateur (Servo)
  • Sécurité : Éliminé les vulnérabilités memory safety
  • Performance : Rendu plus rapide, usage mémoire plus bas

Le Changement de Paradigme

Approche Traditionnelle

Code rapide → Gestion manuelle mémoire → Bugs
Code sûr → Garbage collection → Overhead performance

Approche Rust

Compilateur intelligent → Système ownership → Code rapide + sûr

Points Clés

🦀 Rust réunit le meilleur des deux mondes :

Performance prévisible - pas de pauses GC, pas d'overhead runtime
Sécurité mémoire - classes entières de bugs éliminées au moment de la compilation
Concurrence en mode zen - data races préventées par le type system
Programmation systéme - contrôle bas niveau quand nécessaire
Ergonomie moderne - type system puissant, gestion de packages grâce à cargo


TL;DR

L'Évolution :

  1. C : Rapide mais dangereux
  2. Java/Python/JS : Sûr mais lent (overhead GC)
  3. Rust : Rapide ET sûr (garanties compile-time)

Rust n'est pas "C plus sûr". C'est un contrat fondamentalement différent :

"Tu n'as pas besoin d'un runtime pour être sûr — juste d'un compilateur intelligent."

Le Résultat : Sécurité màmoire et "zero-cost abstraction". Le saint graal de la programmation système.


Retour au blog
Partager ::