|
| 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