Skip to content

Named Fn trait parameters#3955

Open
JonathanBrouwer wants to merge 11 commits intorust-lang:masterfrom
JonathanBrouwer:named-fn-trait-arguments
Open

Named Fn trait parameters#3955
JonathanBrouwer wants to merge 11 commits intorust-lang:masterfrom
JonathanBrouwer:named-fn-trait-arguments

Conversation

@JonathanBrouwer
Copy link
Copy Markdown

@JonathanBrouwer JonathanBrouwer commented Apr 24, 2026

View all comments

Allow (optional) named function parameters in parenthesized generic argument lists, such as those of Fn, FnMut, FnOnce, AsyncFn, AsyncFnMut, and AsyncFnOnce.
For example:

fn parse_my_data(
    data: &str,
    log: impl Fn(msg: String, priority: usize)
) { }

Similar to named function pointer parameters, these names don't affect rust's semantics.

Important

Since RFCs involve many conversations at once that can be difficult to follow, please use review comment threads on the text changes instead of direct comments on the RFC.

If you don't have a particular section of the RFC to comment on, you can click on the "Comment on this file" button on the top-right corner of the diff, to the right of the "Viewed" checkbox. This will create a separate thread even if others have commented on the file too.

Rendered

@JonathanBrouwer JonathanBrouwer changed the title Initial named fn trait arguments RFC Named Fn trait parameters Apr 24, 2026
@JonathanBrouwer JonathanBrouwer added the T-lang Relevant to the language team, which will review and decide on the RFC. label Apr 24, 2026
Comment thread text/3955-named-fn-trait-parameters.md Outdated
## Reference-level explanation
[reference-level-explanation]: #reference-level-explanation

The syntax `Fn`, `FnMut` and `FnOnce` traits is currently not documented in the reference.
Copy link
Copy Markdown
Contributor

@clarfonthey clarfonthey Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly just adding this comment here from Zulip to keep track of some of the discussion.

I mentioned that we should probably try to unify the syntax for Fn traits, fn pointers, and functions without bodies. It appears that similarly, the reference is not fully clear on these either.

Particularly, the example you gave that did not compile:

trait Test {
    fn thing((x, y): (usize, usize));
}

seems to directly contradict what the reference says: https://doc.rust-lang.org/stable/reference/items/functions.html

In particular, it appears that only name: Type is supported, not arbitrary patterns.

View changes since the review

Copy link
Copy Markdown
Member

@fmease fmease Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've already briefly discussed this on Zulip but just to get others up to speed: The Reference is correct in stating that the "parameter names" of even bodiless functions are patterns (PatNoTopAlt to be clear).

The grammar rules describe what's syntactically allowed and doesn't dictate what's semantically allowed or forbidden.

Patterns except CommonIdent | "_" in bodiless functions are semantically ill-formed but syntactically allowed.

Copy link
Copy Markdown
Member

@fmease fmease Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To further clarify this (since the Reference doesn't document this nuance which I'll rectify in a moment):

The parameters of function pointer types basically share the same grammar as parameters of trait associated functions in Rust 2015 where the "parameter name" is optional and where the parameter pattern is restricted. As I've mentioned on Zulip it obeys the following rules (excerpt):

RestrictedPat = RestrictedIdent | "&" RestrictedIdent | "&&" RestrictedIdent | "mut" CommonIdent;
RestrictedIdent = CommonIdent | "_" | "false" | "true";

In Rust >2015, however, the grammar of trait associated functions is the same as all other kinds of functions (free, foreign, impl associated).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the past RestrictedPat was semantically accepted as well.
I thought we eliminated it many years ago through a deprecation lint, but apparently the lint only worked at semantic level and wasn't reported at parsing time. I suspect we just didn't have the parse-time linting infra at the time (lint buffering, etc).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm currently working on yeeting that. I've seen the historical RFCs, future-incompat issues and relevant PRs.

Fun fact, the & and the && are the remnants of argument modes (2012/2013-ish era?). Only by happenstance do they get parsed as borrow patterns nowadays.

Back in the day there were 5 argument modes, - (by move), & (by mut ref), && (by ref), + (by copy), ++ (by value). That used to look something like fn f(-a: A, &b: B, &&c: C, +d: D, ++e: E) {}

