Les langages se rangent en deux camps pour gérer la mémoire : un ramasse-miettes
qui passe derrière vous (Java, Go, Python), ou un free() manuel que vous
risquez d’oublier (C, C++). Rust refuse les deux. Sa réponse tient en une phrase :
chaque valeur a un propriétaire unique, et quand le propriétaire disparaît, la
valeur est libérée. C’est l’ownership.
Le move : transférer, pas copier
Affecter une valeur ne la duplique pas — ça transfère sa possession :
let a = String::from("Ada");
let b = a; // la possession de la String passe de `a` à `b`
println!("{a}"); // ❌ erreur : `a` ne possède plus rien — value movedPour beaucoup de débutants c’est un choc : let b = a; invalide a. C’est un
move. Rust interdit d’utiliser a ensuite, parce que sinon a et b
pointeraient vers la même mémoire — et tous deux tenteraient de la libérer en fin
de portée. C’est le bug du double free, rendu tout simplement impossible.
Pourquoi pas une copie ?
Parce qu’une String possède un tampon sur le tas. Copier a dans b
demanderait soit de partager le tampon (et revoilà le double free), soit de le
cloner — une allocation que vous n’avez pas demandée. Rust choisit l’option
la plus honnête : il déplace, gratuitement, et vous laisse cloner explicitement
si vous le voulez vraiment :
let a = String::from("Ada");
let b = a.clone(); // copie profonde, explicite : deux tampons distincts
println!("{a} et {b}"); // ✅ `a` est toujours valideLes types simples posés sur la pile (i32, bool, char…) implémentent Copy :
eux sont copiés bit à bit, sans move, parce que ça ne coûte rien et qu’il n’y a
aucun tampon à libérer.
Le résultat : Drop au bon moment
Quand b sort de portée, Rust appelle drop et libère le tampon — exactement
une fois, exactement là où il faut. Pas de GC qui se réveille à un moment
imprévisible, pas de free() oublié. La gestion mémoire devient une propriété
du type, vérifiée à la compilation.
C’est la fondation de tout Rust : maîtrisez le move, et le reste (emprunts, lifetimes, concurrence) n’est que la suite logique de cette même idée.
Languages fall into two camps for memory management: a garbage collector that
cleans up behind you (Java, Go, Python), or a manual free() you might forget
(C, C++). Rust rejects both. Its answer fits in one sentence: every value has a
single owner, and when the owner goes away, the value is freed. That’s
ownership.
The move: transfer, don’t copy
Assigning a value doesn’t duplicate it — it transfers ownership:
let a = String::from("Ada");
let b = a; // ownership of the String moves from `a` to `b`
println!("{a}"); // ❌ error: `a` owns nothing anymore — value movedFor many beginners it’s a shock: let b = a; invalidates a. That’s a
move. Rust forbids using a afterwards, because otherwise a and b would
point to the same memory — and both would try to free it when they go out of
scope. That’s the double free bug, simply made impossible.
Why not a copy?
Because a String owns a heap buffer. Copying a into b would mean either
sharing the buffer (double free again) or cloning it — an allocation you
never asked for. Rust picks the honest option: it moves, for free, and lets you
clone explicitly when you truly want to:
let a = String::from("Ada");
let b = a.clone(); // explicit deep copy: two separate buffers
println!("{a} and {b}"); // ✅ `a` is still validSimple stack types (i32, bool, char…) implement Copy: they’re copied
bit for bit, no move, because it costs nothing and there’s no buffer to free.
The payoff: Drop at the right moment
When b goes out of scope, Rust calls drop and frees the buffer — exactly
once, exactly where it should. No GC waking up at an unpredictable time, no
forgotten free(). Memory management becomes a property of the type, checked
at compile time.
This is the foundation of all of Rust: master the move, and the rest (borrows, lifetimes, concurrency) is just the logical continuation of the same idea.