-
Notifications
You must be signed in to change notification settings - Fork 34
Description
Hi @jbaublitz, thanks for this fantastic crate! I have a few ideas for improving the Getter/MutGetter macros and would love to hear your thoughts.
Motivation
When a struct field is an Option<T> or Result<T, E>, the generated getter currently returns &Option<T> or &Result<T, E>. In many cases, it’s more ergonomic to return Option<&T> or Result<&T, &E> instead:
- Calling
.as_ref()(or.as_mut()) lets users work directly with borrowed inner data without cloning the container. - Clippy recommends preferring
Option<&T>over&Option<T>for performance and readability (clippy::ref_option).
Proposal
Add two optional parameters to the attribute syntax:
get = "as_ref"get_mut = "as_mut"
When applied, the macro expansion will call the corresponding method:
#[derive(Getters, MutGetters)]
struct MyStruct<T> {
#[getset(get = "as_ref", get_mut = "as_mut")]
inner: Option<T>,
}generates:
impl<T> MyStruct<T> {
#[inline(always)]
fn inner(&self) -> Option<&T> {
self.inner.as_ref()
}
#[inline(always)]
fn inner_mut(&mut self) -> Option<&mut T> {
self.inner.as_mut()
}
}All other fields without these options continue to generate &Option<T>/&Result<T, E> as before, ensuring full backward compatibility.
Implementation
I’ve drafted a proof-of-concept PR that:
- Parses
get = "as_ref"andget_mut = "as_mut"in addition to existing options. - Injects
.as_ref()or.as_mut()into the getter body when specified. - Leaves the default behavior untouched if the attributes are omitted.
Feel free to review it here: [#120].
Backward Compatibility
- By making
as_ref/as_mutopt-in, existing derives remain unchanged. - No breaking changes to public APIs unless users explicitly opt in.
Additional Thoughts
-
Generalizing to all
AsReftypes:
It would be nice to auto-apply.as_ref()on any field whose type implementsAsRef, but detecting arbitrary trait implementations in a procedural macro is nontrivial without compiler context. -
Community interest:
Similar requests have appeared in issues [Provide ability to create getters that returnA<&T>instead of&A<T>#78], [Fix problems with global attributes and implements getter/setter for Option<T> #83], and [feat: addinto,optional,as_ref,as_mutandconstoptions #117], but were held back by breaking-change concerns. Because this proposal is fully opt-in and backward-compatible, I believe it addresses past objections and could gain community support. -
Attribute parsing robustness:
I noticed that multiple#[getset(...)]attributes on the same item only respect the last one (due to usingfilter_map(...).next_back()in parsing). For example:#[derive(Getters)] #[getset(get)] #[getset(get = "pub")] #[getset(get)] struct MyStruct { a: isize, }
only the final
#[getset(get)]takes effect. We might consider using a helper like darling to simplify and harden attribute parsing; I’m happy to help integrate it.
Thank you for considering these enhancements—I look forward to your feedback!