💻 Développement

dev-rust-guide

Guide Rust pour ownership, lifetimes et patterns systèmes.

⚡ Installation & lancement en 1 commande

Copiez-collez dans votre terminal : le skill s'installe dans ~/.claude/skills et Claude Code se lance directement dessus.

macOS / Linux
curl -fsSL https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.sh | sh -s -- dev-rust-guide --launch
Windows (PowerShell)
iex "& { $(iwr -useb https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.ps1) } dev-rust-guide -Launch"

🚀 Déjà installé ?

claude "/dev-rust-guide"

Ou tapez /dev-rust-guide dans une session Claude Code, ou décrivez simplement votre besoin — le skill se déclenche automatiquement via le skill-router.

🔑 Déclencheurs automatiques

Le skill s'active automatiquement quand votre demande contient :

Rustownershipborrow checkerlifetimecargotraitsasync Rustsystems programmingmemory safety

📦 Installation manuelle

git clone https://github.com/khalilbenaz/claude-skills-collection.git cp -r claude-skills-collection/skills/dev-rust-guide ~/.claude/skills/

Payload du plugin : skills/dev-rust-guide · source éditable : dev-skills/rust-guide

📖 Manuel

Rust Guide

Workflow

1. Ownership & Borrowing

Trois règles fondamentales :

let s1 = String::from("hello");
let s2 = s1;          // move : s1 invalide
let s3 = s2.clone();  // copie profonde explicite

fn inspect(s: &str) { /* lecture seule */ }
fn mutate(s: &mut String) { s.push('!'); } // ref exclusive

Règle d'emprunt : soit N références partagées &T, soit UNE seule &mut T, jamais les deux simultanément.

2. Lifetimes

Les lifetimes décrivent des relations entre références, elles ne créent pas de durée de vie.

// Nécessaire : le compilateur ne peut pas inférer quelle ref est retournée
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

// Struct avec référence : lifetime obligatoire
struct Excerpt<'a> {
    part: &'a str,
}

Critères :

3. Error Handling

ContexteCrate recommandéePourquoi
Bibliothèque (API publique)thiserrorErreurs typées, messages contrôlés
Application (binaire)anyhowContext riche, propagation facile
Proto / script rapideBox<dyn Error>Zéro configuration
// Bibliothèque
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AppError {
    #[error("fichier introuvable : {0}")]
    NotFound(String),
    #[error(transparent)]
    Io(#[from] std::io::Error),
}

// Application : propagation avec contexte
use anyhow::{Context, Result};
fn load(path: &str) -> Result<String> {
    std::fs::read_to_string(path)
        .with_context(|| format!("lecture de {path}"))
}

Règle : ne jamais unwrap() en production ; réserver .expect("raison") aux invariants impossibles à violer.

4. Traits & Generics

// Trait bound statique (monomorphisation, zéro overhead)
fn affiche<T: std::fmt::Display>(val: T) { println!("{val}"); }

// impl Trait en retour (opaque type)
fn make_adder(x: i32) -> impl Fn(i32) -> i32 { move |y| x + y }

// dyn Trait : dispatch dynamique (heap, vtable)
fn process(items: &[Box<dyn Draw>]) { items.iter().for_each(|i| i.draw()); }

Critères impl Trait vs dyn Trait :

5. Patterns idiomatiques

Newtype — type safety sans overhead :

struct Meters(f64);
struct Kilograms(f64);
// impossible de confondre Meters et Kilograms à la compile

Builder avec validation :

struct Config { host: String, port: u16 }
struct ConfigBuilder { host: String, port: Option<u16> }
impl ConfigBuilder {
    pub fn port(mut self, p: u16) -> Self { self.port = Some(p); self }
    pub fn build(self) -> Result<Config, String> {
        Ok(Config { host: self.host, port: self.port.ok_or("port requis")? })
    }
}

Typestate — états vérifiés à la compilation :

struct Connection<S> { _state: std::marker::PhantomData<S> }
struct Disconnected; struct Connected;
impl Connection<Disconnected> {
    fn connect(self) -> Connection<Connected> { /* ... */ todo!() }
}
impl Connection<Connected> {
    fn send(&self, data: &[u8]) { /* interdit si Disconnected */ }
}

6. Async Rust

# Cargo.toml
tokio = { version = "1", features = ["full"] }
#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Tâche concurrente
    let handle = tokio::spawn(async { fetch_data().await });

    // Race entre futures
    tokio::select! {
        res = handle => println!("{res:?}"),
        _ = tokio::time::sleep(std::time::Duration::from_secs(5)) => eprintln!("timeout"),
    }
    Ok(())
}

Points d'attention :

7. Unsafe & FFI

// SAFETY: le pointeur est valide et exclusif pour toute la durée de l'appel.
unsafe fn raw_write(ptr: *mut u8, val: u8) {
    *ptr = val;
}

// Binding C
extern "C" {
    fn strlen(s: *const std::ffi::c_char) -> usize;
}

Règles strictes :

8. Cargo & Écosystème 2026

Commandes essentielles :

cargo new mon_projet --lib   # nouveau projet bibliothèque
cargo add tokio --features full  # ajouter dépendance
cargo clippy -- -D warnings  # lints stricts (CI)
cargo fmt --check            # vérification style (CI)
cargo test                   # tests unitaires + intégration
cargo bench                  # benchmarks (criterion)
cargo doc --open             # documentation locale
cargo audit                  # audit sécurité dépendances

Crates incontournables :

DomaineCrate
Sérialisationserde + serde_json / serde_yaml
CLIclap v4 (derive)
Web asyncaxum (Tower ecosystem)
HTTP clientreqwest
Async runtimetokio
Parallélisme CPUrayon
Loggingtracing + tracing-subscriber
Testing fixturesrstest
Benchmarkscriterion

Garde-fous & Anti-patterns

Anti-patternImpactAlternative
clone() systématique pour contourner le borrow checkerPerf, allocation inutileRefactoriser les lifetimes ou passer des références
unwrap() / expect("") sans justificationPanic en prod?, if let, error handling explicite
Arc<Mutex<T>> sur toutContention, deadlocksPasser la donnée par canal (tokio::sync::mpsc) quand possible
unsafe sans commentaire SAFETYUndefined behavior silencieuxToujours justifier l'invariant
Ignorer clippyCode non-idiomatique, bugs latentsCI bloquante sur cargo clippy -- -D warnings
Spawner des threads OS pour I/O asyncBloque le runtime tokioUtiliser les primitives tokio::
Édition 2018 ou absenteManque les améliorations récentesedition = "2021" dans Cargo.toml

Bonnes pratiques 2026