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