That's literally the reason why, it's beyond insane. Crater run coming soon btw :)

Comment thread text/3955-named-fn-trait-parameters.md
Comment thread text/3955-named-fn-trait-parameters.md Outdated
## Unresolved questions
[unresolved-questions]: #unresolved-questions

* Should duplicate parameter names be allowed in named fn trait arguments? This is currently allowed for `fn` pointers and other functions without an accompanying `Body`.
Copy link
Copy Markdown
Contributor

@clarfonthey clarfonthey Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like something that, even if we allow, we should lint against. It feels reasonable to not propose as part of the RFC, but I feel like what makes the most sense is to replicate what traits/functions do but allow for a deny-by-default lint in the future.

View changes since the review

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Old issue for this - rust-lang/rust#33995.

) { }
```

### Benefit: Better LSP hints
Copy link
Copy Markdown

@ChayimFriedman2 ChayimFriedman2 Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A major reason for this RFC is better documentation and better LSP hints. As a member of T-rust-analyzer, I unfortunately have to inform you that rust-analyzer cannot support this feature, at least not without significant changes (it does not show hints for named fn pointers parameters, either).

It might be possible, though, to support a few special cases.

View changes since the review

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sucks :c

I still believe this RFC is valuable even without this benefit though, as mentioned the primary benefit is "Better documentation"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea I think the main issue is that even if the type IR encodes this info, we might lose this information after inference depending on how the types are unified, trait bounds are proven etc

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That said, even though R-A won't necessarily be able to provide inlay hints, the hover-rustdoc could show this information, meaning that it technically still could have some usefulness in the LSP implementation, if only indirectly.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea I think the main issue is that even if the type IR encodes this info, we might lose this information after inference depending on how the types are unified, trait bounds are proven etc

when unifying two types, couldn't rust-analyzer try to propagate argument names if one type has them and the other doesn't?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@programmerjake We probably could, but that depends on the solver allowing us to encode this info (which is a rustc crate and perf-sensitive, and also a major change).

Comment thread text/3955-named-fn-trait-parameters.md
Comment thread text/3955-named-fn-trait-parameters.md Outdated
## Reference-level explanation
[reference-level-explanation]: #reference-level-explanation

The syntax `Fn`, `FnMut` and `FnOnce` traits is currently not documented in the reference.
Copy link
Copy Markdown
Member

@fmease fmease Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The syntax Fn, FnMut and FnOnce traits is currently not documented in the reference.

It is documented. See e.g., https://doc.rust-lang.org/nightly/reference/paths.html#grammar-TypePathFnInputs. More specifically TypePathFn.

View changes since the review

Copy link
Copy Markdown
Author

@JonathanBrouwer JonathanBrouwer Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh that is where it is, I will change the reference level explanation in terms of those rules

Copy link
Copy Markdown
Member

@fmease fmease Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addendum: Type paths are not the only paths featuring parenthesized generic argument lists. Expression and pattern paths have the same behavior. For example the following is legal:

#[cfg(false)]
fn scope() {
    let F::() -> () = F::() -> ()::Y;
}

Copy link
Copy Markdown
Author

@JonathanBrouwer JonathanBrouwer Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've rewritten the reference-level explanation in terms of TypePathFn. I will process your addendums later

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final addendum: This syntax is obviously not tied to Fn, FnMut and FnOnce; the path can be anything. Moreover, from a semantic standpoint, not only are Fn{,Mut,Once} legal but also AsyncFn, AsyncFnMut and AsyncFnOnce.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type paths are not the only paths featuring parenthesized generic argument lists. Expression and pattern paths have the same behavior. For example the following is legal:

Is this behaviour documented in the reference anywhere?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This syntax is obviously not tied to Fn, FnMut and FnOnce; the path can be anything. Moreover, from a semantic standpoint, not only are Fn{,Mut,Once} legal but also AsyncFn, AsyncFnMut and AsyncFnOnce.

I've made this more explicit in the RFC

Comment thread text/3955-named-fn-trait-parameters.md Outdated
Comment thread text/3955-named-fn-trait-parameters.md Outdated
@fmease fmease added the A-syntax Syntax related proposals & ideas label Apr 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-syntax Syntax related proposals & ideas T-lang Relevant to the language team, which will review and decide on the RFC.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants