Apprendre Rust : le guide pour bien démarrer
Apprendre Rust, c’est adopter un langage qui a rebattu les cartes de la programmation système. Sa promesse est singulière : la performance du C ou du C++, mais avec des garanties de sécurité mémoire vérifiées à la compilation, sans ramasse-miettes. Réputé exigeant, Rust récompense l’effort par une confiance rare dans son code. Ce guide vous accompagne pas à pas : installation, premier programme, et surtout ses concepts phares — l’ownership et le borrow — qui font toute sa singularité.
Pourquoi Rust ?
Rust répond à un problème vieux comme la programmation système : comment gérer la mémoire manuellement sans introduire de bugs dangereux ? Les langages comme le C offrent le contrôle mais laissent la porte ouverte aux erreurs mémoire (accès à une zone libérée, dépassement de tampon). Rust apporte une réponse originale :
- Sécurité mémoire garantie : le compilateur refuse le code susceptible de provoquer ces erreurs, sans coût à l’exécution.
- Performance native : Rust compile en code machine, sans ramasse-miettes qui interromprait le programme.
- Concurrence sereine : les mêmes règles qui protègent la mémoire préviennent une large classe de bugs de programmation parallèle.
Ces atouts expliquent son adoption croissante dans les systèmes d’exploitation, les navigateurs, les outils en ligne de commande et l’embarqué. Si la comparaison avec les langages historiques vous intéresse, notre article Rust vs C++ creuse la question. Rust demande de la patience au début, mais forme durablement au raisonnement sur la mémoire.
Installer Rust avec rustup
L’outil officiel d’installation est rustup, qui gère le compilateur et les versions. Sur macOS et Linux, une seule commande suffit ; sur Windows, un installeur est fourni sur le site officiel. La commande classique est :
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Une fois l’installation terminée, vérifiez que tout fonctionne :
rustc --version
cargo --version
rustc est le compilateur, mais vous l’appellerez rarement directement. En pratique, on passe par Cargo, l’outil tout-en-un de Rust : il gère la compilation, les dépendances, les tests et la documentation. Côté éditeur, VS Code avec l’extension rust-analyzer offre une excellente autocomplétion et signale les erreurs en temps réel.
Votre premier programme avec Cargo
Cargo structure vos projets. Créez-en un nouveau :
cargo new bonjour
cd bonjour
cargo run
Cargo génère l’arborescence, compile et exécute. Le fichier src/main.rs contient le point d’entrée :
fn main() {
let nom = "Coder Studio";
println!("Bonjour, {nom} !");
}
Quelques observations utiles. La fonction main est le point de départ ; let déclare une variable ; println! est une macro (le ! la distingue d’une fonction ordinaire) qui gère l’affichage formaté. Notez aussi qu’en Rust, les variables sont immuables par défaut : une fois affectée, une valeur ne change plus, sauf mention explicite.
fn main() {
let mut compteur = 0; // mut = mutable
compteur += 1;
println!("Compteur : {compteur}");
}
Ce choix par défaut n’est pas anodin : il pousse à un code plus prévisible, où l’on sait exactement ce qui peut changer.
Les types essentiels
Rust est fortement et statiquement typé, mais il infère souvent le type, ce qui allège l’écriture. Les types fondamentaux ressemblent à ceux des autres langages :
fn main() {
let age: i32 = 30; // entier signé 32 bits
let prix: f64 = 19.90; // flottant 64 bits
let actif: bool = true; // booléen
let lettre: char = 'A'; // caractère Unicode
let nom: &str = "Léa"; // chaîne (référence)
}
Pour regrouper des données, Rust propose notamment les tuples, les tableaux de taille fixe et les vecteurs (Vec) de taille variable :
fn main() {
let point: (i32, i32) = (48, 2);
let mut courses: Vec<&str> = vec!["pain", "lait"];
courses.push("œufs");
for article in &courses {
println!("{article}");
}
}
Le Vec est la structure de collection la plus utilisée au quotidien. Remarquez le & devant courses dans la boucle : nous empruntons la collection au lieu de la consommer — ce qui nous amène au cœur de Rust.
Ownership : le concept central
L’ownership (propriété) est l’idée qui distingue Rust de tous les autres langages grand public. Elle repose sur trois règles simples :
- Chaque valeur a un unique propriétaire (owner).
- Il ne peut y avoir qu’un seul propriétaire à la fois.
- Quand le propriétaire sort de portée, la valeur est automatiquement libérée.
Concrètement, affecter une valeur non copiable à une autre variable transfère sa propriété :
fn main() {
let a = String::from("bonjour");
let b = a; // la propriété passe de a à b
// println!("{a}"); // ERREUR : a n'est plus valide
println!("{b}"); // OK
}
Ce mécanisme, appelé move, garantit qu’aucune donnée n’est libérée deux fois, sans avoir besoin d’un ramasse-miettes. C’est déroutant au début, mais c’est exactement ce qui rend Rust sûr.
Borrowing : emprunter sans posséder
Transférer la propriété à chaque usage serait pénible. Rust permet donc d’emprunter une valeur via une référence &, sans en prendre la propriété :
fn longueur(texte: &String) -> usize {
texte.len()
} // texte est emprunté, pas consommé
fn main() {
let phrase = String::from("bonjour");
let n = longueur(&phrase);
println!("{phrase} fait {n} caractères"); // phrase reste utilisable
}
Les emprunts obéissent à une règle stricte, vérifiée par le borrow checker : à un instant donné, on peut avoir soit plusieurs références en lecture seule (&), soit une seule référence mutable (&mut), mais jamais les deux. Cette règle élimine à la compilation les conflits d’accès concurrents.
fn main() {
let mut score = 10;
let r = &mut score; // emprunt mutable unique
*r += 5;
println!("{score}"); // 15
}
Bonne pratique — Écouter le compilateur, pas le combattre Face au borrow checker, le réflexe du débutant est de le contourner. Prenez plutôt ses messages comme un professeur : ils sont réputés parmi les plus pédagogiques de l’industrie et pointent souvent un vrai problème de conception. Comprendre pourquoi le code est refusé est la clé pour progresser.
Gérer l’absence et l’erreur sans null
Rust n’a pas de null. L’absence de valeur se modélise avec le type Option, et les erreurs récupérables avec Result. Le compilateur vous oblige à traiter les deux cas, ce qui supprime toute une catégorie de plantages :
fn diviser(a: f64, b: f64) -> Option<f64> {
if b == 0.0 {
None
} else {
Some(a / b)
}
}
fn main() {
match diviser(10.0, 2.0) {
Some(r) => println!("Résultat : {r}"),
None => println!("Division par zéro"),
}
}
Cette approche, héritée de la programmation fonctionnelle, force à rendre l’échec explicite. C’est plus verbeux qu’un null, mais infiniment plus sûr — le compilateur refuse d’oublier un cas.
Progresser efficacement en Rust
Rust s’apprend en écrivant, en butant sur le borrow checker et en comprenant ses messages. Quelques pistes concrètes pour ancrer les bases :
- Lisez The Rust Programming Language (le « Book »), la référence gratuite et officielle.
- Pratiquez avec Rustlings, une série d’exercices guidés à corriger.
- Construisez un petit outil en ligne de commande : un compteur de mots, un convertisseur. Le terrain CLI met en valeur les forces du langage.
- Explorez crates.io, le registre de bibliothèques, avec des paquets comme
serde(sérialisation) ouclap(arguments CLI).
L’erreur classique est de vouloir aller trop vite en luttant contre le langage. Rust demande d’accepter un modèle mental nouveau. Une fois l’ownership intégré, on écrit du code performant avec une tranquillité d’esprit que peu de langages procurent.