Lär dig programmera i Rust · Lektion

Builder-mönstret

Skapa objekt steg för steg.

Lektion 1 av 413 steg

Builder-mönstret är en gratis lektion i Lär dig programmera i Rust på CoddyKit. Detta är lektion 1 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för Lär dig programmera i Rust, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Lär dig programmera i Rust innehåller totalt 4 lektioner.

Varför en builder?

Rust har inga namngivna eller valfria funktionsargument. När en struct har många fält, särskilt valfria sådana, blir en konstruktor med åtta positionsparametrar svårläst och felbenägen.

Builder-mönstret löser detta. Ni konfigurerar ett objekt steg för steg med namngivna metoder och anropar sedan ett avslutande build() för att skapa värdet. Koden läses som en flytande mening.

Målstrukturen

Börja med den typ ni faktiskt vill skapa. Här innehåller en serverkonfiguration en obligatorisk värd samt flera valfria inställningar.

Observera att fälten är privata för att uppmuntra konstruktion genom buildern i stället för struct-literaler.

pub struct ServerConfig {
    host: String,
    port: u16,
    max_connections: usize,
    use_tls: bool,
}

En separat builder-typ

Det klassiska tillvägagångssättet använder en andra struct, buildern. Den speglar måltypen men lagrar tillståndet under uppbyggnad. Valfria fält blir ofta Option<T>, så att ni kan skilja mellan ”inte angivet” och ”uttryckligen angivet”.

pub struct ServerConfigBuilder {
    host: String,
    port: Option<u16>,
    max_connections: Option<usize>,
    use_tls: bool,
}

Starta buildern

Ge buildern en konstruktor som endast tar de obligatoriska fälten. Alla valfria fält börjar som None eller med ett standardvärde.

En vanlig konvention är en metod builder() på måltypen som returnerar buildern.

impl ServerConfig {
    pub fn builder(host: impl Into<String>) -> ServerConfigBuilder {
        ServerConfigBuilder {
            host: host.into(),
            port: None,
            max_connections: None,
            use_tls: false,
        }
    }
}

Setter-metoder tar self med värde

Varje setter förbrukar self, muterar ett fält och returnerar self. Genom att returnera det ägda värdet kan ni kedja anrop på ett flytande sätt.

Denna ägarskapsbaserade kedjning är den idiomatiska Rust-stilen och undviker problem med livstider.

impl ServerConfigBuilder {
    pub fn port(mut self, port: u16) -> Self {
        self.port = Some(port);
        self
    }
    pub fn use_tls(mut self, yes: bool) -> Self {
        self.use_tls = yes;
        self
    }
}

Tillämpa standardvärden i build()

Det avslutande build() omvandlar buildern till den riktiga typen. Här fyller ni i standardvärden för allt som fortfarande är None med hjälp av unwrap_or.

impl ServerConfigBuilder {
    pub fn build(self) -> ServerConfig {
        ServerConfig {
            host: self.host,
            port: self.port.unwrap_or(8080),
            max_connections: self.max_connections.unwrap_or(128),
            use_tls: self.use_tls,
        }
    }
}

Flytande konstruktion

Nu läses konstruktionen uppifrån och ned. Obligatoriska data skickas till builder(), och varje valfri ändring görs med ett tydligt namngivet anrop.

Fält som ni hoppar över får automatiskt sina standardvärden.

fn main() {
    let cfg = ServerConfig::builder("localhost")
        .port(9000)
        .use_tls(true)
        .build();
    println!("{}:{} tls={}", cfg.host, cfg.port, cfg.use_tls);
}

Ett komplett körbart exempel

Här är hela mönstret sammanfattat i ett program som ni kan köra. Det visar att överhoppade fält får sina standardvärden inuti build().

struct Config { name: String, retries: u32 }
struct Builder { name: String, retries: Option<u32> }
impl Config {
    fn builder(name: &str) -> Builder {
        Builder { name: name.to_string(), retries: None }
    }
}
impl Builder {
    fn retries(mut self, n: u32) -> Self { self.retries = Some(n); self }
    fn build(self) -> Config {
        Config { name: self.name, retries: self.retries.unwrap_or(3) }
    }
}
fn main() {
    let c = Config::builder("job").build();
    println!("{} retries={}", c.name, c.retries);
}

Felhanterande build med Result

Ibland kan en konfiguration vara ogiltig, till exempel om porten är noll. Låt build() returnera Result, så att valideringsfel blir återställningsbara fel i stället för panik.

impl ServerConfigBuilder {
    pub fn try_build(self) -> Result<ServerConfig, String> {
        let port = self.port.unwrap_or(8080);
        if port == 0 {
            return Err("port must be non-zero".into());
        }
        Ok(ServerConfig { host: self.host, port,
            max_connections: self.max_connections.unwrap_or(128),
            use_tls: self.use_tls })
    }
}

Crate: derive_builder

Att skriva builders för hand är repetitivt. Crate:t derive_builder genererar hela buildern från en annotation.

Ni annoterar fält med standardvärden och får en genererad FooBuilder med setters och ett felhanterande build() utan extra arbete.

use derive_builder::Builder;

#[derive(Builder)]
struct Channel {
    #[builder(default = "8080")]
    port: u16,
    name: String,
}

Builders som äger self jämfört med builders med muterbar referens

Det finns två stilar. Den ägande stilen förbrukar self och lämpar sig naturligt för kedjning. Stilen med &mut self returnerar &mut Self och låter er dela upp konstruktionen över flera satser utan att binda om värdet.

Den ägande stilen är vanligare vid konstruktion i ett enda steg, medan den muterbara stilen passar villkorsstyrd konfiguration i loopar.

impl ServerConfigBuilder {
    pub fn port_ref(&mut self, port: u16) -> &mut Self {
        self.port = Some(port);
        self
    }
}

Snabbtest

Testa era kunskaper om builder-stilen med ägt self.

Sammanfattning

Builder-mönstret kringgår Rusts avsaknad av valfria argument. En builder-typ lagrar tillståndet under uppbyggnad, setters förbrukar och returnerar self för kedjning, och build() tillämpar standardvärden för att skapa det slutliga värdet.

Använd Result från build() för validering och använd derive_builder när ni vill slippa standardkoden.

Gratis att börja

Lär dig Rust med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
39
Lektioner
144

Vanliga frågor

Är lektionen ”Builder-mönstret” gratis?

Ja – du kan läsa vilka 3 lektioner som helst i lärvägen Lär dig programmera i Rust, inklusive ”Builder-mönstret”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i Lär dig programmera i Rust innehåller totalt 4 lektioner.

Vad lär jag mig i ”Builder-mönstret”?

Skapa objekt steg för steg. Ni övar på Lär dig programmera i Rust med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Lär dig programmera i Rust?

Du behöver inga förkunskaper. Utbildningen i Lär dig programmera i Rust på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 1 av 4.

Hur lång tid tar lektionen ”Builder-mönstret”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Lär dig programmera i Rust-lektionen?

Ja. Varje Lär dig programmera i Rust-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Builder-mönstret
  2. Newtype-mönstret
  3. Type-state-builders
  4. Deref och wrapper-ergonomi
← Tillbaka till Lär dig programmera i Rust