matklad

Who Builds the Builder?

This is a short note on the builder pattern, or, rather, on the builder method pattern.

TL;DR: if you have Foo and FooBuilder, consider adding a builder method to Foo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
struct Foo { ... }

#[derive(Default)]
struct FooBuilder { ... }

impl Foo {
    fn builder() -> FooBuilder {
        FooBuilder::default()
    }
}

impl FooBuilder {
    fn build(self) -> Foo { ... }
}

A more minimal solution is to rely just on FooBuilder::default or FooBuilder::new. There are two problems with that:

First, it is hard to discover. Nothing in the docs/signature of Foo mentions FooBuilder, you need to look elsewhere to learn how to create a Foo. I remember being puzzled at how to create a GlobSet for exactly this reason. In contrast, the builder method is right there on Foo, probably the first one.

Second, it is more annoying to use, as you need to import both Foo and FooBuilder. With Foo::builder method often only one import suffices, as you don’t need to name the builder type.

Case studies:

Discussion on /r/rust.