0Pricing
Learn Rust Coding · Lektion

Builder mit Type-State

Kodieren Sie Gültigkeit im Typsystem.

Builder mit Type-State ist eine kostenlose Learn Rust Coding-Lektion auf CoddyKit. Dies ist Lektion 3 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Learn Rust Coding-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Learn Rust Coding-Kurs umfasst insgesamt 4 Lektionen.

Das Problem einfacher Builder

Bei einem gewöhnlichen Builder können Sie jederzeit build() aufrufen, auch bevor erforderliche Felder gesetzt wurden. Fehlende Daten führen dann zu einem Panikfehler zur Laufzeit oder zu einem Err.

Builder mit Type-State verlagern diese Prüfung in die Compile-Zeit: Wenn Sie einen erforderlichen Schritt vergessen, schlägt die Kompilierung einfach fehl.

Zustände in Typen codieren

Der Trick besteht darin, den Builder über Markertypen generisch zu machen, die angeben, welche Schritte abgeschlossen sind. Sobald Sie ein Feld setzen, ändert sich der Typ des Builders.

Erst wenn jeder erforderliche Marker den Zustand "set" erreicht, wird eine build()-Methode verfügbar.

struct Missing;
struct Set;

Ein über Marker generischer Builder

Geben Sie dem Builder für den Zustand jedes erforderlichen Feldes einen Typparameter. PhantomData trägt den Marker, ohne tatsächliche Daten zu speichern.

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

Der Ausgangszustand

Der Konstruktor gibt einen Builder zurück, bei dem jeder erforderliche Marker Missing ist. Zu diesem Zeitpunkt gibt es kein build(), daher weiß das Typsystem, dass das Objekt unvollständig ist.

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

Einen Marker umschalten

Ein Setter verbraucht den alten Builder und gibt einen neuen zurück, bei dem der Marker dieses Feldes auf Set umgeschaltet wurde. Der andere Marker bleibt erhalten, indem sein Typparameter generisch bleibt.

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

Der zweite Übergang

Das Setzen der Methode funktioniert auf dieselbe Weise: Der zweite Marker wird von Missing auf Set umgeschaltet, während der erste unverändert bleibt.

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() erst bei vollständigem Zustand

Entscheidend ist, dass build() nur für ReqBuilder<Set, Set> implementiert ist. In jedem anderen Zustand existiert diese Methode einfach nicht, sodass der Aufruf nicht kompiliert.

Innerhalb dieser Methode können die Aufrufe von unwrap niemals in Panik geraten, weil der Typ beweist, dass beide Felder vorhanden sind.

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

Alles zusammenfügen

Eine korrekte Aufrufkette wird problemlos kompiliert, weil jeder Aufruf den Builder in Richtung ReqBuilder<Set, Set> weiterführt, wo build() vorhanden ist.

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

Der gewünschte Kompilierungsfehler

Wenn Sie einen erforderlichen Schritt überspringen, verweigert der Compiler die Kompilierung. Der Aufruf von build() für ReqBuilder<Set, Missing> meldet "no method named build" und erkennt das Versäumnis, bevor das Programm überhaupt ausgeführt wird.

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

Eine eigenständige ausführbare Version

Dieses minimale Programm verwendet ein einziges erforderliches Feld, um den vollständigen Übergang von Anfang bis Ende zu zeigen. Es wird kompiliert und gibt den erstellten Wert aus.

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);
}

Kosten und Abwägungen

Builder mit Type-State bieten Garantien zur Compile-Zeit ohne Laufzeitaufwand, da die Marker entfernt werden. Der Preis sind mehr Typkonstrukte und eine wachsende Zahl kombinatorischer impl-Blöcke, sobald mehr Felder erforderlich sind.

Verwenden Sie dieses Muster nur für APIs, bei denen ein unvollständiger Aufbau konstruktionsbedingt unmöglich sein muss.

Kurze Überprüfung

Ermitteln Sie, was bei einem Builder mit Type-State tatsächlich die Vollständigkeit erzwingt.

Zusammenfassung

Builder mit Type-State codieren jeden erforderlichen Schritt als Typparameter eines Markertyps, der von PhantomData getragen wird. Setter verbrauchen den Builder und geben einen neuen Typ zurück, bei dem ein Marker auf Set umgeschaltet wurde.

Da build() nur für den Zustand implementiert ist, in dem alle Marker auf Set stehen, führt ein vergessener Schritt zu einem Kompilierungsfehler statt zu einem Panikfehler zur Laufzeit – und das ohne Laufzeitkosten.

Häufig gestellte Fragen

Ist die Lektion „Builder mit Type-State“ kostenlos?

Ja — der vollständige Text von „Builder mit Type-State“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Learn Rust Coding-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Learn Rust Coding-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Builder mit Type-State“?

Kodieren Sie Gültigkeit im Typsystem. Du übst Learn Rust Coding mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um Learn Rust Coding zu starten?

Keine Vorkenntnisse erforderlich. Learn Rust Coding auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 3 von 4.

Wie lange dauert die Lektion „Builder mit Type-State“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser Learn Rust Coding-Lektion Code schreiben und ausführen?

Ja. Jede Learn Rust Coding-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Das Builder-Pattern
  2. Das Newtype-Pattern
  3. Builder mit Type-State
  4. Deref und ergonomische Wrapper
← Zurück zu Learn Rust Coding