-
-
Notifications
You must be signed in to change notification settings - Fork 22
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
Support getters for already set members #221
Comments
Hi, thank you for creating the issue! Accessing the private fields of the builder (except for But.. I was actually planning to add an ability to generate getters for already set members as the next feature after In future increments, I plan to provide the ability to generate getters for mutable references, the ability to do |
Fantastic to hear! Thank you. I am fairly familiar with |
Yeah, I've been procrastinating on this a bit given the Stalker 2 release 😳. I haven't started with this feature yet, so feel free to take a stab at this 👍 |
Haha, have fun. Feel free to assign me this. Will get started now. |
At a high level, how do you feel about having the getters return an |
Honestly, I have mixed feelings about automatic magic conversions. If you suggest the getter to return literally In general, even if we use Here are some ideas I have for this API here in general to be more specific: #[builder(
// For optional members automatically does `Option::as_ref` (reference
// is moved under the Option).
//
// &T | Option<&T>
getter,
// For optional members does no conversion, because `&mut Option<_>` makes
// sense if the caller wants to reset the `Option` to `None`.
//
// &mut T | &mut Option<T>
getter(mut),
// Deref is only supported for readonly getters. One probably doesn't
// need deref for mutable getters, because returning e.g. `&mut String`
// or `&mut Vec<T>` makes perfect sense.
//
// The argument to `deref` specifies the return type of the getter
// (i.e. the target of the deref coercion). However, it can be omitted
// (i.e. just `getter(deref)`) for some well-known types such as:
// - String
// - Vec<T>
// - Box<T>
// - Rc<T>
// - Arc<T>
// - Cow<T>
// - PathBuf
// - OsString
// - CString
// - Utf8PathBuf
// - ...
// Just look up the `Deref` impls in `std` here: https://doc.rust-lang.org/stable/std/ops/trait.Deref.html,
// and in `camino` crate, we may extend this list in the future.
//
// &str | Option<&str>
getter(deref(&str)),
// Useful for primitive types (e.g. `u32`)
// Getter by `Copy` -> T | Option<T>
getter(copy),
// Getter by `Clone` -> T | Option<T>
getter(clone),
// Some other standard additional attributes
getter(
name = getter_name,
vis = "",
docs {
/// Docs for getter_name
}
)
// Multiple getters need to have name specified explicitly
getter(name = getter_name_1),
getter(name = getter_name_2, clone),
// Custom readonly getter. Accepts a readonly reference and transforms it.
// Only `&_` type is accepted (_ is replaced with member's type)
getter = |value: &_| -> Ty { expr }
// Custom mutable getter. Accepts a mutable reference and transforms it.
// Only `&mut _` type is accepted (_ is replaced with member's type)
getter = |value: &mut _| -> Ty { expr }
// Give a name to the getter if there are several getters
getter(name = foo, with = |value: &mut _| -> Ty { expr }),
)] So users would manually need to write Feel free to implement just part of this API that would be good enough for the start. You may also take a look at the |
Just one more remark. I expect implementing |
Got it. How do you feel about this then? I'll finish up the PR and polish it and get it to support the following:
That could probably go in as an experimental / unstable API like |
As another aside (this will likely be pretty complex to implement correctly too and would break the API), it would be very cool if we could add another state like |
Makes sense to me.
By "that" do you mean the
Yeah, I had such thing in mind as well even before releasing v3 where the new typestate design landed. However, I wouldn't like to have that state generated by default, because that would complicate things and also probably increase compile times. I was thinking of adding something like that in the future, and make that enabled with some explicit attribute. |
Sweet, just put up the PR: #222 This supports those mentioned configurations. |
I have some more ideas. Receiver SupportWe should also probably support generating a getter for the receiver ( bon/bon-macros/src/builder/builder_gen/input_fn.rs Lines 148 to 153 in 00d9a3e
.. and I guess this is the first precedent when we need to support The question for this use case is how such getter should be named? Should it be |
Tbh in this case, I would probably just do But at the same time, I don't actually think |
Another interesting design, I'm wondering how we should support |
I don't think explicit name is required for |
Hi, Bon is my favorite crate and I'm using it as the backbone for a complex pipeline internal framework.
In essence, my pipeline works like this:
Each step of the pipeline is (simplified) implemented like this:
The types of the pipeline enforce that the state produced by a prior pass must be fed into the pass under it, so you can't accidentally order anything wrong. I love using Bon to back this pipeline system and allow me to make each pass specify what the conditions of its state should be.
Where I am struggling is that sometimes I run into a weird, limiting situation.
I have one step in my pipeline that scrapes some information off of a user in my DB, processes it, then sets it in the builder (because this value must go in the row that's being built). Then, in another pass of the pipeline, I must read processed data out and use it to compute another value that also goes on my row. Given the current implementation of Bon (while following the rules and not directly implementing unsafe methods on generated code), I have three solutions:
IsSet
because this cache has to be completely detached frombon
builder(field)
on these fields I want to cache. This also works, but it has the same problem as before where I lose the ability to do guards on the passes which is what I love so much about bon.I noticed in 3.1.1 you removed the non-determinism of generated privates. After seeing this, being able to add methods that take in
&self
and return a reference to a value in the builder state would be incredible and make my pipeline work exactly how I want it to. This long winded context finally leads to my question (the one in the title)...How fundamentally unsafe is this? Taking a look at the expanded macro from
builder
everything I can see indicates that adding read only accessor methods that do not move out of the tuple or clone the data would be completely safe, but I figured it would be well worth opening an issue for this because this will be running in production code where a mistake like this will not be acceptable.Thank you!
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: