Rust pour Pascalien

Bienvenue, ami(e) Pascalien(ne) 👋

Ce site est pensé pour les développeurs Object Pascal / Delphi qui veulent apprendre Rust. Chaque concept est illustré par un parallèle de syntaxe Delphi ↔ Rust, et les spécificités de Rust (ownership, borrowing, traits…) sont expliquées en termes Pascalien.

🛡️ Sécurité mémoire

Pas de garbage collector, pas de nil sauvage : le compilateur garantit l'absence de fuites, de double-free, et de courses à la donnée.

⚡ Performances natives

Binaires natifs (comme Delphi), zero-cost abstractions, performances comparables à C++.

🧰 Outillage moderne

cargo = build + paquet + test + doc. Comparé à l'écosystème Delphi, l'outillage est unifié et standardisé.

🔀 Concurrence sûre

"Fearless concurrency" : le compilateur empêche les data races à la compilation. Plus de TThread fragile.

Pourquoi un développeur Delphi devrait s'intéresser à Rust ?

  • Mêmes valeurs : compilation native, typage fort, structures (recordstruct), begin/end{ }.
  • Ce que Delphi n'offre pas : un système de propriété qui élimine 70 % des bugs runtime (Access Violation, fuites, race conditions).
  • Pas de RTL propriétaire : la bibliothèque standard est open-source, multiplateforme, et l'écosystème (crates.io) compte plus de 150 000 paquets.
  • Embarqué & WebAssembly : Rust cible aussi bien le microcontrôleur que le navigateur, là où Delphi est essentiellement Windows/desktop.

Comparatif Rust face aux autres langages

Petit tour d'horizon pour situer Rust dans le paysage que vous connaissez :

Critère Rust Object Pascal (Delphi) C++ C# Java Python VBA
CompilationNativeNativeNativeJIT (IL)JIT (bytecode)InterprétéInterprété
Gestion mémoireOwnership (compile-time)Manuelle (Free) + ARC interfacesManuelle / RAIIGCGCGCGC
Sécurité mémoireGarantie statiqueFaibleFaibleBonne (GC)Bonne (GC)BonneBonne
Null / nilAucun (Option<T>)nilnullptrnull (+ nullable)nullNoneNothing
ExceptionsNon (Result)OuiOuiOuiOuiOuiOui (On Error)
Concurrence sûreStatiqueManuelle (TThread)ManuelleTasksThreadsGILNon
Héritage de classesNon (traits + composition)Oui (simple)Oui (multiple)Oui (simple)Oui (simple)Oui (multiple)Limité
Vitesse exécution⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
MultiplateformeExcellentBon (FMX)ExcellentBon (.NET)ExcellentExcellentOffice only
Outillage standardcargoIDE DelphiCMake/vcpkg/...dotnet CLIMaven/Gradlepip/poetryAucun
À retenir : Rust est le seul à offrir performances natives ET sécurité mémoire sans garbage collector. C'est le "Delphi du futur" si l'on prend l'angle "binaire natif rapide", avec en bonus les garanties statiques d'un langage moderne.

Installation & premier projet

Sous Windows (équivalent Pascalien : l'installeur Delphi) :

  1. Téléchargez l'installateur officiel sur rustup.rs
  2. L'outil rustup installe rustc (compilateur) et cargo (build/paquet).
  3. Vérifiez :
    rustc --version
    cargo --version
  4. Créez votre premier projet :
    cargo new bonjour
    cd bonjour
    cargo run

Cargo génère src/main.rs. C'est l'équivalent du fichier .dpr de Delphi (le point d'entrée), mais ici tout est unifié : le manifest Cargo.toml joue le rôle du fichier projet (.dproj).

Object Pascal (Delphi)
program Bonjour;
{$APPTYPE CONSOLE}
begin
  Writeln('Bonjour, monde !');
end.
Rust
fn main() {
    println!("Bonjour, monde !");
}

println! avec un point d'exclamation est une macro (résolue à la compilation), pas une fonction : en Rust, c'est une convention pour repérer immédiatement qu'on appelle du code généré.

Variables & types primitifs

En Delphi, on déclare ses variables dans un bloc var. En Rust, on utilise let, et — surprise pour un Pascalien — les variables sont immuables par défaut. Il faut let mut pour pouvoir modifier une valeur.

Object Pascal
var
  age: Integer;
  nom: string;
  pi: Double;
  actif: Boolean;
begin
  age := 30;
  nom := 'Bertrand';
  pi := 3.14159;
  actif := True;
  age := age + 1;  // modifiable
end;
Rust
fn main() {
    let age: i32 = 30;            // immuable
    let nom: &str = "Bertrand";
    let pi: f64 = 3.14159;
    let actif: bool = true;

    let mut compteur = 0;         // mutable
    compteur += 1;

    // age = age + 1;  // ❌ erreur de compilation !
}

Tableau de correspondance des types

DelphiRustNote
Byteu80..255
ShortInti8-128..127
Wordu16
SmallInti16
Cardinalu32
Integeri32défaut entier en Rust
Int64i64
NativeIntisizetaille du pointeur
NativeUIntusizeindices de tableau
Singlef32
Doublef64défaut flottant en Rust
Booleanbooltrue / false
Charchar4 octets en Rust (Unicode complet)
stringString / &strdeux types, voir Ownership
array[0..9] of T[T; 10]taille fixe
TArray<T>Vec<T>taille dynamique

Constantes et inférence

Object Pascal
const
  MAX_AGE = 150;
  GREETING: string = 'Salut';
Rust
const MAX_AGE: u32 = 150;
const GREETING: &str = "Salut";

let x = 42;          // type inféré : i32
let y = 3.14;        // f64
let z = "hello";     // &str
Spécificité Rust : le shadowing. Vous pouvez redéclarer une variable avec let et même changer son type. C'est sans équivalent direct en Pascal.
let valeur = "42";              // &str
let valeur: i32 = valeur.parse().unwrap();  // i32 maintenant

Contrôle de flux

If / then / else

Object Pascal
if age >= 18 then
  Writeln('Majeur')
else
  Writeln('Mineur');
Rust
if age >= 18 {
    println!("Majeur");
} else {
    println!("Mineur");
}

// if est une EXPRESSION : il retourne une valeur
let statut = if age >= 18 { "Majeur" } else { "Mineur" };

Boucles

Object Pascal
// For
for i := 0 to 9 do
  Writeln(i);

// While
while x < 100 do
  Inc(x);

// Repeat
repeat
  x := x - 1;
until x = 0;

// For-in (TArray, TList…)
for item in liste do
  Writeln(item);
Rust
// For sur une plage (0 inclus, 10 exclu)
for i in 0..10 {
    println!("{i}");
}

// While
while x < 100 {
    x += 1;
}

// loop = boucle infinie (équivalent du while true)
let resultat = loop {
    x -= 1;
    if x == 0 { break 42; }   // loop peut renvoyer une valeur !
};

// For-in
for item in &liste {
    println!("{item}");
}

Case / Match

Le match de Rust est bien plus puissant que le case Delphi : il fait du pattern matching, vérifie l'exhaustivité à la compilation, et peut destructurer des types complexes.

Object Pascal
case jour of
  1: Writeln('Lundi');
  2: Writeln('Mardi');
  3..5: Writeln('Milieu de semaine');
else
  Writeln('Week-end');
end;
Rust
match jour {
    1 => println!("Lundi"),
    2 => println!("Mardi"),
    3..=5 => println!("Milieu de semaine"),
    _ => println!("Week-end"),   // _ = wildcard (obligatoire si non-exhaustif)
}

// match est une EXPRESSION
let nom = match jour {
    1 => "Lundi",
    2 => "Mardi",
    _ => "Autre",
};

Fonctions & procédures

En Pascal, vous distinguez function et procedure. En Rust, il n'y a que fn : une "procédure" est simplement une fonction qui retourne le type vide () (appelé "unit").

Object Pascal
function Additionner(a, b: Integer): Integer;
begin
  Result := a + b;
end;

procedure Saluer(const nom: string);
begin
  Writeln('Bonjour ', nom);
end;
Rust
fn additionner(a: i32, b: i32) -> i32 {
    a + b               // pas de ; → c'est l'expression retournée
}

fn saluer(nom: &str) {
    println!("Bonjour {nom}");
}

// On peut aussi écrire :
fn additionner_v2(a: i32, b: i32) -> i32 {
    return a + b;       // explicite, mais moins idiomatique
}
Spécificité Rust : une expression sans ; en fin de bloc est la valeur de retour. C'est l'inverse de Pascal où le ; sépare les instructions. Cela rend le code très concis.

Paramètres : valeur, référence, mutabilité

Object Pascal
// Passage par valeur
procedure ParValeur(x: Integer);

// const = lecture seule, possible référence
procedure ParConst(const s: string);

// var = par référence (modifiable)
procedure ParVar(var x: Integer);

// out = sortie pure
procedure ParOut(out x: Integer);
Rust
// Passage par valeur (déplace ou copie)
fn par_valeur(x: i32) { /* ... */ }

// Référence immuable (≈ const en Delphi)
fn par_ref(s: &String) { /* lecture seule */ }

// Référence mutable (≈ var)
fn par_ref_mut(x: &mut i32) {
    *x += 1;            // déréférence avec *
}

// Appel :
let mut n = 10;
par_ref_mut(&mut n);

Structures & énumérations

Le record Pascal correspond à la struct Rust. Mais Rust offre aussi des enums algébriques qui n'ont pas d'équivalent en Delphi : chaque variant peut porter ses propres données.

Structs

Object Pascal
type
  TPersonne = record
    Nom: string;
    Age: Integer;
  end;

var
  p: TPersonne;
begin
  p.Nom := 'Ada';
  p.Age := 36;
  Writeln(p.Nom);
end;
Rust
struct Personne {
    nom: String,
    age: u32,
}

fn main() {
    let p = Personne {
        nom: String::from("Ada"),
        age: 36,
    };
    println!("{}", p.nom);
}

Méthodes (impl)

En Pascal, on ajoute des méthodes dans le type. En Rust, on les regroupe dans un bloc impl séparé : c'est plus net.

Object Pascal
type
  TRectangle = record
    L, H: Double;
    function Aire: Double;
  end;

function TRectangle.Aire: Double;
begin
  Result := L * H;
end;
Rust
struct Rectangle {
    l: f64,
    h: f64,
}

impl Rectangle {
    // "Constructeur" associé (sans self)
    fn nouveau(l: f64, h: f64) -> Self {
        Self { l, h }
    }

    // Méthode (avec &self)
    fn aire(&self) -> f64 {
        self.l * self.h
    }
}

let r = Rectangle::nouveau(3.0, 4.0);
println!("{}", r.aire());

Énumérations algébriques

L'enum Delphi est juste un ensemble de valeurs nommées. L'enum Rust peut porter des données par variant, ce qui est l'équivalent d'une union discriminée ou d'un sealed class en C#/Java.

Object Pascal
type
  TForme = (fCercle, fCarre, fTriangle);

// Pour porter des données, on combine
// avec un record variant — assez verbeux :
type
  TFormeData = record
    case Kind: TForme of
      fCercle:   (Rayon: Double);
      fCarre:    (Cote: Double);
      fTriangle: (B, H: Double);
  end;
Rust
enum Forme {
    Cercle(f64),               // rayon
    Carre(f64),                // côté
    Triangle { base: f64, hauteur: f64 },
}

fn aire(f: &Forme) -> f64 {
    match f {                  // exhaustivité vérifiée !
        Forme::Cercle(r) => std::f64::consts::PI * r * r,
        Forme::Carre(c) => c * c,
        Forme::Triangle { base, hauteur } => base * hauteur / 2.0,
    }
}
Pourquoi c'est puissant ? Combiné au match, l'enum Rust rend impossibles les états invalides. Si vous ajoutez un nouveau variant Forme::Hexagone, le compilateur refuse de compiler tant que vous n'avez pas géré ce cas partout. Bye-bye les bugs "j'ai oublié un cas du case".

Le type Option<T> — la fin du nil pointer exception

C'est l'une des grandes réussites de Rust : il n'y a tout simplement pas de nil/null. On utilise l'enum standard Option<T>.

enum Option<T> {
    Some(T),
    None,
}

fn chercher(id: u32) -> Option<Personne> {
    if id == 1 {
        Some(Personne { nom: String::from("Ada"), age: 36 })
    } else {
        None
    }
}

match chercher(1) {
    Some(p) => println!("Trouvé : {}", p.nom),
    None => println!("Pas trouvé"),
}

En Delphi, il faut systématiquement tester if p <> nil then … — et on oublie parfois, d'où les fameux Access Violation. En Rust, impossible d'utiliser le contenu sans avoir explicitement géré le cas None.

Modules & visibilité

L'unit Pascal avec ses sections interface / implementation correspond au mod Rust avec le mot-clé pub pour rendre publique une entité.

Object Pascal (unit math.pas)
unit Math;

interface
function Carre(x: Integer): Integer;
function Cube(x: Integer): Integer;

implementation

function Carre(x: Integer): Integer;
begin Result := x * x; end;

function Cube(x: Integer): Integer;
begin Result := x * x * x; end;

end.

// usage :
uses Math;
Writeln(Carre(5));
Rust (src/math.rs)
// Tout ce qui est marqué `pub` est exporté
pub fn carre(x: i32) -> i32 {
    x * x
}

pub fn cube(x: i32) -> i32 {
    x * x * x
}

fn helper_prive(x: i32) -> i32 {
    x + 1
}

// dans src/main.rs :
mod math;            // déclare le module
use math::carre;     // équivaut au `uses` de Pascal

fn main() {
    println!("{}", carre(5));
}

POO en Rust : Traits vs Classes Delphi

Choc culturel pour un Delphiste : Rust n'a pas de classes et pas d'héritage. À la place :

  • struct pour les données (≈ record),
  • impl pour les méthodes attachées,
  • trait pour les comportements partagés (≈ interface Delphi),
  • Composition au lieu d'héritage.

Traits ≈ Interfaces

Object Pascal
type
  IAnimal = interface
    function Crier: string;
  end;

  TChien = class(TInterfacedObject, IAnimal)
    function Crier: string;
  end;

  TChat = class(TInterfacedObject, IAnimal)
    function Crier: string;
  end;

function TChien.Crier: string;
begin Result := 'Wouf !'; end;

function TChat.Crier: string;
begin Result := 'Miaou'; end;
Rust
trait Animal {
    fn crier(&self) -> String;

    // méthode par défaut (mixin)
    fn presenter(&self) {
        println!("Je dis : {}", self.crier());
    }
}

struct Chien;
struct Chat;

impl Animal for Chien {
    fn crier(&self) -> String {
        String::from("Wouf !")
    }
}

impl Animal for Chat {
    fn crier(&self) -> String {
        String::from("Miaou")
    }
}

fn main() {
    let c = Chien;
    c.presenter();   // utilise la méthode par défaut
}

Polymorphisme : dynamique ou statique

En Delphi, le polymorphisme passe par les méthodes virtuelles et les interfaces. En Rust, on a deux options :

  • Statique (générique) : résolu à la compilation, zéro coût runtime.
  • Dynamique (dyn Trait) : équivalent vtable Delphi.
// Statique : monomorphisé à la compilation
fn faire_crier<A: Animal>(a: &A) {
    println!("{}", a.crier());
}

// Dynamique : vtable, comme une interface Delphi
fn faire_crier_dyn(a: &dyn Animal) {
    println!("{}", a.crier());
}

let animaux: Vec<Box<dyn Animal>> = vec![
    Box::new(Chien),
    Box::new(Chat),
];
for a in &animaux {
    a.presenter();
}
"Et l'héritage ?" Vous n'en aurez pas besoin autant que vous le croyez. La règle "composition over inheritance" prônée depuis 20 ans est ici appliquée par le langage. Un trait peut hériter d'un autre trait (trait B: A { … }), ce qui couvre la plupart des cas légitimes d'héritage.

⭐ Ownership : LA spécificité Rust

Si vous ne deviez retenir qu'un seul concept de Rust, ce serait celui-ci. L'ownership (propriété) remplace à la fois le garbage collector des langages managés ET le couple Create/Free manuel de Delphi.

Les 3 règles d'or

  1. Chaque valeur a un unique propriétaire (une variable).
  2. Quand le propriétaire sort du scope, la valeur est libérée automatiquement.
  3. Affecter une valeur à une autre variable la déplace (move) — l'ancien propriétaire n'y a plus accès.

Le problème en Delphi

var
  a, b: TStringList;
begin
  a := TStringList.Create;
  a.Add('coucou');

  b := a;            // a et b pointent vers le MÊME objet
  b.Free;            // OK

  a.Add('encore');   // ❌ ACCESS VIOLATION : a est dangling
end;

Pas d'erreur à la compilation. L'erreur n'apparaît qu'à l'exécution, peut-être sur le poste du client.

La solution en Rust

fn main() {
    let a = String::from("coucou");
    let b = a;              // ↘ MOVE : a n'est plus utilisable

    // println!("{}", a);   // ❌ erreur de COMPILATION
    println!("{}", b);      // ✅
}                           // b sort du scope → mémoire libérée automatiquement

L'erreur est attrapée au moment du build. Pas de dangling pointer possible.

Copy vs Move

Les types simples (entiers, flottants, bool, char) implémentent le trait Copy : l'affectation les duplique, comme en Pascal.

let x = 5;
let y = x;        // copie (Copy)
println!("{} {}", x, y);   // ✅ les deux marchent

let s = String::from("hello");
let t = s;        // move (pas de Copy)
// println!("{}", s);      // ❌
println!("{}", t);

Et si je veux vraiment dupliquer ?

let s = String::from("hello");
let t = s.clone();        // copie profonde explicite
println!("{} et {}", s, t);   // ✅

En Delphi, b := a sur un objet recopie le pointeur silencieusement. En Rust, soit c'est Copy (bon marché), soit c'est Move (transfert), soit on appelle explicitement .clone(). C'est verbeux mais sans surprise.

⭐ Borrowing & Lifetimes

"Tout déplacer / tout cloner" serait vite pénible. Rust propose un système d'emprunts (références) avec des règles strictes vérifiées par le compilateur.

Les règles d'emprunt

  • Vous pouvez avoir plusieurs références immuables (&T) en même temps, OU
  • Une seule référence mutable (&mut T), JAMAIS les deux à la fois.
  • Une référence ne peut pas survivre à la donnée qu'elle référence (pas de dangling pointer).
fn longueur(s: &String) -> usize {   // emprunt immuable
    s.len()
}                                       // s sort du scope mais ne libère rien

fn ajouter(s: &mut String) {           // emprunt mutable
    s.push_str(" !");
}

fn main() {
    let mut texte = String::from("Bonjour");
    let len = longueur(&texte);          // emprunt en lecture
    ajouter(&mut texte);                 // emprunt en écriture
    println!("{} ({} caractères)", texte, len);
}

Analogie Delphi

C'est l'équivalent de :

  • &Tconst T (lecture seule, multiple readers)
  • &mut Tvar T (écriture, unique writer)

… sauf que Delphi vous fait juste confiance, alors que Rust vérifie.

Lifetimes (durées de vie)

Dans 90 % des cas, le compilateur déduit les lifetimes tout seul (élision). Mais parfois vous devez les annoter avec 'a :

// "La référence retournée vit aussi longtemps que la plus courte des deux entrées"
fn plus_long<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

Cela paraît étrange au début, mais c'est ce qui garantit qu'il n'y a jamais de référence vers une variable détruite.

Le borrow checker au quotidien : au début, vous allez "vous battre" contre lui. Au bout de quelques semaines, vous remarquerez que les erreurs qu'il vous signale sont presque toujours de vrais bugs que vous auriez fini par produire en Delphi.

Gestion d'erreurs : Result au lieu d'exceptions

Delphi utilise les exceptions (try / except / finally). Rust choisit une approche différente : les erreurs récupérables sont des valeurs de retour, via l'enum Result<T, E>. Les vraies situations exceptionnelles (bug logiciel) utilisent panic!, qui termine le programme.

Object Pascal
function Diviser(a, b: Double): Double;
begin
  if b = 0 then
    raise EDivByZero.Create('Division par zéro');
  Result := a / b;
end;

try
  x := Diviser(10, 0);
except
  on E: EDivByZero do
    Writeln('Erreur : ', E.Message);
end;
Rust
fn diviser(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err(String::from("Division par zéro"))
    } else {
        Ok(a / b)
    }
}

fn main() {
    match diviser(10.0, 0.0) {
        Ok(v)  => println!("Résultat : {v}"),
        Err(e) => println!("Erreur : {e}"),
    }
}

L'opérateur ? — propager les erreurs

Pour ne pas écrire un match à chaque appel, Rust propose l'opérateur ? qui retourne automatiquement l'erreur au caller. C'est l'équivalent d'un except "remonter telle quelle".

fn lire_fichier_puis_parser(chemin: &str) -> Result<i32, std::io::Error> {
    let contenu = std::fs::read_to_string(chemin)?;   // si erreur → return Err
    let n: i32 = contenu.trim().parse().unwrap();
    Ok(n)
}
Avantage : en Rust, vous voyez immédiatement dans la signature d'une fonction si elle peut échouer (Result) ou pas. En Delphi/Java/C#, n'importe quel appel peut lever n'importe quelle exception ; on ne sait jamais quoi attraper.

Génériques & traits avancés

Les génériques Rust sont proches de ceux de Delphi (TList<T>), mais avec un système de contraintes par trait très expressif.

Object Pascal
type
  TPaire<T> = record
    A, B: T;
  end;

function Max<T>(x, y: T): T;
begin
  // contrainte difficile : pas de comparaison
  // générique standard en Pascal
end;
Rust
struct Paire<T> {
    a: T,
    b: T,
}

// Contrainte : T doit implémenter PartialOrd (comparable)
fn maximum<T: PartialOrd>(x: T, y: T) -> T {
    if x > y { x } else { y }
}

// Syntaxe `where` (équivalente, plus lisible avec plusieurs bornes)
fn afficher<T>(v: T) where T: std::fmt::Display + Clone {
    let v2 = v.clone();
    println!("{v} et {v2}");
}

Le compilateur génère du code spécialisé pour chaque type utilisé (monomorphisation) — comme les génériques C++, donc zéro coût runtime.

Closures & Itérateurs

Rust offre une approche fonctionnelle très riche pour manipuler les collections, équivalente aux LINQ de C# ou aux comprehensions Python — en plus performant car compilé.

Object Pascal
var
  nombres: TArray<Integer>;
  somme: Integer;
  i: Integer;
begin
  nombres := [1, 2, 3, 4, 5];
  somme := 0;
  for i in nombres do
    if i mod 2 = 0 then
      somme := somme + i * i;
  // somme = 2² + 4² = 20
end;
Rust
fn main() {
    let nombres = vec![1, 2, 3, 4, 5];

    let somme: i32 = nombres.iter()
        .filter(|&&n| n % 2 == 0)   // pairs
        .map(|&n| n * n)             // au carré
        .sum();                       // somme

    println!("{somme}");   // 20
}

Closures (lambdas)

Object Pascal (anonymous methods)
var
  carre: TFunc<Integer, Integer>;
begin
  carre := function(x: Integer): Integer
           begin Result := x * x; end;
  Writeln(carre(5));
end;
Rust
let carre = |x: i32| x * x;
println!("{}", carre(5));

// closure qui capture une variable de l'environnement
let facteur = 3;
let multiplier = |x| x * facteur;
println!("{}", multiplier(10));   // 30

Concurrence "sans peur"

Le slogan officiel est "fearless concurrency". Le système d'ownership s'étend aux threads : il devient impossible à la compilation d'introduire un data race.

Object Pascal (TThread)
var
  t: TThread;
begin
  t := TThread.CreateAnonymousThread(
    procedure
    begin
      Writeln('Dans le thread !');
    end);
  t.Start;
end;
Rust
use std::thread;

fn main() {
    let handle = thread::spawn(|| {
        println!("Dans le thread !");
    });
    handle.join().unwrap();
}

Canaux (channels)

Plutôt que de partager de la mémoire avec des verrous, Rust encourage le "share memory by communicating" (à la Go).

use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        for i in 1..=3 {
            tx.send(i).unwrap();
        }
    });

    for valeur in rx {
        println!("Reçu : {valeur}");
    }
}

