-
-
Notifications
You must be signed in to change notification settings - Fork 19
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
Allow specifying mandatory Option
argument
#35
Comments
Hi! Thank you for creating the issue! I've been thinking of adding smth like Example: struct Foo {
#[builder(must_use)]
optional: Option<u32>,
#[builder(default = 42, must_use)]
has_default: u32,
}
let foo = Foo::builder()
.maybe_optional(None) // not calling this or `.optional(value)` will be a compile error
.maybe_has_default(None) // not calling this or `.has_default(value)` will be a compile error
.build();
assert_eq!(foo.optional, None);
assert_eq!(foo.has_default, 42); Note that the workaround you suggested (hiding |
I re-read the issue again. I suppose in this use case This would be a bit different from what I suggested higher. In this case another attribute like |
Yes, exactly. Thank you for looking into this! The suggested |
Here are some more thoughts about this. The proposed Maybe it's fine to use a type alias in this case to explicitly state that Also another interesting workaround for this that I found is that you can prefix Turns out I unintentionally coded For example, this works as you'd like: #[bon::builder]
fn foo(val: r#Option<i32>) {}
// `val` is a mandatory field that has no default and must be set
// No special `Option` handling was applied by `bon`
foo().val(Some(32)).call(); Maybe this behavior of |
That's a neat little workaround – could roll with it. However, I must say that if I was reading code that used Maybe a That said, both the typealias and |
The term
There should be some way to configure both of these independently, and I just can't find good names and syntax for that. Maybe |
On the second thought struct Example {
#[builder(required)]
arg1: Option<String>,
// A bit confusing, but it means the setter is required to call, although a default
// value may be requested (also explicitly). That default value may be changed
// by the implementor and the caller won't be broken if it changes
#[builder(required, default = "foo")]
arg2: String,
#[builder(required, default = Some("bar".to_owned()))]
arg3: Option<String>
}
let example = Example::builder()
// accepts an `Option<String>`. There is no `maybe_` method.
.arg1(Some("value".to_owned()))
// For this member a `maybe_` method was generated (because of `#[builder(default)]`).
// However calling the setter is still required (because of `#[builder(required)]`).
// If you want to set the default value, pass `None` explicitly.
// Not calling any setter for `arg2` won't compile
.maybe_arg2(None::<String>)
// This works the same as with `arg3`, although there is `Option<Option<String>>`
// accepted here. If `None` is passed the default value of `Some("bar".to_owned())`
// is used. The method `.arg3()` accept a single-level `Option<String>` though
.maybe_arg3(None)
.build();
assert_eq!(example.arg1, "value");
assert_eq!(example.arg2, "foo");
assert_eq!(example.arg2.as_deref(), Some("bar")); Additionally, the macro may generate |
I'm liking how the My main use-case for bon is for enforcing immutability in certain structs (where all fields are private with builder+getter), rather for partially filling struct fields. So something that would make it easy to make all fields mandatory during construction would be great! |
Hi @musjj, I've already revisited this issue and design a bit on the background while working on #145. To me, the feature of having exhaustiveness in the builder (where you need to fill all fields, even ones that have default values) looks quite complicated to even express and explain for the users. However, I'm not entirely giving up on this idea, and I'd be glad to have it if user feedback proves me wrong on this. The main problem for me has been figuring out the proper naming for the different behaviors that I'd like to be configurable.
For the first behavior I'm thinking to return to the Then I'd like to reserve something like So it would look like this: #[derive(bon::Builder)]
struct Example {
field1: Option<u32>,
#[builder(transparent)]
field2: Option<u32>,
#[builder(explicit)]
field3: Option<u32>
}
Example::builder()
// `field1` has two setters generated, and it includes a `maybe_{}` setter. We could also omit
// calling this setter.
.maybe_field1(None)
// `field2` has just one setter generated that accepts `Option<T>` and it's required to call
.field2(None)
// It works the same as `field1`, but with a difference that if you don't call any setter for
// the `field3` the code won't compile
.maybe_field3(None)
.build() I've already added support for WDYT about this design @musjj?
Is your use case for a Note that struct Example {
#[builder(explicit, default = 42)]
field1: u32
}
Example::builder()
// We can't omit calling a setter for `field1`. This requires the caller to
// explicitly state "yes, I acknowledge there is a `field1`" and I want to
// use a default value for it
.maybe_field1(None)
.build() Right now I'm not adding With this complex behaviors having |
For my personal use-case, I'm heavily preferring the Btw, any chance of allowing the attribute to be used on the top level? So something like: #[derive(bon::Builder)]
#[builder(transparent)] // All fields have to be explicitly constructed
struct Example {
field1: Option<u32>,
field2: Option<u32>,
field3: Option<u32>
} It would make it harder to forget to mark a field. |
I see, thank you for the feedback! I'll just postpone the Regarding the top-level config, it does make sense! I can make an extension of the #[builder(on(_, transparent))] With this you can also select members of specific types. For example, say you want to apply transparency only for members of type #[builder(on(u32, transparent))] Note that I plan to extend the syntax of the selector part of the With the bare |
Hello, I found myself in need of this feature with no workarounds, though I only really require the I use protobuf for some between-service communication, and, at the moment I use prost with Now proto3 file: message Foo {
// multiple fields
// ...
required Bar bar = 10;
} in rust would be [derive(Clone, Debug, PartialEq, Message, bon::Builder)]
#[non_exhaustive]
pub struct Foo {
// the fields added here
// ..
bar: Option<Bar>, // notice this is not mandatory in bon, but it would be if I could use #[builder(explicit)]
} I hope this counts as a valid use-case since this would greatly help me, thanks! |
I see the problem, thank you for describing it. I have a followup question. Are these types annotated with #[derive(bon::Builder)]
pub struct Example {
#[builder(transparent, with = |value: Bar| Some(value))]
required: Option<Bar>,
}
Example::builder()
.required(Bar {}) // The setter now accepts `Bar` directly and wraps with `Some` internally
.build() The attributes [dependencies]
bon = { git = "https://github.com/elastio/bon", branch = "master" } Alternatively, this could be solved with a (not implemented yet) Instead, if you just want to make sure that developers fill all the fields of the protobuf message explicitly, I guess using a builder doesn't really buy you anything. Adding |
I would only need this for some fields, not all, otherwise it would defeat the purpose of i guess the transparent feature would be great when I would like to force consumers to provide a value, but there are cases (and this is more common) where I also find it weird that the Later Edit: it seems I forgot to answer the first question, sorry. They are generated using a custom |
This is why I think So I'd prefer if
Does it mean you would use field_attribute config of prost-build to specify which fields are required? That means you'd need to maintain that config separately from the A small ergonomic problem of I could make a special case of Do you think this would work then for you and not require a #[derive(bon::Builder)]
pub struct Example {
#[builder(transparent, with = Some)]
required: Option<Bar>,
} |
Thanks for your time, I misunderstood what transparent was doing.
|
Small update, I decided to do a last-minute change before the release of this feature as part of 3.0. I'll use the term I believe my idea of |
|
Awesome library! Had a lot of fun using it so far.
I have an edge case where one of my input argument is an
Option<bool>
since I want to encode the states yes/no/maybe viaSome(true)
/Some(false)
/None
. Currently,bon::builder
would interpret that argument as optional, even though I want it to be mandatory.#22 could be related in a sense that it would allow me to work around this issue.
For other people looking for a workaround: create a typealias
type ... = Option<T>
, such thatbon
is not aware of the top-levelOption
type.A note for the community from the maintainers
Please vote on this issue by adding a 👍 reaction to help the maintainers with prioritizing it. You may add a comment describing your real use case related to this issue for us to better understand the problem domain.
The text was updated successfully, but these errors were encountered: