Table des matières
Rust gagne considérablement en popularité parmi les développeurs pour son focus sur la performance, la memory safety, et la concurrence. Si tu es nouveau à Rust, ce guide t'aidera à débuter avec les bases.
Configuration de Ton Environnement
D'abord, tu dois installer Rust. La façon la plus simple est d'utiliser rustup, l'installateur de toolchain Rust :
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Cette commande téléchargera un script et démarrera le processus d'installation.
Vérification de l'Installation
Une fois l'installation terminée, vérifie que tout fonctionne correctement :
rustc --version
cargo --version
Tu dois voir les numéros de version de Rust et Cargo s'afficher.
Configuration de l'Éditeur
Pour une meilleure expérience de développement, configure ton éditeur préféré :
VS Code :
- Installe l'extension "rust-analyzer"
- Installe l'extension "CodeLLDB" pour le debugging
IntelliJ/CLion :
- Installe le plugin "Rust"
Vim/Neovim :
- Utilise rust.vim et coc-rust-analyzer
Ton Premier Programme Rust
Créons un simple programme "Hello, World!". Créé un nouveau fichier appelé hello.rs avec le contenu suivant :
fn main() {
println!("Hello, World!");
}
Pour compiler et exécuter ce programme, utilise les commandes suivantes :
rustc hello.rs
./hello # Sur Windows: hello.exe
Anatomie du Programme
Analysons ce simple programme :
fn main() { // Fonction principale - point d'entrée du programme
println!( // Macro pour imprimer du texte
"Hello, World!" // String à imprimer
); // Point-virgule requis pour terminer l'expression
}
Points importants :
fn main()est la fonction principale, exécutée en premierprintln!est une macro (note le!) qui imprime du texte- Les déclarations se terminent par un point-virgule
;
Comprendre Cargo
Cargo est le système de build et gestionnaire de packages de Rust. Il gère de nombreuses tâches comme compilateur ton code, télécharger des libraries, et compilateur ces libraries.
Créer un Nouveau Projet
Pour créer un nouveau projet avec Cargo :
cargo new hello_cargo
cd hello_cargo
Ceci crée un nouveau répertoire appelé hello_cargo avec la structure suivante :
hello_cargo/
├── Cargo.toml # Métadonnées du projet et dépendances
├── Cargo.lock # Verrouillage des versions (généré automatiquement)
├── .gitignore # Fichier Git ignore
└── src/
└── main.rs # Code source principal
Le Fichier Cargo.toml
Le fichier Cargo.toml contient les métadonnées de ton projet :
[package]
name = "hello_cargo"
version = "0.1.0"
edition = "2021" # Édition de Rust à utiliser
[dependencies]
# Les dépendances seront listées ici
Commandes Cargo Essentielles
cargo build # Compile le projet (debug mode)
cargo build --release # Compile en mode optimisé (release)
cargo run # Compile et exécute le projet
cargo check # Vérifie que le code compile sans générer l'exécutable
cargo clean # Nettoie les fichiers de build
cargo test # Exécute les tests
cargo doc # Génère la documentation
Exemple Pratique avec une Dépendance
Modifions notre projet pour utiliser une dépendance externe. Éditons Cargo.toml :
[package]
name = "hello_cargo"
version = "0.1.0"
edition = "2021"
[dependencies]
rand = "0.8"
Maintenant, modifions src/main.rs :
use rand::Rng; // Importer le trait Rng
fn main() {
println!("Hello, World!");
let secret_number = rand::thread_rng().gen_range(1..=100);
println!("Le nombre secret est : {}", secret_number);
}
Exécute avec cargo run - Cargo téléchargera et compilera automatiquement la dépendance rand.
Concepts Clés de Rust
Variables et Mutabilité
Par défaut, les variables en Rust sont immutables :
fn main() {
let x = 5;
println!("La valeur de x est : {}", x);
// x = 6; // ❌ ERREUR : cannot assign twice to immutable variable
}
Pour rendre une variable mutable, utilise le mot-clé mut :
fn main() {
let mut y = 5;
println!("La valeur de y est : {}", y);
y = 6; // ✅ OK : y est mutable
println!("La valeur de y est maintenant : {}", y);
}
Constantes vs Variables
const PI: f64 = 3.14159; // Constante - toujours immutable
fn main() {
let x = 5; // Variable immutable
let mut y = 10; // Variable mutable
println!("PI = {}, x = {}, y = {}", PI, x, y);
}
Shadowing (Masquage)
Rust permet de "masquer" une variable en déclarant une nouvelle variable avec le même nom :
fn main() {
let x = 5;
let x = x + 1; // Nouveau x qui masque le précédent
let x = x * 2; // Encore un nouveau x
println!("La valeur de x est : {}", x); // 12
// On peut même changer le type
let spaces = " "; // &str
let spaces = spaces.len(); // usize
println!("Nombre d'espaces : {}", spaces);
}
Types de Données de Base
fn main() {
// Integers
let signed: i32 = -42; // 32-bit signé
let unsigned: u32 = 42; // 32-bit non-signé
// Floats
let pi: f64 = 3.14159; // 64-bit (default)
let e: f32 = 2.718; // 32-bit
// Boolean
let is_rust_awesome: bool = true;
// Character (Unicode)
let heart: char = '💖';
// String slice
let greeting: &str = "Bonjour";
// String owned
let name: String = String::from("Rust");
println!("i32: {}, u32: {}, f64: {}, f32: {}", signed, unsigned, pi, e);
println!("bool: {}, char: {}, &str: {}, String: {}",
is_rust_awesome, heart, greeting, name);
}
Tuples et Arrays
fn main() {
// Tuple - types mixtes, taille fixe
let person: (String, i32, bool) = (String::from("Alice"), 25, true);
let (name, age, is_student) = person; // Destructuring
println!("{} a {} ans, étudiant: {}", name, age, is_student);
// Access par index
let coordinates = (3.0, 4.0);
println!("x: {}, y: {}", coordinates.0, coordinates.1);
// Array - même type, taille fixe
let numbers: [i32; 5] = [1, 2, 3, 4, 5];
let first = numbers[0];
let length = numbers.len();
println!("Premier élément: {}, longueur: {}", first, length);
// Array avec valeur répétée
let zeros = [0; 10]; // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
println!("Array de zéros: {:?}", zeros);
}
Ownership (Propriété)
L'ownership est la fonctionnalité la plus unique de Rust et permet la memory safety sans garbage collection. Les règles principales sont :
- Chaque valeur en Rust a une variable qui en est le propriétaire
- Il ne peut y avoir qu'un seul propriétaire à la fois
- Quand le propriétaire sort du scope, la valeur sera supprimée
Exemple de Move
fn main() {
let s1 = String::from("hello");
let s2 = s1; // ✅ s1 est "moved" vers s2, s1 n'est plus valide
// println!("{}", s1); // ❌ ERREUR : borrow of moved value
println!("{}", s2); // ✅ OK
}
Clone pour Copier
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone(); // ✅ Copie profonde explicite
println!("s1 = {}, s2 = {}", s1, s2); // ✅ Les deux sont valides
}
Copy Types
Certains types implémentent le trait Copy et sont copiés automatiquement :
fn main() {
let x = 5; // i32 implémente Copy
let y = x; // x est copié vers y
println!("x = {}, y = {}", x, y); // ✅ Les deux sont valides
// Types qui implémentent Copy :
// - Tous les types entiers (i32, u32, etc.)
// - Types booléens (bool)
// - Types flottants (f32, f64)
// - Character (char)
// - Tuples contenant uniquement des types Copy
}
Ownership et Functions
fn main() {
let s = String::from("hello");
takes_ownership(s); // s est moved dans la fonction
// println!("{}", s); // ❌ ERREUR : s n'est plus valide
let x = 5;
makes_copy(x); // x est copié dans la fonction
println!("{}", x); // ✅ OK : x est encore valide
}
fn takes_ownership(some_string: String) { // some_string entre dans le scope
println!("{}", some_string);
} // some_string sort du scope et est supprimé
fn makes_copy(some_integer: i32) { // some_integer entre dans le scope
println!("{}", some_integer);
} // some_integer sort du scope, mais rien de spécial ne se passe
Références et Emprunts
Pour utiliser une valeur sans prendre l'ownership, utilise les références :
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1); // Passer une référence
println!("La longueur de '{}' est {}.", s1, len); // s1 toujours valide
}
fn calculate_length(s: &String) -> usize { // s est une référence à String
s.len()
} // s sort du scope, mais ne possède pas la donnée, rien n'est supprimé
Références Mutables
fn main() {
let mut s = String::from("hello");
change(&mut s); // Passer une référence mutable
println!("{}", s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
Règles des références :
- Une seule référence mutable OU plusieurs références immutables
- Les références doivent toujours être valides
Structures de Contrôle
Conditions avec if
fn main() {
let number = 6;
if number % 4 == 0 {
println!("Le nombre est divisible par 4");
} else if number % 3 == 0 {
println!("Le nombre est divisible par 3");
} else if number % 2 == 0 {
println!("Le nombre est divisible par 2");
} else {
println!("Le nombre n'est pas divisible par 4, 3, ou 2");
}
// if est une expression
let condition = true;
let number = if condition { 5 } else { 6 };
println!("La valeur du nombre est : {}", number);
}
Boucles
fn main() {
// Boucle infinie
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; // Retourner une valeur avec break
}
};
println!("Le résultat est {}", result);
// Boucle while
let mut number = 3;
while number != 0 {
println!("{}!", number);
number -= 1;
}
println!("LIFTOFF!!!");
// Boucle for
let a = [10, 20, 30, 40, 50];
for element in a {
println!("La valeur est : {}", element);
}
// Range avec for
for number in (1..4).rev() {
println!("{}!", number);
}
println!("LIFTOFF!!!");
}
Functions
fn main() {
println!("Hello, world!");
another_function(5);
print_labeled_measurement(5, 'h');
let x = plus_one(5);
println!("La valeur de x est : {}", x);
}
fn another_function(x: i32) {
println!("La valeur de x est : {}", x);
}
fn print_labeled_measurement(value: i32, unit_label: char) {
println!("La mesure est : {}{}", value, unit_label);
}
// Function avec valeur de retour
fn plus_one(x: i32) -> i32 {
x + 1 // Expression sans point-virgule = valeur de retour
}
// Avec return explicite
fn minus_one(x: i32) -> i32 {
return x - 1; // return explicite avec point-virgule
}
Gestion d'Erreur de Base
Option
fn main() {
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;
// Pattern matching avec match
match some_number {
Some(value) => println!("La valeur est {}", value),
None => println!("Aucune valeur"),
}
// Avec if let
if let Some(value) = some_number {
println!("La valeur est {}", value);
}
}
Result
use std::fs::File;
use std::io::ErrorKind;
fn main() {
// Gestion d'erreur avec match
let file_result = File::open("hello.txt");
let _file = match file_result {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => {
println!("Fichier non trouvé !");
return;
}
other_error => {
println!("Problème à l'ouverture du fichier : {:?}", other_error);
return;
}
},
};
// Avec unwrap_or_else
let _file = File::open("hello.txt").unwrap_or_else(|error| {
if error.kind() == ErrorKind::NotFound {
println!("Création du fichier...");
File::create("hello.txt").unwrap_or_else(|error| {
panic!("Problème lors de la création du fichier : {:?}", error);
})
} else {
panic!("Problème à l'ouverture du fichier : {:?}", error);
}
});
}
Premier Projet Pratique : Jeu de Devinette
Créons un jeu simple pour mettre en pratique ce que nous avons appris :
use rand::Rng;
use std::cmp::Ordering;
use std::io;
fn main() {
println!("Devine le nombre !");
let secret_number = rand::thread_rng().gen_range(1..=100);
loop {
println!("Saisie quelque chose.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Échec de la lecture de la ligne");
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("Entre un nombre valide !");
continue;
}
};
println!("Tu as deviné : {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Trop petit !"),
Ordering::Greater => println!("Trop grand !"),
Ordering::Equal => {
println!("Tu as gagné !");
break;
}
}
}
}
Pour ce projet, ajoute dans ton Cargo.toml :
[dependencies]
rand = "0.8"
Outils Utiles
Formatage du Code
cargo fmt # Formate automatiquement ton code
Linting
cargo clippy # Analyse statique pour améliorer ton code
Tests
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = 2 + 2;
assert_eq!(result, 4);
}
#[test]
#[should_panic]
fn another() {
panic!("Make this test fail");
}
}
Exécute avec cargo test.
Prochaines Étapes
Maintenant que tu as les bases, Essaie de construire un petit projet pour pratiquer vos compétences. Voici quelques suggestions :
Projets Débutants
- Calculatrice en ligne de commande
- Convertisseur de température
- Gestionnaire de liste de tâches simple
- Générateur de mots de passe
Ressources d'Apprentissage
La documentation Rust est excellente pour approfondir vos connaissances :
- Le Livre Rust (The Rust Book) - Guide complet et officiel
- Rust by Example - Apprendre par l'exemple
- Rustlings - Petits exercices pratiques
- Rust Cookbook - Recettes de code
- Comprehensive Rust - Cours Google
Communauté
- Forum officiel Rust
- Reddit r/rust
- Discord Rust Community
- This Week in Rust - Newsletter hebdomadaire
Concepts Avancés à Explorer Ensuite
- Structs et Enums
- Pattern Matching avancé
- Traits et Generics
- Gestion des erreurs avancée
- Concurrence et parallélisme
- Macros
- Unsafe Rust
Bon codage avec Rust ! 🦀
Astuce : N'hésite pas à expérimenter avec le code. Rust a un excellent compilateur qui te guidera avec des messages d'erreur très informatifs. Chaque erreur est une opportunité d'apprentissage !