Live data from Hacker News

Builder Pattern in Rust

greyblake.com

11–20 of 93 posts

Re: Builder Pattern in Rust

#11
post #7
post #2

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…

This pattern is fairly good but doesn't support required arguments.

Re: Builder Pattern in Rust

#12
post #4
post #2

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.

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 forwarding and non-exhaustiveness for adding additional fields for free.

Re: Builder Pattern in Rust

#13
post #9
post #5

Earlier 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() }; }

It would be really nice if .. could be a shortcut for ..Default::default() which would match the syntax of pattern matching.

Re: Builder Pattern in Rust

#14

Where 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…

Why is this better than a configurable config object that you pass around?

I'm downvoted but I am legitimately curious.

Re: Builder Pattern in Rust

#15
> 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

#16
post #2

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.

It is useful in embedded. For example, you may see something like [1]:

    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

#17
> yeah, the newtype technique could help us here, but this article is not about that

Well... 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
post #15

> 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...

This solution works sometimes, but it's not great if any of the fields don't have a sensible default.

Re: Builder Pattern in Rust

#19
post #12
post #4

Earlier 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…

I would like this plus the ability to specify per-field defaults on structs like

    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

#20
post #9
post #5

Earlier 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() }; }

The deal breaker with `..Default::default()` is that it only works for structs without any required fields.

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)

Post reply on HN