Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use the chained builder pattern in a loop without creating a compiler error?

Tags:

rust

How do you properly use a builder pattern that expects method chaining in a loop? Using an example from log4rs. Notice self isn't a reference in appender.

//builder pattern from log4rs

pub struct ConfigBuilder {
    appenders: Vec<Appender>,
    loggers: Vec<Logger>,
}

impl ConfigBuilder {
    pub fn appender(mut self, appender: Appender) -> ConfigBuilder {
        self.appenders.push(appender);
        self
    }
}

Doing this below results in an error because (I think) cb is getting moved to the memory being returned by .appender().

let cb = ConfigBuilder::new();
for x in ys {
    cb.appender(x);
}

This below appears to work. Is this the only way to do it?

let mut cb = ConfigBuilder::new();
for x in ys {
    cb = cb.appender(x);
}
like image 910
marathon Avatar asked Sep 14 '25 07:09

marathon


1 Answers

Is this the only way to do it?

Semantically it is the critical way, though there are other ways to write it. The appender function takes mut self so it will take ownership of the cb variable's value and make the variable unusable after that point. It could have been designed to borrow a reference, but chaining is nice. Since you're in a loop, the builder needs to be available on the next iteration, so you need to assign the value to something new. This means that

let mut cb = ConfigBuilder::new();
for x in ys {
    cb = cb.appender(x);
}

is indeed one way to do that. Another way would be to use Iterator's .fold to do

let cb = ys.into_iter()
  .fold(ConfigBuilder::new(), |cb, x| cb.appender(x));

which keeps everything in one assignment, but is otherwise pretty much the same.

like image 72
loganfsmyth Avatar answered Sep 15 '25 22:09

loganfsmyth