Builder type design: return &mut Self vs Self

For example, I'm designing a builder:

#[derive(Default)]
struct BarBuilder {

}

impl BarBuilder {
    pub fn add_foo(&mut self) -> &mut Self {
        // add foo
        self
    }

    pub fn add_foo(mut self) -> Self { // even though passed in a mut self, trait it as a &mut self
        self.add_foo(&mut self)
    }

    pub fn build(self) -> Bar { // build will consume self
        ...
    }
}

fn main() {
    let bar = BarBuilder::default().add_foo().build();
    
    
    let bar_builder =  BarBuilder::default();
    bar_builder.add_foo();
    let bar = bar_builder.build();
}

If I can check whether the input of add_foo is a mut self or a &mut self and return the correspond type, it is really convenient to write both codes:

    let bar = BarBuilder::default().add_foo().build();

and

    let bar_builder =  BarBuilder::default();
    bar_builder.add_foo();
    let bar = bar_builder.build();

But Rust does not support function overloading. I tried to use AsRef<Self>, but it seems that only self, &self, &mut self, self: Box<Self>, self: Rc<Self>, self: Arc<Self>, or self: Pin<P> are allowed.

So is there any way I can do this?

AFAIK, no.

You could have a trait with add_foo(self) -> Self and implement it for BarBuilder and &mut BarBuilder... but IMO you shouldn't.

  • Requires importing the trait
  • Non-idiomatic and unintuitive
  • Wouldn't actually work with your example as written, because the owning impl takes precedence

Somewhat satisfactory pattern I've found for xshell's Cmd is to just have both functions.

impl Builder {
    fn foo(self, foo: Foo) -> Self;
    fn set_foo(&mut self, foo: Foo);
}

It works especially well for boolean flags:

impl Builder {
    fn extra_cute(self) -> Self;
    fn set_extra_cute(&mut self, yes: bool);
}
8 Likes