0Pricing
Learn Rust Coding · Leçon

Builders fondés sur l’état du type

Encodez la validité dans le système de types.

Builders fondés sur l’état du type est une leçon Learn Rust Coding gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Learn Rust Coding, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Learn Rust Coding comprend 4 leçons au total.

Le problème des builders ordinaires

Un builder ordinaire vous permet d’appeler build() à tout moment, même avant de renseigner les champs obligatoires. Les données manquantes apparaissent alors sous la forme d’une panique à l’exécution ou d’un Err.

Les builders à état de type déplacent cette vérification au moment de la compilation : oublier une étape obligatoire empêche simplement la compilation.

Encoder l’état dans les types

L’astuce consiste à rendre le builder générique sur des types marqueurs qui représentent les étapes déjà effectuées. Lorsque vous définissez chaque champ, le type du builder change.

La méthode build() n’est disponible que lorsque chaque marqueur obligatoire atteint son état « défini ».

struct Missing;
struct Set;

Un Builder générique sur des marqueurs

Donnez au type builder un paramètre de type pour l’état de chaque champ obligatoire. PhantomData transporte le marqueur sans stocker de données réelles.

use std::marker::PhantomData;
struct ReqBuilder<H, U> {
    url: Option<String>,
    method: Option<String>,
    _state: PhantomData<(H, U)>,
}

L’état initial

Le constructeur renvoie un builder dans lequel chaque marqueur obligatoire vaut Missing. À ce stade, aucun build() n’existe : le système de types sait donc que l’objet est incomplet.

impl ReqBuilder<Missing, Missing> {
    fn new() -> Self {
        ReqBuilder { url: None, method: None, _state: PhantomData }
    }
}

Faire évoluer un marqueur

Un accesseur consomme l’ancien builder et en renvoie un nouveau dont le marqueur du champ concerné passe à Set. L’autre marqueur est conservé grâce au maintien de son paramètre de type générique.

impl<U> ReqBuilder<Missing, U> {
    fn url(self, url: &str) -> ReqBuilder<Set, U> {
        ReqBuilder { url: Some(url.to_string()),
            method: self.method, _state: PhantomData }
    }
}

La deuxième transition

La définition du champ fonctionne de la même manière : le deuxième marqueur passe de Missing à Set, tandis que le premier reste inchangé.

impl<H> ReqBuilder<H, Missing> {
    fn method(self, m: &str) -> ReqBuilder<H, Set> {
        ReqBuilder { url: self.url,
            method: Some(m.to_string()), _state: PhantomData }
    }
}

build() uniquement lorsque tout est défini

Point essentiel, build() n’est implémenté que pour ReqBuilder<Set, Set>. Tout autre état ne possède tout simplement pas cette méthode, et l’appel échoue donc à la compilation.

À l’intérieur, les appels à unwrap ne peuvent jamais paniquer, car le type prouve que les deux champs sont présents.

struct Request { url: String, method: String }
impl ReqBuilder<Set, Set> {
    fn build(self) -> Request {
        Request { url: self.url.unwrap(), method: self.method.unwrap() }
    }
}

Tout assembler

Une chaîne correcte se compile sans problème, car chaque appel fait progresser le builder vers ReqBuilder<Set, Set>, où build() existe.

fn demo() -> Request {
    ReqBuilder::new()
        .url("https://example.com")
        .method("GET")
        .build()
}

L’erreur de compilation souhaitée

Si vous omettez une étape obligatoire, le compilateur refuse le code. Appeler build() sur ReqBuilder<Set, Missing> signale « aucune méthode nommée build », ce qui détecte l’oubli avant même l’exécution du programme.

// ReqBuilder::new().url("x").build();
// error: no method named `build` found for
// ReqBuilder<Set, Missing>

Une version exécutable autonome

Ce programme minimal utilise un seul champ obligatoire pour montrer toute la transition de bout en bout. Il se compile et affiche la valeur construite.

use std::marker::PhantomData;
struct Missing; struct Set;
struct B<N> { name: Option<String>, _s: PhantomData<N> }
impl B<Missing> {
    fn new() -> Self { B { name: None, _s: PhantomData } }
    fn name(self, n: &str) -> B<Set> {
        B { name: Some(n.to_string()), _s: PhantomData }
    }
}
impl B<Set> {
    fn build(self) -> String { self.name.unwrap() }
}
fn main() {
    let v = B::new().name("prod").build();
    println!("built: {}", v);
}

Coûts et compromis

Les builders à état de type offrent des garanties à la compilation sans surcoût à l’exécution, puisque les marqueurs sont supprimés. En contrepartie, ils ajoutent davantage de mécanismes de typage et de blocs d’implémentation combinatoires à mesure que le nombre de champs obligatoires augmente.

Réservez ce modèle aux interfaces où une construction incomplète doit être impossible par conception.

Vérification rapide

Identifiez ce qui garantit réellement la complétude dans un builder à état de type.

Récapitulatif

Les générateurs à états de type encodent chaque étape requise comme un paramètre de type marqueur, transporté par PhantomData. Les accesseurs consomment le générateur et renvoient un nouveau type dont l’un des marqueurs est remplacé par Set.

Comme build() n’est implémenté que pour l’état où tous les marqueurs valent Set, l’oubli d’une étape devient une erreur de compilation, et non une panique à l’exécution, le tout sans aucun coût à l’exécution.

Questions Fréquemment Posées

La leçon « Builders fondés sur l’état du type » est-elle gratuite ?

Oui — le texte complet de « Builders fondés sur l’état du type » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Learn Rust Coding, passe à CoddyKit PRO. Le cours Learn Rust Coding comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Builders fondés sur l’état du type » ?

Encodez la validité dans le système de types. Tu pratiques Learn Rust Coding avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Learn Rust Coding ?

Aucune expérience préalable n'est requise. Learn Rust Coding sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.

Combien de temps prend la leçon « Builders fondés sur l’état du type » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Learn Rust Coding ?

Oui. Chaque leçon Learn Rust Coding inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Le patron Builder
  2. Le patron Newtype
  3. Builders fondés sur l’état du type
  4. Ergonomie de Deref et des enveloppes
← Retour à Learn Rust Coding