Builder Pattern

Construct a Complex Struct with a Builder

The builder pattern provides a clean and readable way to construct complex objects, especially when dealing with multiple optional fields or validation rules.

  • Instead of creating the object directly, you first create a separate Builder object. It is typically named xyzBuilder where xyz is the name of the struct you're building. The builder holds the state needed to construct the final object, often storing fields as Option<T> internally.
  • The builder provides methods (often named after the fields they set, like .name("Example"), .timeout(Duration::from_secs(5))) to configure the object piece by piece. These methods usually return self, allowing you to chain calls together fluently: builder.field_a(value_a).field_b(value_b).
  • Once the desired fields are set, you call a final method on the builder (commonly named .build() or .finish()). This method takes the configuration stored in the builder, performs any necessary validation (like checking if required fields were provided), and then constructs and returns the final, fully-formed object. If the validation can fail, the build method typically returns a Result.
#![allow(dead_code)]
//! Example builder pattern.

/// The object to build.
#[derive(Debug)]
struct DatabaseConfig {
    host: String,
    port: u16,
    username: Option<String>,
    password: Option<String>,
    pool_size: u32,
    connection_timeout_seconds: u64,
    // More fields...
}

impl DatabaseConfig {
    /// Optional: you may provide a method that returns a builder.
    fn build(host: String, port: u16) -> DatabaseConfigBuilder {
        DatabaseConfigBuilder::new(host, port)
    }
    // Note that there are no constructor-like method otherwise.
}

/// The builder object.
struct DatabaseConfigBuilder {
    host: String,
    port: u16,
    username: Option<String>,
    password: Option<String>,
    pool_size: u32,
    connection_timeout_seconds: u64,
}

impl DatabaseConfigBuilder {
    /// The builder's constructor (with required fields, if necessary).
    fn new(host: String, port: u16) -> DatabaseConfigBuilder {
        DatabaseConfigBuilder {
            host,
            port,
            // The optional fields are set to None or default values.
            username: None,
            password: None,
            pool_size: 10,
            connection_timeout_seconds: 30,
        }
    }

    /// Note that the builder's methods consume `self` and returns `Self`.
    fn username(mut self, username: String) -> Self {
        self.username = Some(username);
        self
    }

    fn password(mut self, password: String) -> Self {
        self.password = Some(password);
        self
    }

    fn pool_size(mut self, pool_size: u32) -> Self {
        self.pool_size = pool_size;
        self
    }

    fn connection_timeout_seconds(
        mut self,
        connection_timeout_seconds: u64,
    ) -> Self {
        self.connection_timeout_seconds = connection_timeout_seconds;
        self
    }

    fn build(self) -> DatabaseConfig {
        DatabaseConfig {
            host: self.host,
            port: self.port,
            username: self.username,
            password: self.password,
            pool_size: self.pool_size,
            connection_timeout_seconds: self.connection_timeout_seconds,
        }
    }
}

fn main() {
    // Create a builder struct.
    let config = DatabaseConfigBuilder::new("localhost".to_string(), 5432)
        // Call (some of) the builder's methods to initialize or update the fields.
        .username("admin".to_string())
        .password("secret".to_string())
        .pool_size(20)
        .build();

    println!("Database Configuration: {:?}", config);
}

typed-builder

typed-builder typed-builder-crates.io typed-builder-github typed-builder-lib.rs cat-rust-patterns

typed-builder lets you derive compile-time type-checked builders. It uses a derive macro and leverages Rust's type system to ensure that all required fields are set. If you forget a required field, your code won't even compile.

derive_builder

derive_builder derive_builder-crates.io derive_builder-github derive_builder-lib.rs cat-development-tools cat-rust-patterns

derive_builder provides a #[derive(Builder)] macro to automatically implement the builder pattern for arbitrary structs. It performs checks at runtime within the .build() method.

bon

bon-website bon bon-crates.io bon-github bon-lib.rs cat-asynchronous cat-data-structures cat-no-std cat-no-std::no-alloc cat-rust-patterns

bon is a compile-time-checked builder generator with additional features like support for fallible/async builders and named function arguments via the builder pattern.