-
Notifications
You must be signed in to change notification settings - Fork 84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
generate a (customizable) constructor #69
Comments
I like the I think starting out simple can be desirable, not possible to rename the constructor and not possible to reorder fields. Are you thinking that if one ask to get a constructor generated for them, it automatically takes all required fields as arguments, or would the default be to just work like how |
Ok, I also like Yes, starting simple is a valid option. But I don't think I would ever support reordering fields - just wanted to make that clear upfront. Yes IMO the constructor should always take all required fields (those that don't have a default). This behaviour would be completely independent of fallible/infallible choice. This has the advantage that method signatures are somewhat more consistent across different derived builders. The new function would always tell you the required fields. If you then opt into infallible build methods and you do have required fields, then the Does that make sense? :-) |
I agree with never supporting field reorders. Then one can just reorder the fields of their struct instead to get that result. I completely agree with the constructor always taking arguments, no matter infallible or not. Better not create too many possible variants of builders. I like that the constructor always takes the values of the required fields. I have to say I think it's kind of odd that builders with required fields do implement |
Hm, I don't find it odd. :-) Because the So maybe it just seems odd, because we only have the fallible behaviour currently? As soon as you opt into the infallible behaviour, you would basically tell derive builder that you'll always know everything up front and the The updated documentation would definitely use Another reason to keep the |
@faern have a look at #61 and #62 - the |
To avoid any confusion, I think there are two separate discussions about |
How much code does this save the crate user, vs. adding their own |
Documentation: As long as the fields of the target struct are documented, we could collect that documentation and stitch it together to get a generic documentation for the constructor. That feels ok to me. :-) Reordering: I'm worried about that, too. Here are some random ideas:
#[cfg(not(test))]
// For `cargo build` we emit the method as normal.
pub fn new() {}
#[cfg(test)]
#[cfg_attr(test, forbid(unused))]
// If `cargo test` is run, we make add `#[forbid(unused)]`.
// This forces the user to write something like a unit test,
// thereby locking down the signature. Note that
// `#[forbid(unused)]` does not work with exported items.
// To prevent that, we can make the method private.
fn new() {} This approach however is not perfect, because we need to make the function private for
Note: The important value of the constructor with required fields is, that it would enable us to have a non-panicking infallible build method. |
@TedDriggs For me this adds much more value than just adding a constructor (that a user can do manually with the existing version). I'm looking forward to infallible builds so I can get rid of the I don't see reordering as that much of a problem actually. In the same way that I have to be careful to not reorder arguments in my public API I should be careful to not rearrange structs if I know they will have code generated for them. But I agree it adds one level of indirection that is easy to forget. I just really don't like the thought of having to maintain two lists of my required fields, first the fields themselves and then the order in which they come again. |
If the author has to specify all the arguments and their positions anyway, then all we seem to have saved them is some generic type declarations (if they want to use
I still don't think it's good for the exporting crate to make the decision that the builder must be infallible; the author can't know how the consuming crate will populate the object - it could have all the values hardcoded and is using the builder for convenient conversions, or it could be parsing user input into the builder and have no idea whether or not it's going to succeed. Alternative using Session TypesIf Session types could also be useful for converting a fallible builder to an infallible one, verifying that all required fields were present and performing a "pre-build". |
I'm not sure if I understood everything correctly, but it sounds at least comparable to this idea of typesafe builders #33. I.e. encode the state of the builder with the typesystem and use generic bounds to tell the compiler, whether the build method can be called safely. That's actually a feature I am very much looking forward to. It might very well blow up compile time, but it would be a really cool showcase for derive_builder. :-) If we want to dig deeper into that direction, let's discuss it in #33 instead to keep the discussions organized (..it's already all so intertwined ^^). |
I would like to ask and maybe extent the original idea with my use case. I tried to find a way to disable I need a constructor for the builder (and I have no issue writing it myself), to force user to enter the required fields, and all other fields are optional, but the builder can be constructed without those fields, which is kind of confusing, and even more, if I disallow setting them. |
@xNxExOx having a way to disable emitting |
See discussion on #56 for this; custom constructors (and the resulting infallible build methods) are going to stay out of scope for this crate, but they can be incrementally wrapped around a generated builder to present the desired API to users of that builder. |
Currently all builder structs implement the
Default
trait. Using theDefault
trait is the proposed way to create a new builder instance right now.As mentioned in this comment by @faern it would be nice to have a dedicated constructor. We could make the identifier customizable (defaulting to
new
) and we could offer opting out of generating a constructor.Possible API
#[builder(constructor(name="brand_new", ...))]
#[builder(new(name="foo", private, ...))]
Questions:
something else)?
Outlook
This could allow better support for infallible build methods #56, if the build method would already take all required values (
new(a, b)
instead ofnew().a(a).b(b)
).Limitations:
The text was updated successfully, but these errors were encountered: