# Rust : Les bases pour développeurs expérimentés en 2026 > Apprenez Rust rapidement en partant de vos acquis. Ownership, borrowing, lifetimes et patterns essentiels expliqués pour développeurs venant de C++, Java ou Python. - Published: 2026-01-09 - Updated: 2026-03-28 - Author: SharpSkill - Tags: rust, systems programming, ownership, memory safety, performance - Reading time: 14 min --- Rust gagne en popularité chaque année, et pour cause : sécurité mémoire garantie à la compilation, performances équivalentes au C++, et un écosystème moderne. Pour un développeur expérimenté venant de C++, Java ou Python, l'apprentissage de Rust peut sembler déroutant au début, mais les concepts fondamentaux deviennent rapidement intuitifs une fois compris. > **Pourquoi Rust en 2026 ?** > > Rust est le langage le plus apprécié sur Stack Overflow depuis 8 ans consécutifs. Adopté par Microsoft, Google, Amazon et Meta pour des composants critiques, il offre la sécurité mémoire sans garbage collector. ## Configuration de l'environnement Rust Avant de coder, il faut installer Rust via rustup, l'outil officiel de gestion des versions. ```bash # install.sh # Installation de Rust via rustup (macOS, Linux) curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Vérification de l'installation rustc --version cargo --version ``` Cargo est le gestionnaire de paquets et outil de build de Rust. Il combine les fonctionnalités de npm, Maven et Make en un seul outil cohérent. ```bash # project-setup.sh # Création d'un nouveau projet cargo new mon_projet cd mon_projet # Structure générée : # mon_projet/ # ├── Cargo.toml # Manifeste (équivalent package.json) # └── src/ # └── main.rs # Point d'entrée # Commandes essentielles cargo build # Compile le projet cargo run # Compile et exécute cargo test # Lance les tests cargo check # Vérifie sans compiler (plus rapide) ``` ## Variables et immutabilité par défaut Rust inverse la convention habituelle : les variables sont **immutables par défaut**. Cette approche force à réfléchir explicitement à la mutabilité et prévient de nombreux bugs. ```rust // variables.rs fn main() { // Variable immutable par défaut let x = 5; // x = 6; // Erreur de compilation ! // Variable mutable explicite let mut y = 5; y = 6; // OK // Shadowing : redéclaration dans le même scope let x = x + 1; // Crée une nouvelle variable x let x = x * 2; // x vaut maintenant 12 // Le shadowing permet aussi de changer le type let spaces = " "; // &str let spaces = spaces.len(); // usize } ``` > **Shadowing vs mutabilité** > > Le shadowing crée une nouvelle variable, contrairement à `mut` qui modifie la valeur existante. Le shadowing permet de transformer une valeur tout en gardant un nom clair. ## Types de données fondamentaux Rust est statiquement typé avec une excellente inférence de types. Voici les types primitifs essentiels à connaître. ```rust // types.rs fn main() { // Entiers signés : i8, i16, i32, i64, i128, isize let age: i32 = 30; // Entiers non signés : u8, u16, u32, u64, u128, usize let count: u64 = 1_000_000; // Underscores pour lisibilité // Flottants : f32, f64 (défaut) let pi: f64 = 3.14159; // Booléen let active: bool = true; // Caractère Unicode (4 bytes) let emoji: char = '🦀'; // Tuple : collection fixe de types différents let person: (String, i32) = (String::from("Alice"), 28); let (name, age) = person; // Destructuring // Array : taille fixe, même type let numbers: [i32; 5] = [1, 2, 3, 4, 5]; let first = numbers[0]; // Slice : vue sur une portion de données let slice: &[i32] = &numbers[1..4]; // [2, 3, 4] } ``` ## Ownership : le concept révolutionnaire L'ownership est LA innovation de Rust. Ce système garantit la sécurité mémoire sans garbage collector, au prix d'une courbe d'apprentissage initiale. ### Les trois règles de l'ownership ```rust // ownership.rs fn main() { // Règle 1 : Chaque valeur a un unique propriétaire let s1 = String::from("hello"); // Règle 2 : Quand le propriétaire sort du scope, la valeur est libérée { let s2 = String::from("world"); // s2 est valide ici } // s2 est libéré (drop), plus accessible // Règle 3 : Une seule ownership à la fois (move) let s3 = s1; // s1 est MOVE vers s3 // println!("{}", s1); // Erreur : s1 n'est plus valide ! println!("{}", s3); // OK : s3 est le propriétaire } ``` ### Move vs Clone ```rust // move_clone.rs fn main() { // Types simples (stack) : Copy automatique let x = 5; let y = x; // Copy, pas move println!("x = {}, y = {}", x, y); // Les deux sont valides // Types complexes (heap) : Move par défaut let s1 = String::from("hello"); let s2 = s1; // Move // s1 n'est plus utilisable // Clone explicite pour dupliquer let s3 = String::from("world"); let s4 = s3.clone(); // Deep copy println!("s3 = {}, s4 = {}", s3, s4); // Les deux valides } ``` > **Move et fonctions** > > Passer une valeur à une fonction transfère l'ownership. La fonction devient propriétaire et la valeur n'est plus accessible après l'appel, sauf si elle est retournée. ```rust // ownership_functions.rs fn main() { let s = String::from("hello"); takes_ownership(s); // println!("{}", s); // Erreur : s a été moved let x = 5; makes_copy(x); println!("{}", x); // OK : i32 implémente Copy // Pour récupérer l'ownership, retourner la valeur let s2 = String::from("hello"); let s3 = takes_and_gives_back(s2); println!("{}", s3); // OK } fn takes_ownership(s: String) { println!("{}", s); } // s est dropped ici fn makes_copy(x: i32) { println!("{}", x); } fn takes_and_gives_back(s: String) -> String { s // Retourne l'ownership } ``` ## Borrowing : références sans transfert Le borrowing permet d'utiliser une valeur sans en prendre l'ownership. C'est le mécanisme le plus utilisé en Rust. ```rust // borrowing.rs fn main() { let s1 = String::from("hello"); // Référence immutable : lecture seule let len = calculate_length(&s1); println!("Longueur de '{}' : {}", s1, len); // s1 toujours valide // Référence mutable : modification possible let mut s2 = String::from("hello"); change(&mut s2); println!("{}", s2); // "hello, world" } fn calculate_length(s: &String) -> usize { s.len() } // s sort du scope mais ne drop pas car c'est une référence fn change(s: &mut String) { s.push_str(", world"); } ``` ### Règles du borrowing ```rust // borrowing_rules.rs fn main() { let mut s = String::from("hello"); // Règle 1 : Plusieurs références immutables simultanées OK let r1 = &s; let r2 = &s; println!("{} and {}", r1, r2); // Règle 2 : UNE SEULE référence mutable à la fois let r3 = &mut s; // let r4 = &mut s; // Erreur : déjà emprunté mutably println!("{}", r3); // Règle 3 : Pas de ref mutable si ref immutable existe let r5 = &s; // let r6 = &mut s; // Erreur : r5 est encore actif println!("{}", r5); // Une fois r5 utilisé pour la dernière fois, on peut emprunter mut let r7 = &mut s; // OK : r5 n'est plus utilisé après r7.push_str("!"); } ``` ## Lifetimes : garantir la validité des références Les lifetimes s'assurent que les références restent valides. Le compilateur les infère souvent automatiquement, mais parfois une annotation explicite est nécessaire. ```rust // lifetimes_basic.rs // Erreur classique : référence vers donnée libérée // fn dangling() -> &String { // let s = String::from("hello"); // &s // Erreur : s sera dropped, référence invalide ! // } // Solution : retourner la valeur owned fn no_dangle() -> String { let s = String::from("hello"); s // Ownership transféré, pas de problème } ``` ### Annotations de lifetimes ```rust // lifetimes_annotation.rs // Le compilateur ne peut pas deviner quelle référence sera retournée // fn longest(x: &str, y: &str) -> &str { ... } // Erreur ! // Annotation explicite : le retour vit aussi longtemps que x ET y fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } fn main() { let string1 = String::from("long string"); let result; { let string2 = String::from("xyz"); result = longest(&string1, &string2); println!("Longest: {}", result); // OK ici } // println!("{}", result); // Erreur : string2 dropped } ``` > **Élision des lifetimes** > > Rust applique des règles d'élision pour éviter d'annoter les cas simples. Pour les débutants, il suffit de suivre les messages du compilateur qui sont très explicites. ## Structs et implémentations Les structs sont les blocs de construction pour les types personnalisés en Rust. ```rust // structs.rs // Définition d'une struct #[derive(Debug)] // Permet d'afficher avec {:?} struct User { username: String, email: String, active: bool, sign_in_count: u64, } // Bloc d'implémentation pour les méthodes impl User { // Constructeur (convention : fn new ou fn nom_explicite) fn new(username: String, email: String) -> Self { Self { username, email, active: true, sign_in_count: 1, } } // Méthode : prend &self (référence à l'instance) fn is_active(&self) -> bool { self.active } // Méthode avec mutation : prend &mut self fn deactivate(&mut self) { self.active = false; } // Méthode consommant self (rare) fn into_username(self) -> String { self.username } } fn main() { let mut user = User::new( String::from("alice"), String::from("alice@example.com"), ); println!("Active: {}", user.is_active()); user.deactivate(); println!("Active: {}", user.is_active()); // Debug print println!("{:?}", user); } ``` ## Enums et Pattern Matching Les enums Rust sont bien plus puissants que dans la plupart des langages : chaque variant peut contenir des données. ```rust // enums.rs // Enum simple enum Direction { North, South, East, West, } // Enum avec données (algebraic data type) enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(u8, u8, u8), } impl Message { fn process(&self) { match self { Message::Quit => println!("Quitting"), Message::Move { x, y } => println!("Moving to ({}, {})", x, y), Message::Write(text) => println!("Writing: {}", text), Message::ChangeColor(r, g, b) => { println!("Color: rgb({}, {}, {})", r, g, b) } } } } fn main() { let msg = Message::Move { x: 10, y: 20 }; msg.process(); let msg2 = Message::Write(String::from("Hello Rust!")); msg2.process(); } ``` ### Option et Result : gestion des erreurs ```rust // option_result.rs use std::fs::File; use std::io::{self, Read}; fn main() { // Option : valeur présente ou absente (remplace null) let numbers = vec![1, 2, 3]; let first: Option<&i32> = numbers.first(); match first { Some(n) => println!("Premier : {}", n), None => println!("Liste vide"), } // Méthodes utilitaires let value = first.unwrap_or(&0); let doubled = first.map(|n| n * 2); // Result : succès ou erreur let result = read_file("config.txt"); match result { Ok(content) => println!("Contenu : {}", content), Err(e) => println!("Erreur : {}", e), } } fn read_file(path: &str) -> Result { let mut file = File::open(path)?; // ? propage l'erreur let mut content = String::new(); file.read_to_string(&mut content)?; Ok(content) } ``` > **L'opérateur ?** > > L'opérateur `?` est du sucre syntaxique pour propager les erreurs. Il retourne automatiquement l'erreur si Result est Err, sinon il unwrap la valeur Ok. ## Traits : polymorphisme à la Rust Les traits définissent un comportement partagé, similaires aux interfaces Java ou aux protocols Swift. ```rust // traits.rs // Définition d'un trait trait Summary { fn summarize(&self) -> String; // Méthode avec implémentation par défaut fn preview(&self) -> String { format!("{}...", &self.summarize()[..50.min(self.summarize().len())]) } } struct Article { title: String, author: String, content: String, } struct Tweet { username: String, content: String, } // Implémentation pour Article impl Summary for Article { fn summarize(&self) -> String { format!("{} by {}", self.title, self.author) } } // Implémentation pour Tweet impl Summary for Tweet { fn summarize(&self) -> String { format!("@{}: {}", self.username, self.content) } } // Fonction acceptant tout type implémentant Summary fn notify(item: &impl Summary) { println!("Breaking news: {}", item.summarize()); } // Syntaxe équivalente avec trait bound fn notify_generic(item: &T) { println!("Breaking news: {}", item.summarize()); } fn main() { let article = Article { title: String::from("Rust 2026"), author: String::from("Community"), content: String::from("..."), }; let tweet = Tweet { username: String::from("rustlang"), content: String::from("Rust is awesome!"), }; notify(&article); notify(&tweet); } ``` ## Collections essentielles Rust fournit des collections puissantes dans la bibliothèque standard. ```rust // collections.rs use std::collections::HashMap; fn main() { // Vec : tableau dynamique let mut numbers: Vec = Vec::new(); numbers.push(1); numbers.push(2); numbers.push(3); // Macro vec! pour initialisation let nums = vec![1, 2, 3, 4, 5]; // Itération for n in &nums { println!("{}", n); } // Méthodes fonctionnelles let doubled: Vec = nums.iter().map(|x| x * 2).collect(); let sum: i32 = nums.iter().sum(); let evens: Vec<&i32> = nums.iter().filter(|x| *x % 2 == 0).collect(); // String : chaîne UTF-8 growable let mut s = String::from("Hello"); s.push_str(", World!"); s.push('!'); // Concaténation let s1 = String::from("Hello, "); let s2 = String::from("World!"); let s3 = s1 + &s2; // s1 moved, s2 borrowed // ou avec format! let s4 = format!("{}{}", "Hello, ", "World!"); // HashMap let mut scores: HashMap = HashMap::new(); scores.insert(String::from("Blue"), 10); scores.insert(String::from("Red"), 50); // Accès avec get (retourne Option) if let Some(score) = scores.get("Blue") { println!("Blue: {}", score); } // Entry API pour insertion conditionnelle scores.entry(String::from("Yellow")).or_insert(25); } ``` ## Gestion des erreurs idiomatique Une bonne gestion des erreurs est essentielle en Rust. Voici les patterns recommandés. ```rust // error_handling.rs use std::fs::File; use std::io::{self, Read}; use std::num::ParseIntError; // Définir un type d'erreur custom #[derive(Debug)] enum AppError { Io(io::Error), Parse(ParseIntError), Custom(String), } // Implémenter From pour conversion automatique impl From for AppError { fn from(err: io::Error) -> Self { AppError::Io(err) } } impl From for AppError { fn from(err: ParseIntError) -> Self { AppError::Parse(err) } } // Fonction retournant Result avec erreur custom fn read_number_from_file(path: &str) -> Result { let mut file = File::open(path)?; // io::Error -> AppError let mut content = String::new(); file.read_to_string(&mut content)?; let number: i32 = content.trim().parse()?; // ParseIntError -> AppError Ok(number) } fn main() { match read_number_from_file("number.txt") { Ok(n) => println!("Number: {}", n), Err(AppError::Io(e)) => println!("IO error: {}", e), Err(AppError::Parse(e)) => println!("Parse error: {}", e), Err(AppError::Custom(msg)) => println!("Error: {}", msg), } } ``` > **Crates recommandées** > > Pour des projets réels, les crates `thiserror` (pour les libraries) et `anyhow` (pour les applications) simplifient grandement la gestion des erreurs. ## Tests en Rust Rust intègre un framework de test directement dans le langage. ```rust // lib.rs pub fn add(a: i32, b: i32) -> i32 { a + b } pub fn divide(a: i32, b: i32) -> Result { if b == 0 { Err(String::from("Division by zero")) } else { Ok(a / b) } } // Module de tests (compilé uniquement pour `cargo test`) #[cfg(test)] mod tests { use super::*; #[test] fn test_add() { assert_eq!(add(2, 3), 5); } #[test] fn test_add_negative() { assert_eq!(add(-1, 1), 0); } #[test] fn test_divide_success() { assert_eq!(divide(10, 2), Ok(5)); } #[test] fn test_divide_by_zero() { assert!(divide(10, 0).is_err()); } #[test] #[should_panic(expected = "index out of bounds")] fn test_panic() { let v = vec![1, 2, 3]; let _ = v[99]; // Panic ! } } ``` ## Conclusion Rust offre un paradigme unique qui combine sécurité mémoire et performances. Les concepts d'ownership et de borrowing semblent contraignants au début, mais deviennent naturels avec la pratique. Le compilateur Rust est un allié précieux : ses messages d'erreur sont parmi les meilleurs de l'industrie. ### Checklist pour bien démarrer - ✅ Installer Rust via rustup et maîtriser Cargo - ✅ Comprendre la différence entre immutabilité et mutabilité explicite - ✅ Maîtriser les trois règles de l'ownership - ✅ Pratiquer le borrowing avec références `&` et `&mut` - ✅ Utiliser `Option` et `Result` au lieu de null et exceptions - ✅ Écrire des tests avec `#[test]` La communauté Rust est accueillante et les ressources abondantes. Le livre officiel "The Rust Programming Language" est disponible gratuitement en ligne. Avec ces bases solides, vous êtes prêt à explorer des sujets avancés comme async/await, les macros et WebAssembly. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/fr/blog/rust/rust-bases-developpeurs-experimentes