Skip to content

Generate const fn accessors with const_fn - #125

Merged
jbr merged 1 commit into
jbr:mainfrom
gerasim13:const-fn
Sep 21, 2026
Merged

jbr merged 1 commit into
jbr:mainfrom
gerasim13:const-fn

Conversation

@gerasim13

Copy link
Copy Markdown
Contributor

What

Adds a const_fn setting that makes fieldwork generate const fn accessors.

#[derive(fieldwork::Fieldwork)]
#[fieldwork(get, get_mut, const_fn)]
struct Counters {
    /// how many times this has happened
    count: usize,
}

const fn read(c: &Counters) -> usize {
    c.count()
}

It is an ordinary cascading boolean, so it resolves through the usual four
levels — field-method, field, item-method, item — and can be turned on for a
whole type and off for one field, or the other way round:

#[fieldwork(get)]
struct Mixed {
    /// borrowed by an ordinary getter
    name: String,
    /// read in a const context
    #[field(const_fn)]
    port: u16,
}

Why

An accessor that copies a Copy field out of &self, or returns &mut to
it, is already inside the const subset — but the generated method is a plain
fn, so a type whose fields are private cannot be read from a const item
at all through its own API. Today that means keeping hand-written const
accessors next to the derived ones, which is exactly the duplication the
derive exists to remove.

Notes

  • Opt-in, defaults to false: no existing generated code changes.
  • Spelled const_fn, not const, because const is a reserved word and
    does not parse as a path inside the attribute's expression list.
  • Whether the body is const is left to the compiler rather than being
    pre-validated by the macro: deref calls Deref::deref, into calls
    Into::into, and a setter drops the previous value, so those combinations
    produce a normal const-eval error pointing at the generated method.
    docs/const_fn.md spells this out.

Tests

  • New expansion snapshot tests/expand/expand_29_const_fn.rs covering the
    item-level and field-level forms over get and get_mut.
  • tests/ui/invalid_config.stderr updated: const_fn now appears in the
    recognized-key list.
  • New docs/const_fn.md, included under #[cfg(doc)] like the other topic
    pages; cargo run -p docs-gen -- --verify is clean.

cargo test --all passes. cargo clippy --all-targets -- -Dwarnings reports
the same 13 findings before and after this change on my toolchain (all in
files this PR does not touch), so I left them alone.

Accessors that only move a `Copy` field out of `&self`, or hand back a
`&mut` to it, are valid in a const context, but fieldwork always emitted a
plain `fn`. A type whose getters are the only way to read its fields could
therefore not be read from a `const` item at all, and hand-written const
accessors had to be kept alongside the derived ones.

`const_fn` is an ordinary cascading boolean setting, so it resolves the same
way as `copy` or `chain`: field-method, field, item-method, item. It is
opt-in and defaults to false, so no existing generated code changes.

    #[derive(fieldwork::Fieldwork)]
    #[fieldwork(get, const_fn)]
    struct Counters {
        count: usize,
    }

The key is spelled `const_fn` rather than `const` because `const` is a
reserved word and does not parse as a path in the attribute's expression
list.

Whether the body is actually const is left to the compiler: `deref` and
`into` call trait methods, and a setter drops the old value, so those
combinations report a normal const-eval error at the generated method.
@jbr

jbr commented Sep 21, 2026

Copy link
Copy Markdown
Owner

Looks good, thank you!

@jbr
jbr merged commit fc2eb27 into jbr:main Sep 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants