I appreciate the tradeoffs that led to the builder pattern becoming commonplace, but it's probably my least favorite part of Rust. Compared to optional named parameters it just feels clunky, verbose and kind of Java-esque.
IMO, the toy example in the article is too simple to show a good use of the pattern. The builder pattern is great if the builder methods configure complicated invariants and are not just setters. Otherwise, there's nothing wrong with making the struct public, as in User { id, email, ..Default::default() } What's Java-esque is having an irrational fear of using public fields for things that are just data containers, b…
Builder Pattern in Rust
11–20 of 93 posts
Re: Builder Pattern in Rust
#12I appreciate the tradeoffs that led to the builder pattern becoming commonplace, but it's probably my least favorite part of Rust. Compared to optional named parameters it just feels clunky, verbose and kind of Java-esque.
I agree. I’ve seen a few libraries (I can’t remember which) instead take the approach of a constructor that takes a single-use struct as an argument. The struct implements Default and all of its members are public. You can then use ..Struct::default() to splice in the default values, and override only the values you want to set. It ends up being an approximation of named optional arguments, albeit with a somewhat mor…
f(arg1, arg2, _ {name: "Hello", address, ...})
instead of f(arg1, arg2, MyKwargs {name: "Hello", address, ..Default::default()}
Using a struct for this feels natural as you get forwarding and non-exhaustiveness for adding additional fields for free.Re: Builder Pattern in Rust
#13Earlier quoted context omitted.
I hope that one day the default struct field values RFC will be reconsidered: https://github.com/rust-lang/rfcs/pull/1806 . This would allow a syntax more familiar to users of JavaScript, where fields with their default value can be omitted: struct Person { name: String, email: Option = None, } let person = Person { name: "John Doe".to_owned(), .. };
This is probably obvious and it isn't 100% alternative to Javascript syntax, but you can leverage Default trait to come a bit closer: #[derive(Default)] struct Person { name: String, email: Option , } fn main() { let person = Person { name: "John Doe".to_owned(), ..Default::default() }; }
Re: Builder Pattern in Rust
#14Where I've really seen builders be useful is when you have lots and lots of parameters, where most of the time defaults are fine, and you may actually want to pass then around to other parts of the systems, perhaps with different dependencies, who will then further modify them. An example of this would be configuring a client. Your unit tests may want the client to literally just be a function that returns "OK", your…
I'm downvoted but I am legitimately curious.
Re: Builder Pattern in Rust
#15I'm surprised the article doesn't mention Default:
https://doc.rust-lang.org/std/default/trait.Default.html
It can be combined with the rest pattern to yield something very similar to the solution the author is after.
https://stackoverflow.com/questions/19650265/is-there-a-fast...
Re: Builder Pattern in Rust
#16I appreciate the tradeoffs that led to the builder pattern becoming commonplace, but it's probably my least favorite part of Rust. Compared to optional named parameters it just feels clunky, verbose and kind of Java-esque.
let _clocks = rcc
.cfgr
.hse(HSEClock::new(25_000_000.Hz(), HSEClockMode::Bypass))
.sysclk(216_000_000.Hz())
.hclk(216_000_000.Hz())
.freeze();
where the freeze function does a lot of calculation to set up the peripheral [2].[1]: https://github.com/stm32-rs/stm32f7xx-hal/blob/0a0d06d5f63c3...
[2]: https://github.com/stm32-rs/stm32f7xx-hal/blob/0a0d06d5f63c3...
Re: Builder Pattern in Rust
#17Well... what kind of reasoning is that? Yes, there is a technique to solve that problem (or part of the problem) perfectly but... you choose not to use it so that you can try to figure out a second, suboptimal way?
I don't know about that
Re: Builder Pattern in Rust
#18> Since we do not have default arguments in Rust, in order to initialize such structure we would have to list all fields: I'm surprised the article doesn't mention Default: https://doc.rust-lang.org/std/default/trait.Default.html It can be combined with the rest pattern to yield something very similar to the solution the author is after. https://stackoverflow.com/questions/19650265/is-there-a-fast...
Re: Builder Pattern in Rust
#19Earlier quoted context omitted.
I agree. I’ve seen a few libraries (I can’t remember which) instead take the approach of a constructor that takes a single-use struct as an argument. The struct implements Default and all of its members are public. You can then use ..Struct::default() to splice in the default values, and override only the values you want to set. It ends up being an approximation of named optional arguments, albeit with a somewhat mor…
I would quite like to see some syntax sugar around this. If we could elide the type and made `...` equivalent to `..Default::default()` it would be a lot closer to kwargs without having to introduce any new "real" features. So we could write: f(arg1, arg2, _ {name: "Hello", address, ...}) instead of f(arg1, arg2, MyKwargs {name: "Hello", address, ..Default::default()} Using a struct for this feels natural as you get…
struct Foo {
bar: String, // No default
baz: Option = None,
}
I think this would give us 90% of what I would want from first-class named parameters.Re: Builder Pattern in Rust
#20Earlier quoted context omitted.
I hope that one day the default struct field values RFC will be reconsidered: https://github.com/rust-lang/rfcs/pull/1806 . This would allow a syntax more familiar to users of JavaScript, where fields with their default value can be omitted: struct Person { name: String, email: Option = None, } let person = Person { name: "John Doe".to_owned(), .. };
This is probably obvious and it isn't 100% alternative to Javascript syntax, but you can leverage Default trait to come a bit closer: #[derive(Default)] struct Person { name: String, email: Option , } fn main() { let person = Person { name: "John Doe".to_owned(), ..Default::default() }; }
Forgetting a required field just means you get the dummy value from Default, that is, if you even manage to implement Default for the struct at all (the required fields may not have a sensible default value)