1. 18
  1.  

  2. 11

    There is also std::default::Default trait one can use, and then have the structure populated with

    
    let greyblake = User {
        email: Some("example@example.com".to_string()),
        first_name: Some("Sergey".to_string()),
        ..Default::default()
    }
    

    No need to go for the Builder pattern if not necessary.

    I’m not claiming this is more idiomatic or readable, just trying to offer another implementation with less code.

    1. 6

      This requires all fields to be public, and when all fields are public, then addition of a new field is a semver-break (you can’t expect all users to always add ..default()). I had such whack-a-mole with comrak config.

      1. 1

        You can use #[non_exhaustive] or keep a private field to force the use of default!

        1. 3

          Unfortunately, this also forbids construction of the struct outside of the defining crate. Same if the struct has any private field. The ..default syntax has no extra privileges, and can’t do anything that couldn’t be done with a struct literal.

          1. 5

            This is discussed on Pre-RFC: Relaxed #[non_exhaustive] structs, among other places. There are two ideas that would work:

            • A new attribute #[non_exhaustive(pub)] or a trailing pub .. field, which indicates that only public fields will be added (i.e. adding private fields will be source breaking).
            • Allowing a move keyword in the functional update syntax { x: 42, .. move foo } that sidesteps the issue by desugaring differently (first taking foo, and then assigning the given fields).

            The second is more powerful since it allows private fields. Changing the functional update syntax to always desugar that way would could break existing code, hence the move keyword. Either of these combined with default_free_fn or making .. shorthand for ..default(), would be pretty nice.

            In the FIDL Rust bindings, we use this workaround for now:

            #[deprecated = "Use `..Foo::EMPTY` to construct and `..` to match."]
            #[doc(hidden)]
            pub __non_exhaustive: (),
            
            1. 1

              This is one of those things where I’d personally be in favor of Default getting special treatment to enable some pattern of “construction with certain things set to custom values, but otherwise default” that doesn’t require the crate defining the type to explicitly provide such a constructor.

      2. 6

        The pattern fits fine but I wonder how much of this boilerplate could be reduced with some kind of named arguments support.

        1. 5

          For me, keyword arguments should just be table stakes for any new (imperative) language. The increased readability of procedure calls just makes it a no-brainer for me. I was so disappointed not to find them in Rust, or any plans for including them.

          1. 3

            No kidding! Every time I write one of these builder things I die inside a little. Umpteen hundred lines of boilerplate just to be able to properly build a stupid struct, all because somehow saying foo::builder().arg1("Life, the universe, and everything").arg2(42).build(); through a bunch of programmer-defined functions is for some reason better than foo(arg1="Life, the universe, and everything", arg2=42) which the compiler can check.

            There’s actually been a lot of talk about resolving these things – there are at least two RFCs about keyword arguments, I think? and there are several other mechanisms being proposed, but from what I can tell, this is still the best option (as in, the one with the least amount of surprises and pitfalls, so that you don’t end up trying to solve a problem and creating two instead). I wasn’t using Rust at the time when some of these discussions were happening so it’s very likely that I just don’t understand enough about the context because I find this whole situation very baffling.

          2. 3