19 avril 2025
Rust suscite constamment l'intérêt des développeurs, remportant le titre de langage de programmation “le plus apprécié” dans les sondages Stack Overflow plusieurs années consécutives. Ce n'est pas seulement un engouement ; Rust offre un mélange convaincant de performance, de sécurité et de fonctionnalités de langage modernes qui résolvent les problèmes courants rencontrés dans d'autres langages de programmation système. Si vous êtes curieux de savoir ce qui rend Rust spécial et que vous souhaitez commencer votre parcours, ce guide pour débutants fournit les connaissances fondamentales pour démarrer. Nous explorerons la syntaxe de base, les concepts uniques comme la propriété, et l'outillage essentiel qui alimente l'écosystème Rust.
Rust se positionne comme un langage pour construire des logiciels fiables et efficaces. Ses principaux avantages tournent autour de la sécurité de la mémoire sans dépendre d'un ramasse-miettes et de la possibilité d'une concurrence sans crainte. Voici pourquoi vous devriez envisager d'apprendre Rust :
Avant d'écrire votre première ligne de code Rust, vous devez configurer la chaîne d'outils Rust. La manière standard et recommandée d'installer Rust est d'utiliser rustup
, l'installateur de la chaîne d'outils Rust.
rustup
: Cet outil en ligne de commande gère vos installations Rust. Il vous permet d'installer, de mettre à jour et de basculer facilement entre différentes versions de Rust (comme stable, bêta ou nightly). Visitez le site officiel de Rust (https://www.rust-lang.org/tools/install) pour les instructions d'installation spécifiques à votre système d'exploitation.rustc
: C'est le compilateur Rust. Après avoir écrit votre code source Rust dans des fichiers .rs
, rustc
les compile en binaires exécutables ou en bibliothèques que votre ordinateur peut comprendre. Bien que crucial, vous n'invoquerez généralement pas rustc
directement très souvent dans votre flux de travail quotidien.cargo
: C'est le système de build et le gestionnaire de paquets de Rust, et c'est l'outil avec lequel vous interagirez le plus fréquemment. Cargo orchestre de nombreuses tâches de développement courantes :
cargo new
).cargo build
).cargo run
).cargo test
).Cargo.toml
).Pour une expérimentation rapide sans nécessiter d'installation locale, les plateformes en ligne comme le Rust Playground officiel ou les environnements de développement intégrés comme Replit sont d'excellentes options.
Commençons par le programme traditionnel "Hello, world!", un rite de passage pour l'apprentissage de tout nouveau langage. Créez un fichier nommé main.rs
et ajoutez le code suivant :
fn main() {
// Cette ligne affiche du texte dans la console
println!("Hello, Rust!");
}
Pour compiler et exécuter ce programme simple en utilisant les outils de base :
main.rs
, et exécutez :
rustc main.rs
Cette commande invoque le compilateur Rust (rustc
), qui crée un fichier exécutable (par exemple, main
sous Linux/macOS, main.exe
sous Windows)../main
# Sous Windows, utilisez : .\main.exe
Cependant, pour tout ce qui dépasse un seul fichier, utiliser Cargo est l'approche standard et beaucoup plus pratique :
cargo new hello_rust
cd hello_rust
Cargo crée un nouveau répertoire nommé hello_rust
contenant un sous-répertoire src
avec main.rs
(déjà rempli avec le code "Hello, Rust!") et un fichier de configuration nommé Cargo.toml
.cargo run
Cargo s'occupera de l'étape de compilation puis exécutera le programme résultant, affichant “Hello, Rust!” sur votre console.Décortiquons l'extrait de code :
fn main()
: Ceci définit la fonction principale. Le mot-clé fn
signifie une déclaration de fonction. main
est un nom de fonction spécial ; c'est le point d'entrée où chaque programme Rust exécutable commence son exécution. Les parenthèses ()
indiquent que cette fonction ne prend aucun paramètre d'entrée.{}
: Les accolades définissent un bloc de code ou une portée. Tout le code appartenant à la fonction va à l'intérieur de ces accolades.println!("Hello, Rust!");
: Cette ligne effectue l'action d'afficher du texte dans la console.
println!
est une macro Rust. Les macros sont similaires aux fonctions mais ont une différence clé : elles se terminent par un point d'exclamation !
. Les macros effectuent une génération de code au moment de la compilation, offrant plus de puissance et de flexibilité que les fonctions régulières (comme la gestion d'un nombre variable d'arguments, ce que fait println!
). La macro println!
affiche le texte fourni dans la console et ajoute automatiquement un caractère de nouvelle ligne à la fin."Hello, Rust!"
est un littéral de chaîne – une séquence fixe de caractères représentant du texte, entourée de guillemets doubles.;
: Le point-virgule marque la fin de l'instruction. La plupart des lignes de code Rust exécutables (instructions) se terminent par un point-virgule.Maintenant, plongeons dans les éléments fondamentaux du langage de programmation Rust.
Les variables sont utilisées pour stocker des valeurs de données. En Rust, vous déclarez des variables en utilisant le mot-clé let
.
let pommes = 5;
let message = "Prends-en cinq";
Un concept fondamental en Rust est que les variables sont immuables par défaut. Cela signifie qu'une fois qu'une valeur est liée à un nom de variable, vous ne pouvez plus changer cette valeur plus tard.
let x = 10;
// x = 15; // Cette ligne provoquera une erreur de compilation ! Impossible d'assigner deux fois à la variable immuable `x`.
println!("La valeur de x est : {}", x); // {} est un espace réservé pour la valeur de x
Cette immutabilité par défaut est un choix de conception délibéré qui vous aide à écrire du code plus sûr et plus prévisible en empêchant la modification accidentelle des données, qui peut être une source courante de bogues. Si vous avez besoin d'une variable dont la valeur peut changer, vous devez explicitement la marquer comme mutable en utilisant le mot-clé mut
lors de la déclaration.
let mut compteur = 0; // Déclare 'compteur' comme mutable
println!("Compteur initial : {}", compteur);
compteur = 1; // Ceci est autorisé car 'compteur' a été déclaré avec 'mut'
println!("Nouveau compteur : {}", compteur);
Rust permet également le masquage (shadowing). Vous pouvez déclarer une nouvelle variable avec le même nom qu'une variable précédente dans la même portée. La nouvelle variable “masque” l'ancienne, ce qui signifie que les utilisations ultérieures du nom se réfèrent à la nouvelle variable. C'est différent de la mutation car nous créons une variable entièrement nouvelle, qui peut même avoir un type différent.
let espaces = " "; // 'espaces' est initialement une tranche de chaîne (&str)
let espaces = espaces.len(); // 'espaces' est maintenant masqué par une nouvelle variable contenant la longueur (un entier, usize)
println!("Nombre d'espaces : {}", espaces); // Affiche la valeur entière
Rust est un langage à typage statique. Cela signifie que le type de chaque variable doit être connu par le compilateur au moment de la compilation. Cependant, Rust a une excellente inférence de type. Dans de nombreuses situations, vous n'avez pas besoin d'écrire explicitement le type ; le compilateur peut souvent le déterminer en fonction de la valeur et de la façon dont vous l'utilisez.
let quantite = 10; // Le compilateur infère i32 (le type entier signé par défaut)
let prix = 9.99; // Le compilateur infère f64 (le type à virgule flottante par défaut)
let actif = true; // Le compilateur infère bool (booléen)
let initiale = 'R'; // Le compilateur infère char (caractère)
Si vous voulez ou devez être explicite (par exemple, pour la clarté ou lorsque le compilateur a besoin d'aide), vous pouvez fournir des annotations de type en utilisant un deux-points :
suivi du nom du type.
let score: i32 = 100; // Explicitement un entier signé de 32 bits
let ratio: f32 = 0.5; // Explicitement un flottant simple précision
let est_complet: bool = false; // Explicitement un booléen
let note: char = 'A'; // Explicitement un caractère (valeur scalaire Unicode)
Rust possède plusieurs types scalaires intégrés (représentant des valeurs uniques) :
i8
, i16
, i32
, i64
, i128
, isize
) stockent des nombres entiers positifs et négatifs. Les entiers non signés (u8
, u16
, u32
, u64
, u128
, usize
) ne stockent que des nombres entiers non négatifs. Les types isize
et usize
dépendent de l'architecture de l'ordinateur (32 bits ou 64 bits) et sont principalement utilisés pour indexer les collections.f32
(simple précision) et f64
(double précision). Le défaut est f64
.bool
a deux valeurs possibles : true
ou false
.char
représente une seule valeur scalaire Unicode (plus complète que les simples caractères ASCII), entourée d'apostrophes simples (par exemple, 'z'
, 'π'
, '🚀'
).String
vs &str
La gestion du texte en Rust implique souvent deux types principaux, ce qui peut être déroutant pour les nouveaux arrivants :
&str
(prononcé “string slice” ou “tranche de chaîne”) : Il s'agit d'une référence immuable à une séquence d'octets encodés en UTF-8 stockée quelque part en mémoire. Les littéraux de chaîne (comme "Hello"
) sont de type &'static str
, ce qui signifie qu'ils sont stockés directement dans le binaire du programme et vivent pendant toute la durée du programme. Les tranches fournissent une vue sur les données de chaîne sans les posséder. Elles ont une taille fixe.String
: Il s'agit d'un type de chaîne de caractères possédé, redimensionnable, mutable, encodé en UTF-8. Les données String
sont stockées sur le tas, ce qui leur permet d'être redimensionnées. Vous utilisez généralement String
lorsque vous devez modifier des données de chaîne, ou lorsque la chaîne doit posséder ses propres données et gérer sa propre durée de vie (souvent lors du retour de chaînes depuis des fonctions ou de leur stockage dans des structures).// Littéral de chaîne (stocké dans le binaire du programme, immuable)
let tranche_statique: &'static str = "Je suis immuable";
// Crée une String possédée, allouée sur le tas, à partir d'un littéral
let mut chaine_dynamique: String = String::from("Début");
// Modifie la String (possible car elle est mutable et possède ses données)
chaine_dynamique.push_str(" et croissance");
println!("{}", chaine_dynamique); // Sortie : Début et croissance
// Crée une tranche de chaîne qui référence une partie de la String
// Cette tranche emprunte des données à chaine_dynamique
let tranche_depuis_string: &str = &chaine_dynamique[0..5]; // Référence "Début"
println!("Tranche : {}", tranche_depuis_string);
Les fonctions sont fondamentales pour organiser le code en unités nommées et réutilisables. Nous avons déjà rencontré la fonction spéciale main
.
// Définition de fonction
fn saluer(nom: &str) { // Prend un paramètre : 'nom', qui est une tranche de chaîne (&str)
println!("Bonjour, {}!", nom);
}
// Fonction qui prend deux paramètres i32 et retourne un i32
fn additionner(a: i32, b: i32) -> i32 {
// En Rust, la dernière expression dans le corps d'une fonction est automatiquement retournée,
// tant qu'elle ne se termine pas par un point-virgule.
a + b
// Ceci est équivalent à écrire : return a + b;
}
fn main() {
saluer("Alice"); // Appelle la fonction 'saluer'
let somme = additionner(5, 3); // Appelle 'additionner', lie la valeur retournée à 'somme'
println!("5 + 3 = {}", somme); // Sortie : 5 + 3 = 8
}
Points clés sur les fonctions :
fn
pour déclarer des fonctions.->
. Si une fonction ne retourne pas de valeur, son type de retour est implicitement ()
(un tuple vide, souvent appelé “type unité”).;
) et se terminent éventuellement par une expression (quelque chose qui s'évalue en une valeur).return
peut être utilisé pour des retours explicites et anticipés depuis n'importe où dans la fonction.Rust fournit des structures de flux de contrôle standard pour déterminer l'ordre dans lequel le code s'exécute :
if
/else
/else if
: Utilisé pour l'exécution conditionnelle.let nombre = 6;
if nombre % 4 == 0 {
println!("le nombre est divisible par 4");
} else if nombre % 3 == 0 {
println!("le nombre est divisible par 3"); // Cette branche s'exécute
} else {
println!("le nombre n'est pas divisible par 4 ou 3");
}
// Fait important, 'if' est une expression en Rust, ce qui signifie qu'elle s'évalue en une valeur.
// Cela vous permet de l'utiliser directement dans les instructions 'let'.
let condition = true;
let valeur = if condition { 5 } else { 6 }; // valeur sera 5
println!("La valeur est : {}", valeur);
// Note : Les deux branches de l'expression 'if' doivent s'évaluer au même type.
loop
: Crée une boucle infinie. Vous utilisez typiquement break
pour sortir de la boucle, en retournant éventuellement une valeur.while
: Boucle tant qu'une condition spécifiée reste vraie.for
: Itère sur les éléments d'une collection ou d'une plage. C'est le type de boucle le plus couramment utilisé et souvent le plus sûr en Rust.// Exemple de loop
let mut compteur = 0;
let resultat = loop {
compteur += 1;
if compteur == 10 {
break compteur * 2; // Sort de la boucle et retourne 'compteur * 2'
}
};
println!("Résultat de la boucle : {}", resultat); // Sortie : Résultat de la boucle : 20
// Exemple de while
let mut num = 3;
while num != 0 {
println!("{}!", num);
num -= 1;
}
println!("DÉCOLLAGE !!!");
// Exemple de for (itération sur un tableau)
let a = [10, 20, 30, 40, 50];
for element in a.iter() { // .iter() crée un itérateur sur les éléments du tableau
println!("la valeur est : {}", element);
}
// Exemple de for (itération sur une plage)
// (1..4) crée une plage incluant 1, 2, 3 (excluant 4)
// .rev() inverse l'itérateur
for nombre in (1..4).rev() {
println!("{}!", nombre); // Affiche 3!, 2!, 1!
}
println!("ENCORE DÉCOLLAGE !!!");
Utilisez des commentaires pour ajouter des explications et des notes à votre code que le compilateur ignorera.
// Ceci est un commentaire sur une seule ligne. Il s'étend jusqu'à la fin de la ligne.
/*
* Ceci est un commentaire de bloc, sur plusieurs lignes.
* Il peut s'étendre sur plusieurs lignes et est utile
* pour des explications plus longues.
*/
let nombre_chanceux = 7; // Vous pouvez aussi placer des commentaires à la fin d'une ligne.
La propriété est la caractéristique la plus distinctive et centrale de Rust. C'est le mécanisme qui permet à Rust de garantir la sécurité de la mémoire au moment de la compilation sans avoir besoin d'un ramasse-miettes. Saisir la propriété est la clé pour comprendre Rust. Elle suit trois règles fondamentales :
{ // s n'est pas valide ici, il n'est pas encore déclaré
let s = String::from("hello"); // s est valide à partir de ce point ;
// s 'possède' les données de la String allouées sur le tas.
// Vous pouvez utiliser s ici
println!("{}", s);
} // La portée se termine ici. 's' n'est plus valide.
// Rust appelle automatiquement une fonction spéciale 'drop' pour la String que 's' possède,
// libérant sa mémoire sur le tas.
Lorsque vous assignez une valeur possédée (comme une String
, un Vec
, ou une structure contenant des types possédés) à une autre variable, ou que vous la passez à une fonction par valeur, la propriété est déplacée. La variable originale devient invalide.
let s1 = String::from("original");
let s2 = s1; // La propriété des données de la String est DÉPLACÉE de s1 à s2.
// s1 n'est plus considéré comme valide après ce point.
// println!("s1 est : {}", s1); // Erreur de compilation ! Valeur empruntée ici après déplacement.
// s1 ne possède plus les données.
println!("s2 est : {}", s2); // s2 est maintenant le propriétaire et est valide.
Ce comportement de déplacement empêche les erreurs de “double libération”, où deux variables pourraient accidentellement essayer de libérer le même emplacement mémoire lorsqu'elles sortent de la portée. Les types primitifs comme les entiers, les flottants, les booléens et les caractères implémentent le trait Copy
, ce qui signifie qu'ils sont simplement copiés au lieu d'être déplacés lors de l'assignation ou du passage aux fonctions.
Que faire si vous voulez laisser une fonction utiliser une valeur sans transférer la propriété ? Vous pouvez créer des références. Créer une référence s'appelle emprunter. Une référence vous permet d'accéder aux données possédées par une autre variable sans en prendre possession.
// Cette fonction prend une référence (&) vers une String.
// Elle emprunte la String mais ne prend pas possession.
fn calculer_longueur(s: &String) -> usize {
s.len()
} // Ici, s (la référence) sort de la portée. Mais comme elle ne possède pas
// les données de la String, les données ne sont PAS libérées lorsque la référence sort de la portée.
fn main() {
let s1 = String::from("hello");
// Nous passons une référence à s1 en utilisant le symbole '&'.
// s1 possède toujours les données de la String.
let len = calculer_longueur(&s1);
// s1 est toujours valide ici car la propriété n'a jamais été déplacée.
println!("La longueur de '{}' est {}.", s1, len);
}
Les références sont immuables par défaut, tout comme les variables. Si vous voulez modifier les données empruntées, vous avez besoin d'une référence mutable, indiquée par &mut
. Cependant, Rust applique des règles strictes concernant les références mutables pour prévenir les conflits de données :
Les Règles d'Emprunt :
&mut T
).&T
).Ces règles sont appliquées par le compilateur.
// Cette fonction prend une référence mutable vers une String
fn changer(une_chaine: &mut String) {
une_chaine.push_str(", world");
}
fn main() {
// 's' doit être déclaré mutable pour permettre l'emprunt mutable
let mut s = String::from("hello");
// Exemple d'application des règles d'emprunt (décommentez les lignes pour voir les erreurs) :
// let r1 = &s; // emprunt immuable - OK
// let r2 = &s; // autre emprunt immuable - OK (plusieurs emprunts immuables autorisés)
// let r3 = &mut s; // ERREUR ! Impossible d'emprunter `s` comme mutable pendant que des emprunts immuables sont actifs
// // Rust impose : soit plusieurs lecteurs (&T) OU un seul écrivain (&mut T), jamais les deux
// println!("{}, {}", r1, r2); // Utiliser r1/r2 les maintient actifs, déclenchant l'erreur
// // Sans ce println, les NLL (Non-Lexical Lifetimes) de Rust libéreraient r1/r2 plus tôt,
// // rendant &mut s valide ici
// Un emprunt mutable est autorisé ici car aucun autre emprunt n'est actif
changer(&mut s);
println!("{}", s); // Sortie : hello, world
}
Rust fournit des moyens de regrouper plusieurs valeurs en types plus complexes.
Les structures (abréviation de structures) vous permettent de définir des types de données personnalisés en regroupant des champs de données associés sous un seul nom.
// Définit une structure nommée User
struct User {
active: bool,
username: String, // Utilise le type String possédé
email: String,
sign_in_count: u64,
}
fn main() {
// Crée une instance de la structure User
// Les instances doivent fournir des valeurs pour tous les champs
let mut user1 = User {
email: String::from("quelquun@example.com"),
username: String::from("unutilisateur123"),
active: true,
sign_in_count: 1,
};
// Accède aux champs de la structure en utilisant la notation pointée
// L'instance doit être mutable pour changer les valeurs des champs
user1.email = String::from("autreemail@example.com");
println!("Email utilisateur : {}", user1.email);
// Utilisation d'une fonction d'aide pour créer une instance User
let user2 = build_user(String::from("user2@test.com"), String::from("user2"));
println!("Statut actif de l'utilisateur 2 : {}", user2.active);
// Syntaxe de mise à jour de structure : Crée une nouvelle instance en utilisant
// certains champs d'une instance existante pour les champs restants.
let user3 = User {
email: String::from("user3@domain.com"),
username: String::from("user3"),
..user2 // prend les valeurs de 'active' et 'sign_in_count' de user2
};
println!("Nombre de connexions de l'utilisateur 3 : {}", user3.sign_in_count);
}
// Fonction qui retourne une instance User
fn build_user(email: String, username: String) -> User {
User {
email, // Raccourci d'initialisation de champ : si le nom du paramètre correspond au nom du champ
username,
active: true,
sign_in_count: 1,
}
}
Rust supporte également les structures-tuples, qui sont des tuples nommés (par exemple, struct Color(i32, i32, i32);
), et les structures unitaires, qui n'ont pas de champs et sont utiles lorsque vous devez implémenter un trait sur un type mais n'avez pas besoin de stocker de données (par exemple, struct AlwaysEqual;
).
Les énumérations (Enums) vous permettent de définir un type en énumérant ses variantes possibles. Une valeur d'enum ne peut être que l'une de ses variantes possibles.
// Enum simple définissant les types d'adresses IP
enum IpAddrKind {
V4, // Variante 1
V6, // Variante 2
}
// Les variantes d'enum peuvent aussi contenir des données associées
enum IpAddr {
V4(u8, u8, u8, u8), // La variante V4 contient quatre valeurs u8
V6(String), // La variante V6 contient une String
}
// Une enum très courante et importante dans la bibliothèque standard de Rust : Option<T>
// Elle encode le concept d'une valeur qui peut être présente ou absente.
// enum Option<T> {
// Some(T), // Représente la présence d'une valeur de type T
// None, // Représente l'absence de valeur
// }
// Une autre enum cruciale de la bibliothèque standard : Result<T, E>
// Utilisée pour les opérations qui peuvent réussir (Ok) ou échouer (Err).
// enum Result<T, E> {
// Ok(T), // Représente le succès, contenant une valeur de type T
// Err(E), // Représente l'échec, contenant une valeur d'erreur de type E
// }
fn main() {
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
// Création d'instances de l'enum IpAddr avec des données associées
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
// Exemple avec Option<T>
let some_number: Option<i32> = Some(5);
let no_number: Option<i32> = None;
// L'opérateur de flux de contrôle 'match' est parfait pour travailler avec les enums.
// Il vous permet d'exécuter différents codes en fonction de la variante de l'enum.
match some_number {
Some(i) => println!("Obtenu un nombre : {}", i), // Si c'est Some, lie la valeur interne à i
None => println!("Rien obtenu."), // Si c'est None
}
// 'match' doit être exhaustif : vous devez gérer toutes les variantes possibles.
// Le caractère de soulignement '_' peut être utilisé comme motif joker pour attraper
// toutes les variantes non explicitement listées.
}
Option<T>
et Result<T, E>
sont au cœur de l'approche de Rust pour gérer de manière robuste les valeurs potentiellement manquantes et les erreurs.
Cargo est une partie indispensable de l'écosystème Rust, rationalisant le processus de construction, de test et de gestion des projets Rust. C'est l'une des fonctionnalités que les développeurs apprécient souvent.
Cargo.toml
: C'est le fichier manifeste de votre projet Rust. Il est écrit au format TOML (Tom's Obvious, Minimal Language). Il contient des métadonnées essentielles sur votre projet (comme son nom, sa version et ses auteurs) et, surtout, liste ses dépendances (d'autres crates externes dont votre projet dépend).
[package]
name = "mon_projet"
version = "0.1.0"
edition = "2021" # Spécifie l'édition Rust à utiliser (influence les fonctionnalités du langage)
# Les dépendances sont listées ci-dessous
[dependencies]
# Exemple : Ajouter la crate 'rand' pour la génération de nombres aléatoires
# rand = "0.8.5"
# Lorsque vous construisez, Cargo téléchargera et compilera 'rand' et ses dépendances.
cargo new <nom_projet>
: Crée une nouvelle structure de projet d'application binaire (exécutable).cargo new --lib <nom_bibliotheque>
: Crée une nouvelle structure de projet de bibliothèque (crate destinée à être utilisée par d'autres programmes).cargo build
: Compile votre projet et ses dépendances. Par défaut, il crée un build de débogage non optimisé. La sortie est placée dans le répertoire target/debug/
.cargo build --release
: Compile votre projet avec les optimisations activées, adapté à la distribution ou aux tests de performance. La sortie est placée dans le répertoire target/release/
.cargo run
: Compile (si nécessaire) et exécute votre projet binaire.cargo check
: Vérifie rapidement votre code pour les erreurs de compilation sans produire réellement l'exécutable final. C'est généralement beaucoup plus rapide que cargo build
et utile pendant le développement pour un retour rapide.cargo test
: Exécute tous les tests définis dans votre projet (généralement situés dans le répertoire src
ou dans un répertoire tests
séparé).Lorsque vous ajoutez une dépendance à votre fichier Cargo.toml
puis exécutez une commande comme cargo build
ou cargo run
, Cargo gère automatiquement le téléchargement de la crate requise (et de ses dépendances) depuis le dépôt central crates.io et compile tout ensemble.
Ce guide de démarrage a couvert les bases absolues pour vous lancer. Le langage Rust offre de nombreuses autres fonctionnalités puissantes à explorer au fur et à mesure de votre progression :
Result
, la propagation des erreurs à l'aide de l'opérateur ?
, et la définition de types d'erreurs personnalisés.Vec<T>
(tableaux/vecteurs dynamiques), HashMap<K, V>
(tables de hachage), HashSet<T>
, etc.<T>
).'a
).Mutex
et Arc
, et la puissante syntaxe async
/await
de Rust pour la programmation asynchrone.Rust présente une proposition unique et convaincante : la performance brute attendue des langages de bas niveau comme C++, combinée à de fortes garanties de sécurité au moment de la compilation qui éliminent des classes entières de bogues courants, en particulier autour de la gestion de la mémoire et de la concurrence. Bien que la courbe d'apprentissage implique d'internaliser de nouveaux concepts comme la propriété et l'emprunt (et d'écouter le compilateur parfois strict), le gain est un logiciel hautement fiable, efficace et souvent plus facile à maintenir à long terme. Avec son excellent outillage via Cargo et une communauté solidaire, Rust est un langage gratifiant à apprendre.
Commencez petit, expérimentez en utilisant Cargo, acceptez les messages d'erreur utiles (bien que parfois verbeux) du compilateur comme des guides, et vous serez bien parti pour maîtriser ce langage puissant et de plus en plus populaire. Bon codage en Rust !