Asynchrone : async / await

async fn telecharger(url: &str) -> String {
    // ... requête HTTP non bloquante
    format!("contenu de {url}")
}

#[tokio::main]
async fn main() {
    let page = telecharger("https://example.com").await;
    println!("{page}");
}

Cargo & écosystème

Cargo est le bonheur d'un Delphiste qui découvre l'outillage moderne : une commande, tout marche.

ActionDelphiRust
Nouveau projetIDE → File → New → Consolecargo new monapp
Compiler (debug)F9cargo build
Compiler (release)changer le mode dans l'IDEcargo build --release
ExécuterF9cargo run
TestsDUnit / DUnitX (à installer)cargo test (intégré)
Ajouter une dépendanceGetIt / manuelcargo add serde
Mettre à jourmanuelcargo update
Documentationcargo doc --open
Format du codecargo fmt
Lintercargo clippy

Cargo.toml — le manifest du projet

[package]
name = "monapp"
version = "0.1.0"
edition = "2021"

[dependencies]
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
reqwest = "0.12"

L'équivalent du fichier .dproj + GetIt, mais en texte versionnable. Le site crates.io recense plus de 150 000 paquets.

Tests intégrés

Pas besoin d'installer DUnitX : les tests sont dans le langage.

fn double(x: i32) -> i32 {
    x * 2
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn double_de_zero() {
        assert_eq!(double(0), 0);
    }

    #[test]
    fn double_de_cinq() {
        assert_eq!(double(5), 10);
    }
}

Puis : cargo test. Rust compile et exécute uniquement les tests, en parallèle.

Récapitulatif : 10 idées clés à emporter

  1. Immuable par défaut — utilisez let mut pour la mutation.
  2. Pas de null — utilisez Option<T>.
  3. Pas d'exceptions — utilisez Result<T, E> et l'opérateur ?.
  4. Ownership remplace GC et Free manuel.
  5. Borrowing : références multiples en lecture XOR une seule en écriture.
  6. Pas de classes ni d'héritagestruct + trait + composition.
  7. match est exhaustif : les bugs "oubli d'un cas" disparaissent.
  8. Enums algébriques = des données par variant + match = états invalides impossibles.
  9. cargo unifie build, test, doc, dépendances, formatage.
  10. Concurrence sûre à la compilation — fini les data races sournoises.

Pour aller plus loin

Bonne route dans le monde de Rust 🦀 ! N'hésitez pas à revenir sur cette page comme aide-mémoire : les sections sont indépendantes et conçues pour servir de référence rapide.