I have a couple of questions regarding how builder should be implemented idiomatically in Rust when it's necessary to call its parts depending on some flags.
Here's my take on three different designs of builders (also with usage example on rust-playground):
#[derive(Debug, Default)]
pub struct MutableBuilder {
value: Option<i8>
}
impl MutableBuilder {
pub fn add_value(&mut self, value: i8) -> &mut Self {
self.value = self
.value
.map(|current| current + value)
.or(Some(value));
self
}
pub fn with_add_value(mut self, value: i8) -> Self {
self.add_value(value);
self
}
pub fn build(self) -> i8 {
self.value.unwrap_or_default()
}
}
#[derive(Debug, Default)]
pub struct ImmutableBuilder {
value: Option<i8>
}
impl ImmutableBuilder {
pub fn with_add_value(mut self, value: i8) -> Self {
self.value = self
.value
.map(|current| current + value)
.or(Some(value));
self
}
pub fn build(self) -> i8 {
self.value.unwrap_or_default()
}
}
#[derive(Debug, Default)]
pub struct WithOptionBuilder {
value: Option<i8>
}
impl WithOptionBuilder {
pub fn with_add_value(mut self, value: impl Into<Option<i8>>) -> Self {
self.value = value.into()
.map(|value| self
.value
.map_or(value, |current| current + value)
)
.or(self.value);
self
}
pub fn build(self) -> i8 {
self.value.unwrap_or_default()
}
}
It seems to me that MutableBuilder
is not how it's usually encouraged to be done in Rust, it also mixes mutable and immutable styles somewhat, but this was my initial thought on how to implement this.
ImmutableBuilder
seems to allow for less flexibility and requires reassigning the variable in the calling code if there are conditional calls.
WithOptionBuilder
seems to be the most flexible, but it struggles to coerce types if I wanted to pass impl Into<ValueType>
instead of ValueType
because it already does impl Into<Option<ValueType>>
. This can be solved by making the parameter Option<impl Into<ValueType>>
but this makes boilerplate code appear at all unconditional call sites.
Another question is how to name the function that modifies value inside the builder rather than sets it. I came up with a scheme of with_{verb}_{value_name}
but I am not sure if that's the usual way.