Builder Pattern low SNR?


#1

It feels to me that signal to noise ration for Builder Pattern is rather low (https://doc.rust-lang.org/book/method-syntax.html).

Instead one could do something like this:

struct Circle {
x :f32,
y :f32,
radius :f32
};
const Circle_INIT :Circle = Circle{x: 0., y: 0., radius: 1.};

impl Circle{
fn new() -> Circle{
Circle{…Circle_INIT}
}
// etc… interface
}

// a la Builder Pattern

let c1 = Circle {
radius: 5,
…Circle_INIT
}

let c1 = Circle {
y: 5.,
radius: 23.,
…Circle_INIT
}

Thoughts?
-D


#2

The problem is that you can’t guarantee that people won’t write Circle { x: 1.0, y: 2.0, radius: 3.0 }, which means that if you add a new field to the struct, it’s a breaking change.


#3

Well, if people do the above in the presence of the Builder Pattern (BP), they would also have the exact same problem. Convincing people to do the right thing is orthogonal to figuring out what exactly we think the right thing might be. If the right thing at the moment is the BP, then I was suggesting a way to accomplish what BP offers with a 4x less code, without the loss of either clarity or generality. This approach also has better performance metrics, as there is no need to instantiate a Builder as an extra, which, depending on the struct at at hand, can be arbitrarily complex. Also, if one leaves out …Circle_INIT at the end it is always a compile time error, while if one forgets to add .finalize() from BP, in some cases it would only be a run-time error.


#4

I think this is possible already but using a function instead of a constant. The vulkano crate does something similar in its triangle example. The function returns DeviceExtensions as described.


#5

As long as DeviceExtensions::none() doesn’t instantiate the DeviceExtensions on every invocation, it would be a similar case/idea. Not the same exactly, though, because DE::none() is not intended as the “default prototype”, but rather as a specific one. For a “default prototype”, “none” would be a poor name choice, I would think. Maybe one of “prototype”, “defaults” or something similar would convey the intent more accurately. The implementation of none() is way over my head at this point and certainly is of higher complexity than a constant, I think.


#6

There is always the Default trait. Example.


#7

Your example instantiates two of Foo’s instead of one, I believe. The one in the “let” in main() and the other one inside default(). This would be happening every time one wants to instantiate a Foo. I have not reached the traits yet in my readings on Rust; perhaps I am wrong about my understanding of your example.