Skip to content

Proposal: Add opt-in as_ref/as_mut getter options for Option and Result fields #119

@ry-sun

Description

@ry-sun

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:

  1. Calling .as_ref() (or .as_mut()) lets users work directly with borrowed inner data without cloning the container.
  2. 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" and get_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_mut opt-in, existing derives remain unchanged.
  • No breaking changes to public APIs unless users explicitly opt in.

Additional Thoughts

  1. Generalizing to all AsRef types:
    It would be nice to auto-apply .as_ref() on any field whose type implements AsRef, but detecting arbitrary trait implementations in a procedural macro is nontrivial without compiler context.

  2. Community interest:
    Similar requests have appeared in issues [Provide ability to create getters that return A<&T> instead of &A<T> #78], [Fix problems with global attributes and implements getter/setter for Option<T> #83], and [feat: add into, optional, as_ref, as_mut and const options #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.

  3. Attribute parsing robustness:
    I noticed that multiple #[getset(...)] attributes on the same item only respect the last one (due to using filter_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!

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions