From db61b4a4de9db4bc17d73e7ca8a986e6fa96f439 Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Thu, 25 Nov 2021 02:15:11 +0100 Subject: [PATCH 1/3] Add RFC for return type attributes --- text/3201-return-type-attrs.md | 248 +++++++++++++++++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 text/3201-return-type-attrs.md diff --git a/text/3201-return-type-attrs.md b/text/3201-return-type-attrs.md new file mode 100644 index 00000000000..02866de72a1 --- /dev/null +++ b/text/3201-return-type-attrs.md @@ -0,0 +1,248 @@ +- Feature Name: `return_type_attrs` +- Start Date: 2021-11-25 +- RFC PR: [rust-lang/rfcs#3201](https://github.com/rust-lang/rfcs/pull/3201) + +# Summary +[summary]: #summary + +Allow attributes in function return type position. For example: + +```rust +fn sum(x: u8, y: u8) -> #[some_attr] u8 { + x + y +} +``` + +# Motivation +[motivation]: #motivation + +Currently the whole function can be annotated with a macro attribute: + +```rust +#[some_attr] +fn example() { .. } +``` + +As well as individual function parameters (introduced in [RFC2565]): + +[RFC2565]: 2565-formal-function-parameter-attributes.md + +```rust +fn example(#[attr] input: String, #[attr] x: u8) { .. } +``` + +However function return types currently cannot be annotated, which forces DSLs +to resort to function attributes, for example: + +```rust +#[return_format = "json"] +fn example(#[param = "q"] query: String, #[page = "p"] page: u64) -> Vec { + .. +} +``` + +Especially for functions with many parameters this can result in the annotation +that is meant for the return type to be several lines apart from the return +type that it actually describes: + +```rs +#[return_format = "json"] +fn example( + #[param = "q"] query: String, + #[param = "p"] page: u64, + #[param = "c"] count: u64, + #[param = "o"] order_by: Column, +) -> Vec { + .. +} +``` + +For such cases return type attributes could provide more clarity: + +```rs +fn example( + #[param = "q"] query: String, + #[param = "p"] page: u64, + #[param = "c"] count: u64, + #[param = "o"] order_by: Column, +) -> #[json] Vec { + .. +} +``` + +Since function parameters already can be annotated this can be regarded as the +next logical step towards more expressive and intuitive DSLs. The motivation +for the introduction of parameter attributes outlined in [RFC2565] largely +applies to return type attributes as well, since they would also be useful for +property based testing, interoperability with other languages and optimization +annotations. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +Return types of `fn` definitions may have attributes attached to them. Thereby, +additional information may be provided. + +For the purposes of illustration, let's assume we have the attribute +`#[apple]` available to us. + +## Basic examples + +The syntax for attaching attributes to return types is shown in the snippet below: + +```rust +// Free functions: +fn foo() -> #[apple] u32 { .. } + +impl Alpha { // In inherent implementations. + fn bar() -> #[apple] u8 { .. } + + .. +} + +impl Beta for Alpha { // Also works in trait implementations. + fn bar() -> #[apple] u8 { .. } + + .. +} +``` + +## Trait definitions + +An `fn` definition doesn't need to have a body to permit return type attributes. +Thus, in `trait` definitions, we may write: + +```rust +trait Beta { + fn bar(&self) -> #[apple] u8; +} +``` + +## `fn` types + +You can also use attributes in function pointer types. +For example, you may write: + +```rust +type Foo = fn() -> #[apple] u8; +``` + +## Unit return type + +When annotating the unit return type `()` must be specified explicitly. For +example: + +```rust +fn foo() -> #[apple] () { + .. +} +``` + +Attempting the following: + +```rust +fn foo() -> #[apple] { + .. +} +``` + +will result in a compile error: + +``` +error: return type attributes require an explicit return type +fn foo() -> #[apple] { + ^ expected () +``` + +## Built-in attributes + +Attributes attached to return types do not have an inherent meaning in +the type system or in the language. Instead, the meaning is what your +procedural macros, the tools you use, or what the compiler interprets certain +specific attributes as. + +As for the built-in attributes and their semantics, we will, for the time being, +only permit the following attributes on return types: + +- Lint check attributes, that is: + `#[allow(C)]`, `#[warn(C)]`, `#[deny(C)]`, `#[forbid(C)]`, + and tool lint attributes such as `#[allow(clippy::foobar)]`. + +All other built-in attributes will be rejected with a semantic check. +For example, you may not write: + +```rust +fn foo() -> #[inline] u32 { .. } +``` + + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +TODO + + + +# Drawbacks +[drawbacks]: #drawbacks + +All drawbacks for attributes in any location also count for this proposal. + +Having attributes in many different places of the language complicates its +grammar. + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +> Why is this design the best in the space of possible designs? + +If function parameters can be annotated it is only natural that function return +types can be annotated as well. + +> What other designs have been considered and what is the rationale for not choosing them? + +Using a function attribute to annotate the return type can result in the +annotation being far apart from the type that it's annotating, as showcased in +the [Motivation](#motivation) section. + +[RFC2602](https://github.com/rust-lang/rfcs/pull/2602) suggests permitting +attributes to be attached to any types, so implementing that RFC would also +permit return types to be annotated with attributes. The concern that has +however been raised with that approach is that permitting attributes nearly +everywhere would undesirably increase the cognitive load needed to read Rust +code and thus harm the readability of Rust. Currently attributes are +restricted to specific positions. Allowing them anywhere (even in nested types) +would pose a more radical change, whereas this RFC is more of a continuation of +the status quo by just permitting attributes in one more specific position. + +> What is the impact of not doing this? + +DSLs cannot take advantage of the additional expressiveness. + +# Prior art +[prior-art]: #prior-art + +Parameter attributes were introduced to Rust with [RFC2565]. + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +Are there more built-in attributes that should be permitted for return types? + +Is there precedent of other programming languages permitting return type +annotations to be placed directly in front of the return type? + +# Future possibilities +[future-possibilities]: #future-possibilities + +If `rustdoc` one day supports documentation comments on parameters, it could +also support documentation comments on return types. From df34fde7137e118efebb5c453abb78685cfa96a8 Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Thu, 25 Nov 2021 07:13:05 +0100 Subject: [PATCH 2/3] return-type-attrs: Also permit attributes on closure return types --- text/3201-return-type-attrs.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/text/3201-return-type-attrs.md b/text/3201-return-type-attrs.md index 02866de72a1..a56ed31f5ed 100644 --- a/text/3201-return-type-attrs.md +++ b/text/3201-return-type-attrs.md @@ -80,8 +80,8 @@ annotations. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation -Return types of `fn` definitions may have attributes attached to them. Thereby, -additional information may be provided. +Return types of `fn` definitions as well as closures may have attributes +attached to them. Thereby, additional information may be provided. For the purposes of illustration, let's assume we have the attribute `#[apple]` available to us. @@ -105,6 +105,11 @@ impl Beta for Alpha { // Also works in trait implementations. .. } + +fn foo() { + // Closures: + let bar = |x| -> #[apple] u8 { .. }; +} ``` ## Trait definitions From 2e3d0126ed853b796191607a3c36badbd05a659d Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Thu, 25 Nov 2021 07:35:41 +0100 Subject: [PATCH 3/3] return-type-attrs: Improve motivating example & clarify DSL --- text/3201-return-type-attrs.md | 54 ++++++++++++++++------------------ 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/text/3201-return-type-attrs.md b/text/3201-return-type-attrs.md index a56ed31f5ed..7806483137d 100644 --- a/text/3201-return-type-attrs.md +++ b/text/3201-return-type-attrs.md @@ -31,44 +31,42 @@ As well as individual function parameters (introduced in [RFC2565]): fn example(#[attr] input: String, #[attr] x: u8) { .. } ``` -However function return types currently cannot be annotated, which forces DSLs -to resort to function attributes, for example: +However function return types currently cannot be annotated, which forces +domain-specific languages (DSLs) to resort to function attributes, for example: ```rust -#[return_format = "json"] -fn example(#[param = "q"] query: String, #[page = "p"] page: u64) -> Vec { - .. +#[wasm_bindgen] +impl RustLayoutEngine { + #[return_type = "MapNode[]"] + pub fn layout( + &self, + #[type = "MapNode[]"] nodes: Vec, + #[type = "MapEdge[]"] edges: Vec + ) -> Vec { + .. + } } ``` -Especially for functions with many parameters this can result in the annotation -that is meant for the return type to be several lines apart from the return -type that it actually describes: +Return type attributes would allow the above example to be changed to: ```rs -#[return_format = "json"] -fn example( - #[param = "q"] query: String, - #[param = "p"] page: u64, - #[param = "c"] count: u64, - #[param = "o"] order_by: Column, -) -> Vec { - .. +#[wasm_bindgen] +impl RustLayoutEngine { + pub fn layout( + &self, + #[type = "MapNode[]"] nodes: Vec, + #[type = "MapEdge[]"] edges: Vec + ) -> #[type = "MapNode[]"] Vec { + .. + } } ``` -For such cases return type attributes could provide more clarity: - -```rs -fn example( - #[param = "q"] query: String, - #[param = "p"] page: u64, - #[param = "c"] count: u64, - #[param = "o"] order_by: Column, -) -> #[json] Vec { - .. -} -``` +The resulting DSL is clearer to read ... in particular in functions with many +parameters where the function attribute would be several lines apart from the +return type (because `rustfmt` by default puts each parameter on its own line +when the function signature gets too long). Since function parameters already can be annotated this can be regarded as the next logical step towards more expressive and intuitive DSLs. The motivation