Skip to content

Commit f635361

Browse files
authored
Merge pull request #3955 from JonathanBrouwer/named-fn-trait-arguments
Named `Fn` trait parameters
2 parents 037f711 + 0b9dce9 commit f635361

1 file changed

Lines changed: 205 additions & 0 deletions

File tree

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
- Feature Name: `named_fn_trait_parameters`
2+
- Start Date: 2026-04-24
3+
- RFC PR: [rust-lang/rfcs#3955](https://github.com/rust-lang/rfcs/pull/3955)
4+
- Rust Issue: [rust-lang/rust#158499](https://github.com/rust-lang/rust/issues/158499)
5+
6+
## Summary
7+
[summary]: #summary
8+
9+
Allow (optional) named function parameters in parenthesized generic argument lists, such as those of `Fn`, `FnMut`, `FnOnce`, `AsyncFn`, `AsyncFnMut`, and `AsyncFnOnce`.
10+
For example:
11+
```rust
12+
fn parse_my_data(
13+
data: &str,
14+
log: impl Fn(msg: String, priority: usize)
15+
) { }
16+
```
17+
Similar to named function pointer parameters, these names don't affect rust's semantics.
18+
19+
## Motivation
20+
[motivation]: #motivation
21+
22+
### Benefit: Better documentation
23+
24+
This allows users to better document the meaning of parameters in signatures. This is the primary benefit of this RFC.
25+
26+
For example, it is not immediately clear what the `String` and `usize` refer to in the type of `log`, providing names like in the example above is much clearer.
27+
28+
```rust
29+
fn parse_my_data(
30+
data: &str,
31+
log: impl Fn(String, usize)
32+
) { }
33+
```
34+
35+
The parameter names should also show up on rustdoc.
36+
37+
### Benefit: Better LSP hints
38+
39+
When calling `log` in the body of `parse_my_data`, the LSP can provide the function parameter names as "inlay parameter name hints":
40+
log(`data: `"Message".to_string(), `priority: `1);
41+
42+
This is a concrete advantage of this approach over using comments to do the same thing, such as in:
43+
```rust
44+
fn parse_my_data(
45+
data: &str,
46+
log: impl Fn(/* msg */ String, /* priority */ usize)
47+
) { }
48+
```
49+
50+
### Benefit: Better consistency with `fn` pointers
51+
52+
Imagine if `parse_my_data` looked like this:
53+
```rust
54+
fn parse_my_data(
55+
data: &str,
56+
log: fn(msg: String, priority: usize)
57+
) { }
58+
```
59+
60+
If due to new requirements the user decides that `impl Fn` suits the usecase better, having to remove the parameter names is unintuitive.
61+
This RFC removes this problem.
62+
63+
## Guide-level explanation
64+
[guide-level-explanation]: #guide-level-explanation
65+
66+
You can give names to parameters to the `Fn` trait and its friends to better document the meaning of these parameters, to help people who call your function.
67+
These names are optional and don't have any semantic meaning. Named and unnamed parameters can be mixed, for example:
68+
69+
```rust
70+
fn parse_my_data(
71+
data: &str,
72+
log: impl Fn(String, priority: usize)
73+
) { }
74+
```
75+
76+
This same syntax also applies to trait bounds, for example:
77+
```rust
78+
fn parse_my_data<
79+
L: Fn(msg: String, priority: usize)
80+
>(
81+
data: &str,
82+
log: L
83+
) { }
84+
```
85+
86+
## Reference-level explanation
87+
[reference-level-explanation]: #reference-level-explanation
88+
89+
Before this RFC, the syntax rules of parenthesized generic argument lists are:
90+
```grammar,types
91+
GenericArgs ->
92+
`<` GenericArgList? `>`
93+
| `(` TypeList? `)` ( `->` TypeNoBounds )?
94+
95+
TypeList ->
96+
`(` Type `,` `)`* Type `,`?
97+
```
98+
99+
After this RFC, these rules will be replaced by:
100+
101+
```grammar,types
102+
GenericArgs ->
103+
`<` GenericArgList? `>`
104+
| `(` MaybeNamedFunctionParameters? `)` ( `->` TypeNoBounds )?
105+
106+
MaybeNamedFunctionParameters →
107+
`(` MaybeNamedParam `,` `)`* MaybeNamedParam `,`?
108+
109+
MaybeNamedParam →
110+
OuterAttribute* (RestrictedPat `:`)? Type
111+
```
112+
113+
Below are two chapters on some design tradeoffs made here.
114+
115+
### Attributes are allowed on parenthesized generic argument lists
116+
Attributes are allowed on parameters in parenthesized generic argument lists:
117+
```rust
118+
fn test(x: impl Fn(#[cfg(...)] msg: String, priority: usize), y: usize) { }
119+
```
120+
Note that attributes are already allowed on `fn` pointers:
121+
```rust
122+
fn test(x: fn(#[cfg(...)] msg: String, priority: usize), y: usize) { }
123+
```
124+
125+
### Restricted patterns are syntactically but not semantically allowed in parenthesized generic argument lists
126+
This syntax is consistent with that of function pointers.
127+
Semantically, the names of function parameters are limited to ``IDENTIFIER | `_` ``.
128+
Below is a comparison with two other language features:
129+
130+
#### Parameters of `fn` pointers and `Fn` trait
131+
Like on `fn` pointers, a `RestrictedPat` is syntactically allowed.
132+
Therefore, the following program compiles:
133+
```rust
134+
#[cfg(false)]
135+
type F = fn(mut x: (), &x: (), &&x: (), false: (), &_: (), &true: ());
136+
```
137+
```
138+
RestrictedPat ->
139+
`mut`? IDENTIFIER
140+
| ( `&` | `&&` )? ( `_` | `false` | `true` | IDENTIFIER )
141+
```
142+
143+
#### trait functions without bodies
144+
For comparison, trait functions without bodies are more permissive than this. Arbitrary patterns are allowed (and then semantically still anything other than identifiers is rejected).
145+
Therefore, the following program compiles:
146+
```rust
147+
#[cfg(false)]
148+
trait Test {
149+
fn x((x, y): usize);
150+
}
151+
```
152+
153+
## Drawbacks
154+
[drawbacks]: #drawbacks
155+
156+
* This makes the syntax of `impl Fn` and friends slightly more complicated
157+
* This keeps the syntax of `impl Fn` and friends inconsistent with that of functions in trait definitions, for the reasoning about this see [reference level explanation](#reference-level-explanation).
158+
159+
## Rationale and alternatives
160+
[rationale-and-alternatives]: #rationale-and-alternatives
161+
162+
* This needs to be implemented in the language, it cannot be provided by a macro or library as it affects syntactic sugar of the language itself.
163+
* This makes Rust code easier to read, as it adds better ways to document function signatures.
164+
165+
## Prior art
166+
[prior-art]: #prior-art
167+
168+
In Rust, this is already allowed in `fn` pointers:
169+
```rust
170+
type LogFunction = fn(msg: String, priority: usize);
171+
```
172+
173+
In TypeScript:
174+
```ts
175+
type LogFunction = (msg: string, priority: number) => void;
176+
```
177+
178+
In Kotlin:
179+
```kotlin
180+
fun log(data: String, logFunction: (msg: String, priority: Int) -> Unit) { }
181+
```
182+
183+
## Unresolved questions
184+
[unresolved-questions]: #unresolved-questions
185+
186+
* 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`.
187+
```rust
188+
type T = fn(x: usize, x: usize);
189+
```
190+
```rust
191+
trait Test {
192+
fn thing(x: usize, x: usize);
193+
}
194+
```
195+
196+
## Future possibilities
197+
[future-possibilities]: #future-possibilities
198+
199+
* We should figure out how this feature interacts with the "named arguments" feature.
200+
One proposal is to mirror whatever solution we come up with for function pointers.
201+
For example, if named arguments used the syntax `fn f(pub a: T, pub b: U) -> R`, the function trait should be `Fn(pub a: T, pub b: U) -> R`.
202+
* Similarly, we should figure out how this feature interacts with the "defaulted arguments" feature.
203+
Again, this should mirror function pointers.
204+
For example, if default arguments used the syntax `fn(x: String, y: i32 = 0)`, the function trait should be `Fn(x: String, y: i32 = 0)`.
205+

0 commit comments

Comments
 (0)