patternrustTip
Builder pattern: constructing complex structs ergonomically
Viewed 0 times
builderpatternchainingconstructorderive_builderDefaultergonomic API
Problem
Structs with many optional fields require either massive constructor signatures or many Optional<T> fields that are awkward to set.
Solution
Implement a builder struct with chained methods and a final build() call:
#[derive(Debug)]
pub struct ServerConfig {
host: String,
port: u16,
max_connections: usize,
timeout_secs: u64,
tls: bool,
}
#[derive(Default)]
pub struct ServerConfigBuilder {
host: String,
port: u16,
max_connections: usize,
timeout_secs: u64,
tls: bool,
}
impl ServerConfigBuilder {
pub fn new() -> Self { Self::default() }
pub fn host(mut self, host: impl Into<String>) -> Self {
self.host = host.into(); self
}
pub fn port(mut self, port: u16) -> Self {
self.port = port; self
}
pub fn max_connections(mut self, n: usize) -> Self {
self.max_connections = n; self
}
pub fn tls(mut self) -> Self { self.tls = true; self }
pub fn build(self) -> ServerConfig {
ServerConfig {
host: if self.host.is_empty() { String::from("0.0.0.0") } else { self.host },
port: if self.port == 0 { 8080 } else { self.port },
max_connections: self.max_connections.max(1),
timeout_secs: if self.timeout_secs == 0 { 30 } else { self.timeout_secs },
tls: self.tls,
}
}
}
let config = ServerConfigBuilder::new()
.host("localhost")
.port(3000)
.tls()
.build();Why
The builder pattern separates construction from the final type, allows defaults, and produces readable call sites. The derive_builder crate can auto-generate most of this boilerplate.
Gotchas
- Returning self (by value) from each setter enables method chaining but requires mut self — &mut self chaining needs explicit return self
- The derive_builder crate auto-generates builder structs from #[derive(Builder)] — avoids writing boilerplate manually
- build() can return Result to validate fields rather than panicking on bad input
Revisions (0)
No revisions yet.