Partie 3 : Rust: Memory safety sans garabe collector
Table des matières
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 :
- Mémoire allouée quand
sest créé - Mémoire automatiquement libérée quand
ssort du scope - Pas de thread GC qui tourne en background
- 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 :
- C : Rapide mais dangereux
- Java/Python/JS : Sûr mais lent (overhead GC)
- 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.