Wzorzec Builder
Zbuduje Pan/Pani obiekty krok po kroku.
Wzorzec Builder to bezpłatna lekcja Learn Rust Coding na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Learn Rust Coding, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Learn Rust Coding zawiera 4 lekcji w sumie.
Po co builder?
Rust nie ma nazwanych ani opcjonalnych argumentów funkcji. Gdy struktura ma wiele pól, zwłaszcza opcjonalnych, konstruktor z ośmioma argumentami pozycyjnymi staje się nieczytelny i podatny na błędy.
Wzorzec builder rozwiązuje ten problem. Konfigurujesz obiekt krok po kroku za pomocą nazwanych metod, a następnie wywołujesz końcowe build(), aby utworzyć wartość. Taki kod przypomina płynne zdanie.
Docelowa struktura
Zacznij od typu, który chcesz rzeczywiście utworzyć. W tym przykładzie konfiguracja serwera zawiera wymagany host oraz kilka opcjonalnych ustawień.
Zwróć uwagę, że pola są prywatne, aby zachęcać do tworzenia obiektów przez builder zamiast za pomocą literałów struktur.
pub struct ServerConfig {
host: String,
port: u16,
max_connections: usize,
use_tls: bool,
}Osobny typ buildera
Klasyczne podejście korzysta z drugiej struktury — buildera. Odwzorowuje on typ docelowy, ale przechowuje stan tworzenia obiektu. Pola opcjonalne często stają się Option<T>, dzięki czemu można rozróżnić wartość „nieustawioną” od „ustawionej jawnie”.
pub struct ServerConfigBuilder {
host: String,
port: Option<u16>,
max_connections: Option<usize>,
use_tls: bool,
}Rozpoczynanie pracy z builderem
Wyposaż builder w konstruktor, który przyjmuje tylko wymagane pola. Wszystkie opcjonalne zaczynają jako None albo otrzymują wartość domyślną.
Popularną konwencją jest metoda builder() w typie docelowym, która zwraca builder.
impl ServerConfig {
pub fn builder(host: impl Into<String>) -> ServerConfigBuilder {
ServerConfigBuilder {
host: host.into(),
port: None,
max_connections: None,
use_tls: false,
}
}
}Metody ustawiające przyjmują self przez wartość
Każda metoda ustawiająca przejmuje self, modyfikuje pole i zwraca self. Zwracanie posiadanej wartości pozwala płynnie łączyć wywołania.
Takie łańcuchowe wywołania oparte na własności są idiomatycznym stylem w Rust i pozwalają uniknąć problemów z czasami życia.
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
}
}Stosowanie wartości domyślnych w build()
Końcowe build() przekształca builder w rzeczywisty typ. W tym miejscu uzupełniasz wartości domyślne dla wszystkiego, co nadal ma wartość None, korzystając z 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,
}
}
}Płynne tworzenie obiektu
Teraz tworzenie obiektu przebiega od góry do dołu. Wymagane dane trafiają do builder(), a każda opcjonalna zmiana jest wyraźnie nazwaną metodą.
Pominięte pola po cichu otrzymują wartości domyślne.
fn main() {
let cfg = ServerConfig::builder("localhost")
.port(9000)
.use_tls(true)
.build();
println!("{}:{} tls={}", cfg.host, cfg.port, cfg.use_tls);
}Kompletny uruchamialny przykład
Oto cały wzorzec zebrany w jednym programie, który można uruchomić. Pokazuje on, że pominięte pola otrzymują wartości domyślne wewnątrz 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);
}Fallible build with Result
Czasami konfiguracja może być nieprawidłowa, na przykład gdy port ma wartość zero. Spraw, aby build() zwracało Result, dzięki czemu błędy walidacji będą obsługiwane i możliwe do odzyskania zamiast powodować 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
Ręczne pisanie builderów jest powtarzalne. Crate derive_builder generuje cały builder na podstawie adnotacji.
Oznaczasz pola wartościami domyślnymi i otrzymujesz wygenerowany FooBuilder z metodami ustawiającymi oraz fallible build() bez dodatkowego kodu.
use derive_builder::Builder;
#[derive(Builder)]
struct Channel {
#[builder(default = "8080")]
port: u16,
name: String,
}Builder posiadający dane a builder z mutowalną referencją
Istnieją dwa style. Styl posiadający dane przejmuje self i naturalnie umożliwia łańcuchowe wywołania. Styl &mut self zwraca &mut Self i pozwala rozdzielić tworzenie obiektu na kilka instrukcji bez ponownego przypisywania.
Styl posiadający dane jest częstszy przy jednorazowym tworzeniu obiektu, natomiast styl mutowalny sprawdza się przy warunkowej konfiguracji w pętlach.
impl ServerConfigBuilder {
pub fn port_ref(&mut self, port: u16) -> &mut Self {
self.port = Some(port);
self
}
}Szybki test
Sprawdź swoją wiedzę o stylu buildera wykorzystującym posiadane self.
Podsumowanie
Wzorzec builder radzi sobie z brakiem opcjonalnych argumentów w Rust. Typ buildera przechowuje stan tworzenia obiektu, metody ustawiające przejmują i zwracają self, umożliwiając łańcuchowe wywołania, a build() stosuje wartości domyślne i tworzy końcową wartość.
Używaj Result w build() do walidacji, a po derive_builder sięgaj wtedy, gdy chcesz uniknąć powtarzalnego kodu.
Często zadawane pytania
Czy lekcja „Wzorzec Builder” jest bezpłatna?
Tak — pełny tekst „Wzorzec Builder” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Learn Rust Coding, przejdź na CoddyKit PRO. Kurs Learn Rust Coding zawiera 4 lekcji w sumie.
Co nauczysz się w „Wzorzec Builder”?
Zbuduje Pan/Pani obiekty krok po kroku. Ćwiczysz Learn Rust Coding z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Learn Rust Coding?
Nie wymagamy żadnego doświadczenia. Learn Rust Coding w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.
Ile czasu zajmuje lekcja „Wzorzec Builder”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Learn Rust Coding?
Tak. Każda lekcja Learn Rust Coding zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.