|
| 1 | +- Feature Name: `return_type_attrs` |
| 2 | +- Start Date: 2021-11-25 |
| 3 | +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) |
| 4 | +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) |
| 5 | + |
| 6 | +# Summary |
| 7 | +[summary]: #summary |
| 8 | + |
| 9 | +Allow attributes in function return type position. For example: |
| 10 | + |
| 11 | +```rust |
| 12 | +fn sum(x: u8, y: u8) -> #[some_attr] u8 { |
| 13 | + x + y |
| 14 | +} |
| 15 | +``` |
| 16 | + |
| 17 | +# Motivation |
| 18 | +[motivation]: #motivation |
| 19 | + |
| 20 | +Currently the whole function can be annotated with a macro attribute: |
| 21 | + |
| 22 | +```rust |
| 23 | +#[some_attr] |
| 24 | +fn example() { .. } |
| 25 | +``` |
| 26 | + |
| 27 | +As well as individual function parameters (introduced in [RFC2565]): |
| 28 | + |
| 29 | +[RFC2565]: 2565-formal-function-parameter-attributes.md |
| 30 | + |
| 31 | +```rust |
| 32 | +fn example(#[attr] input: String, #[attr] x: u8) { .. } |
| 33 | +``` |
| 34 | + |
| 35 | +However function return types currently cannot be annotated, which forces DSLs |
| 36 | +to resort to function attributes, for example: |
| 37 | + |
| 38 | +```rust |
| 39 | +#[return_format = "json"] |
| 40 | +fn example(#[param = "q"] query: String, #[page = "p"] page: u64) -> Vec<Page> { |
| 41 | + .. |
| 42 | +} |
| 43 | +``` |
| 44 | + |
| 45 | +Especially for functions with many parameters this can result in the annotation |
| 46 | +that is meant for the return type to be several lines apart from the return |
| 47 | +type that it actually describes: |
| 48 | + |
| 49 | +```rs |
| 50 | +#[return_format = "json"] |
| 51 | +fn example( |
| 52 | + #[param = "q"] query: String, |
| 53 | + #[param = "p"] page: u64, |
| 54 | + #[param = "c"] count: u64, |
| 55 | + #[param = "o"] order_by: Column, |
| 56 | +) -> Vec<Page> { |
| 57 | + .. |
| 58 | +} |
| 59 | +``` |
| 60 | + |
| 61 | +For such cases return type attributes could provide more clarity: |
| 62 | + |
| 63 | +```rs |
| 64 | +fn example( |
| 65 | + #[param = "q"] query: String, |
| 66 | + #[param = "p"] page: u64, |
| 67 | + #[param = "c"] count: u64, |
| 68 | + #[param = "o"] order_by: Column, |
| 69 | +) -> #[json] Vec<Page> { |
| 70 | + .. |
| 71 | +} |
| 72 | +``` |
| 73 | + |
| 74 | +Since function parameters already can be annotated this can be regarded as the |
| 75 | +next logical step towards more expressive and intuitive DSLs. The motivation |
| 76 | +for the introduction of parameter attributes outlined in [RFC2565] largely |
| 77 | +applies to return type attributes as well, since they would also be useful for |
| 78 | +property based testing, interoperability with other languages and optimization |
| 79 | +annotations. |
| 80 | + |
| 81 | +# Guide-level explanation |
| 82 | +[guide-level-explanation]: #guide-level-explanation |
| 83 | + |
| 84 | +Return types of `fn` definitions may have attributes attached to them. Thereby, |
| 85 | +additional information may be provided. |
| 86 | + |
| 87 | +For the purposes of illustration, let's assume we have the attribute |
| 88 | +`#[apple]` available to us. |
| 89 | + |
| 90 | +## Basic examples |
| 91 | + |
| 92 | +The syntax for attaching attributes to return types is shown in the snippet below: |
| 93 | + |
| 94 | +```rust |
| 95 | +// Free functions: |
| 96 | +fn foo() -> #[apple] u32 { .. } |
| 97 | + |
| 98 | +impl Alpha { // In inherent implementations. |
| 99 | + fn bar() -> #[apple] u8 { .. } |
| 100 | + |
| 101 | + .. |
| 102 | +} |
| 103 | + |
| 104 | +impl Beta for Alpha { // Also works in trait implementations. |
| 105 | + fn bar() -> #[apple] u8 { .. } |
| 106 | + |
| 107 | + .. |
| 108 | +} |
| 109 | +``` |
| 110 | + |
| 111 | +## Trait definitions |
| 112 | + |
| 113 | +An `fn` definition doesn't need to have a body to permit return type attributes. |
| 114 | +Thus, in `trait` definitions, we may write: |
| 115 | + |
| 116 | +```rust |
| 117 | +trait Beta { |
| 118 | + fn bar(&self) -> #[apple] u8; |
| 119 | +} |
| 120 | +``` |
| 121 | + |
| 122 | +## `fn` types |
| 123 | + |
| 124 | +You can also use attributes in function pointer types. |
| 125 | +For example, you may write: |
| 126 | + |
| 127 | +```rust |
| 128 | +type Foo = fn() -> #[apple] u8; |
| 129 | +``` |
| 130 | + |
| 131 | +## Unit return type |
| 132 | + |
| 133 | +When annotating the unit return type `()` must be specified explicitly. For |
| 134 | +example: |
| 135 | + |
| 136 | +```rust |
| 137 | +fn foo() -> #[apple] () { |
| 138 | + .. |
| 139 | +} |
| 140 | +``` |
| 141 | + |
| 142 | +Attempting the following: |
| 143 | + |
| 144 | +```rust |
| 145 | +fn foo() -> #[apple] { |
| 146 | + .. |
| 147 | +} |
| 148 | +``` |
| 149 | + |
| 150 | +will result in a compile error: |
| 151 | + |
| 152 | +``` |
| 153 | +error: return type attributes require an explicit return type |
| 154 | +fn foo() -> #[apple] { |
| 155 | + ^ expected () |
| 156 | +``` |
| 157 | + |
| 158 | +## Built-in attributes |
| 159 | + |
| 160 | +Attributes attached to return types do not have an inherent meaning in |
| 161 | +the type system or in the language. Instead, the meaning is what your |
| 162 | +procedural macros, the tools you use, or what the compiler interprets certain |
| 163 | +specific attributes as. |
| 164 | + |
| 165 | +As for the built-in attributes and their semantics, we will, for the time being, |
| 166 | +only permit the following attributes on return types: |
| 167 | + |
| 168 | +- Lint check attributes, that is: |
| 169 | + `#[allow(C)]`, `#[warn(C)]`, `#[deny(C)]`, `#[forbid(C)]`, |
| 170 | + and tool lint attributes such as `#[allow(clippy::foobar)]`. |
| 171 | + |
| 172 | +All other built-in attributes will be rejected with a semantic check. |
| 173 | +For example, you may not write: |
| 174 | + |
| 175 | +```rust |
| 176 | +fn foo() -> #[inline] u32 { .. } |
| 177 | +``` |
| 178 | + |
| 179 | + |
| 180 | +# Reference-level explanation |
| 181 | +[reference-level-explanation]: #reference-level-explanation |
| 182 | + |
| 183 | +TODO |
| 184 | + |
| 185 | +<!-- |
| 186 | +This is the technical portion of the RFC. Explain the design in sufficient detail that: |
| 187 | +
|
| 188 | +- Its interaction with other features is clear. |
| 189 | +- It is reasonably clear how the feature would be implemented. |
| 190 | +- Corner cases are dissected by example. |
| 191 | +
|
| 192 | +The section should return to the examples given in the previous section, and |
| 193 | +explain more fully how the detailed proposal makes those examples work. |
| 194 | +--> |
| 195 | + |
| 196 | +# Drawbacks |
| 197 | +[drawbacks]: #drawbacks |
| 198 | + |
| 199 | +All drawbacks for attributes in any location also count for this proposal. |
| 200 | + |
| 201 | +Having attributes in many different places of the language complicates its |
| 202 | +grammar. |
| 203 | + |
| 204 | +# Rationale and alternatives |
| 205 | +[rationale-and-alternatives]: #rationale-and-alternatives |
| 206 | + |
| 207 | +> Why is this design the best in the space of possible designs? |
| 208 | +
|
| 209 | +If function parameters can be annotated it is only natural that function return |
| 210 | +types can be annotated as well. |
| 211 | + |
| 212 | +> What other designs have been considered and what is the rationale for not choosing them? |
| 213 | +
|
| 214 | +Using a function attribute to annotate the return type can result in the |
| 215 | +annotation being far apart from the type that it's annotating, as showcased in |
| 216 | +the [Motivation](#motivation) section. |
| 217 | + |
| 218 | +[RFC2602](https://github.com/rust-lang/rfcs/pull/2602) suggests permitting |
| 219 | +attributes to be attached to any types, so implementing that RFC would also |
| 220 | +permit return types to be annotated with attributes. The concern that has |
| 221 | +however been raised with that approach is that permitting attributes nearly |
| 222 | +everywhere would undesirably increase the cognitive load needed to read Rust |
| 223 | +code and thus harm the readability of Rust. Currently attributes are |
| 224 | +restricted to specific positions. Allowing them anywhere (even in nested types) |
| 225 | +would pose a more radical change, whereas this RFC is more of a continuation of |
| 226 | +the status quo by just permitting attributes in one more specific position. |
| 227 | + |
| 228 | +> What is the impact of not doing this? |
| 229 | +
|
| 230 | +DSLs cannot take advantage of the additional expressiveness. |
| 231 | + |
| 232 | +# Prior art |
| 233 | +[prior-art]: #prior-art |
| 234 | + |
| 235 | +Parameter attributes were introduced to Rust with [RFC2565]. |
| 236 | + |
| 237 | +# Unresolved questions |
| 238 | +[unresolved-questions]: #unresolved-questions |
| 239 | + |
| 240 | +Are there more built-in attributes that should be permitted for return types? |
| 241 | + |
| 242 | +Is there precedent of other programming languages permitting return type |
| 243 | +annotations to be placed directly in front of the return type? |
| 244 | + |
| 245 | +# Future possibilities |
| 246 | +[future-possibilities]: #future-possibilities |
| 247 | + |
| 248 | +If `rustdoc` one day supports documentation comments on parameters, it could |
| 249 | +also support documentation comments on return types. |
0 commit comments