In this lesson, you will learn how to use the Builder pattern to consume owned data and transform it into a final, immutable object through state transitions.
The Problem of Partial Initialization
In Rust, we often want to construct complex objects where some fields are mandatory and others are optional. If we use a simple constructor function, we might end up with an unwieldy list of arguments. Worse, if we want to ensure an object is fully valid before it exists, we can use the Builder pattern to move ownership of data into a builder struct and consume that builder to produce the final product.
Consuming Ownership
The key to a "consuming" builder is that each method takes self and returns Self. By taking ownership, we ensure that the builder cannot be reused once the final object is built, preventing invalid states.
struct Config {
url: String,
port: u16,
}
struct ConfigBuilder {
url: Option<String>,
port: Option<u16>,
}
impl ConfigBuilder {
fn new() -> Self {
Self { url: None, port: None }
}
fn url(mut self, url: String) -> Self {
self.url = Some(url);
self
}
fn port(mut self, port: u16) -> Self {
self.port = Some(port);
self
}
fn build(self) -> Config {
Config {
url: self.url.expect("URL is required"),
port: self.port.unwrap_or(80),
}
}
}
Why Ownership Matters
By using self in our methods (e.g., fn url(mut self, ...)), we force the caller to move their data into the builder. When we call .build(), the builder consumes itself. This is powerful because it guarantees that once you have a Config, the ConfigBuilder is destroyed, and the internal data—like our String—has been safely moved into its final home. This pattern avoids unnecessary cloning; the data flows from the builder's fields directly into the final struct. It creates a clean "pipeline" where the state is strictly advanced until the final immutable structure is generated.
Key Takeaway: The Builder pattern consumes ownership to enforce a one-way pipeline, ensuring that builders are destroyed upon completion and data is moved rather than copied into the final immutable object.