From f43c78b733634dc2c21faef615be04193d68caaa Mon Sep 17 00:00:00 2001 From: Lixou <82600264+DasLixou@users.noreply.github.com> Date: Sun, 20 Aug 2023 18:46:35 +0200 Subject: [PATCH 01/16] Add `impl-trait-for-fn` --- text/0000-impl-trait-for-fn.md | 90 ++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 text/0000-impl-trait-for-fn.md diff --git a/text/0000-impl-trait-for-fn.md b/text/0000-impl-trait-for-fn.md new file mode 100644 index 00000000000..50d03ee0117 --- /dev/null +++ b/text/0000-impl-trait-for-fn.md @@ -0,0 +1,90 @@ +- Feature Name: `impl-trait-for-fn` +- Start Date: 2023-08-20 +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- Rust Issue: N/A + +# Summary +[summary]: #summary + +Support for implementing traits on functions + +# Motivation +[motivation]: #motivation + +I was trying to make something similar to bevy's system functions. And for safety reasons, they check for conflicts between SystemParams, so that a function requiring `Res` and `ResMut` [panic](https://github.com/bevyengine/bevy/blob/main/crates/bevy_ecs/src/system/system_param.rs#L421). + +Then after I heard about axum's [`#[debug_handler]`](https://docs.rs/axum/latest/axum/attr.debug_handler.html) I wanted to do something similar to my copy of bevy systems, so that I get compile time errors when there is a conflict. I wanted even more, I wanted to force the user to mark the function with a specific proc attribute macro in order to make it possible to pass it into my code and call itself a system. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +As we all know, you can implement a trait for a struct with the following syntax +```rust +struct Timmy; +impl Person for Timmy { + fn greet() { + println!("Hey it's me, Timmy!"); + } +} +``` +And we can also implement a trait for all functions with a specific signature like this +```rust +impl ValidSignature for F + where F: Fn(i32) -> bool +{ + /* ... */ +} +``` +Now we can also implement traits for specific functions only +```rust +fn valid() {} +impl ValidFunction for fn valid { + /* ... */ +} +``` + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +It gives the possibility to also implement a trait directly scoped to a function instead of generic implementation of multiple. Other than that, it basically behaves the same. It should also be possible to implement them via proc attribute macros: +```rust +#[impl_debug_name = "Greeting"] +fn greet() { + /* ... */ +} +// should expand to something like +fn greet() { + /* ... */ +} +impl FnDebugName for fn greet() { + fn debug_name() -> &'static str { + "Greeting" + } +} +``` + +# Drawbacks +[drawbacks]: #drawbacks + +i dont know any + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +I think it's a easy task because we can already implement traits for a group of functions with the same signature, so why shouldn't we also implement single scoped impls? + +# Prior art +[prior-art]: #prior-art + +i dont know any + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +- Is the syntax good? I feel like we could drop the `fn` to from `impl Trait for fn function` to `impl Trait for function`. +- What about closures? They don't even have names so targetting them would be quite difficult. I wouldn't want to use the compiler generated mess of a name like `[closure@src/main.rs:13:18: 13:20]`. It would also contain line numbers which would be changing quite often so thats not ideal. + +# Future possibilities +[future-possibilities]: #future-possibilities + +- also make it possible to implement traits for closures directly. \ No newline at end of file From ae0d1373bffb6227ab02511b6a19cc26cf825910 Mon Sep 17 00:00:00 2001 From: Lixou <82600264+DasLixou@users.noreply.github.com> Date: Sun, 20 Aug 2023 18:48:34 +0200 Subject: [PATCH 02/16] Add PR number --- text/{0000-impl-trait-for-fn.md => 3476-impl-trait-for-fn.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename text/{0000-impl-trait-for-fn.md => 3476-impl-trait-for-fn.md} (97%) diff --git a/text/0000-impl-trait-for-fn.md b/text/3476-impl-trait-for-fn.md similarity index 97% rename from text/0000-impl-trait-for-fn.md rename to text/3476-impl-trait-for-fn.md index 50d03ee0117..f986045b9ab 100644 --- a/text/0000-impl-trait-for-fn.md +++ b/text/3476-impl-trait-for-fn.md @@ -1,6 +1,6 @@ - Feature Name: `impl-trait-for-fn` - Start Date: 2023-08-20 -- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- RFC PR: [rust-lang/rfcs#3476](https://github.com/rust-lang/rfcs/pull/3476) - Rust Issue: N/A # Summary From b2c592b1c05d7cb125a9a36c7472a87b2196e6c1 Mon Sep 17 00:00:00 2001 From: Lixou <82600264+DasLixou@users.noreply.github.com> Date: Tue, 22 Aug 2023 08:50:15 +0200 Subject: [PATCH 03/16] Make impl for more described functions clearer --- text/3476-impl-trait-for-fn.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/text/3476-impl-trait-for-fn.md b/text/3476-impl-trait-for-fn.md index f986045b9ab..937700ef1b6 100644 --- a/text/3476-impl-trait-for-fn.md +++ b/text/3476-impl-trait-for-fn.md @@ -46,6 +46,14 @@ impl ValidFunction for fn valid { # Reference-level explanation [reference-level-explanation]: #reference-level-explanation +When the function has parameters, modifiers or a return type, it should not be included in the impl block, because the path is already unique +```rust +async fn request_name(id: PersonID) -> String { .. } + +impl Requestable for fn request_name { + /* ... */ +} +``` It gives the possibility to also implement a trait directly scoped to a function instead of generic implementation of multiple. Other than that, it basically behaves the same. It should also be possible to implement them via proc attribute macros: ```rust #[impl_debug_name = "Greeting"] From add0ca5dd303f6af217a43bd6c143e227bc7f918 Mon Sep 17 00:00:00 2001 From: Lixou <82600264+DasLixou@users.noreply.github.com> Date: Tue, 22 Aug 2023 09:14:08 +0200 Subject: [PATCH 04/16] Fix mistakes --- text/3476-impl-trait-for-fn.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/text/3476-impl-trait-for-fn.md b/text/3476-impl-trait-for-fn.md index 937700ef1b6..769a23843b4 100644 --- a/text/3476-impl-trait-for-fn.md +++ b/text/3476-impl-trait-for-fn.md @@ -46,6 +46,9 @@ impl ValidFunction for fn valid { # Reference-level explanation [reference-level-explanation]: #reference-level-explanation +It gives the possibility to also implement a trait directly scoped to a function instead of generic implementation of multiple. Other than that, it basically behaves the same. + + When the function has parameters, modifiers or a return type, it should not be included in the impl block, because the path is already unique ```rust async fn request_name(id: PersonID) -> String { .. } @@ -54,7 +57,7 @@ impl Requestable for fn request_name { /* ... */ } ``` -It gives the possibility to also implement a trait directly scoped to a function instead of generic implementation of multiple. Other than that, it basically behaves the same. It should also be possible to implement them via proc attribute macros: +It should also be possible to implement them via proc attribute macros: ```rust #[impl_debug_name = "Greeting"] fn greet() { @@ -64,7 +67,7 @@ fn greet() { fn greet() { /* ... */ } -impl FnDebugName for fn greet() { +impl FnDebugName for fn greet { fn debug_name() -> &'static str { "Greeting" } From 2f7c0bd4e8a03e9bf64198ccb01377fb2a891548 Mon Sep 17 00:00:00 2001 From: Lixou <82600264+DasLixou@users.noreply.github.com> Date: Tue, 22 Aug 2023 09:21:45 +0200 Subject: [PATCH 05/16] Add more examples for more cases --- text/3476-impl-trait-for-fn.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/text/3476-impl-trait-for-fn.md b/text/3476-impl-trait-for-fn.md index 769a23843b4..f6e0270eca5 100644 --- a/text/3476-impl-trait-for-fn.md +++ b/text/3476-impl-trait-for-fn.md @@ -57,6 +57,25 @@ impl Requestable for fn request_name { /* ... */ } ``` +When the function is for example in a different mod, it should be referenced by its path +```rust +mod sub { + fn sub_mod_fn() { .. } +} +impl OutOfNamesRunnable for fn sub::sub_mod_fn { + /* ... */ +} +``` +Just like how a fn inside an impl block still implements the Fn trait, it should also be possible to implement traits for them +```rust +struct MyStruct; +impl MyStruct { + fn new() -> Self { Self } +} +impl Creatable for fn MyStruct::new { + /* ... */ +} +``` It should also be possible to implement them via proc attribute macros: ```rust #[impl_debug_name = "Greeting"] From f13f239cf042a16445df2aed8727ce22717986af Mon Sep 17 00:00:00 2001 From: Lixou <82600264+DasLixou@users.noreply.github.com> Date: Tue, 22 Aug 2023 15:45:40 +0200 Subject: [PATCH 06/16] Elaborate where to bind the trait to --- text/3476-impl-trait-for-fn.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/3476-impl-trait-for-fn.md b/text/3476-impl-trait-for-fn.md index f6e0270eca5..bc7e090f8dd 100644 --- a/text/3476-impl-trait-for-fn.md +++ b/text/3476-impl-trait-for-fn.md @@ -46,8 +46,8 @@ impl ValidFunction for fn valid { # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -It gives the possibility to also implement a trait directly scoped to a function instead of generic implementation of multiple. Other than that, it basically behaves the same. - +It gives the possibility to also implement a trait directly scoped to a function instead of generic implementation of multiple. +When writing `impl MyTrait for fn func`, the `MyTrait` trait will be implemented for the type behind the function `func`. When the function has parameters, modifiers or a return type, it should not be included in the impl block, because the path is already unique ```rust @@ -117,4 +117,4 @@ i dont know any # Future possibilities [future-possibilities]: #future-possibilities -- also make it possible to implement traits for closures directly. \ No newline at end of file +- also make it possible to implement traits for closures directly. From 7b7aebb6973c2104746208ca52794872071b673e Mon Sep 17 00:00:00 2001 From: Lixou <82600264+DasLixou@users.noreply.github.com> Date: Tue, 22 Aug 2023 16:48:20 +0200 Subject: [PATCH 07/16] Elaborate the RFC to a larger topic --- text/3476-expose-fn-type.md | 147 +++++++++++++++++++++++++++++++++ text/3476-impl-trait-for-fn.md | 120 --------------------------- 2 files changed, 147 insertions(+), 120 deletions(-) create mode 100644 text/3476-expose-fn-type.md delete mode 100644 text/3476-impl-trait-for-fn.md diff --git a/text/3476-expose-fn-type.md b/text/3476-expose-fn-type.md new file mode 100644 index 00000000000..dde650d2f2a --- /dev/null +++ b/text/3476-expose-fn-type.md @@ -0,0 +1,147 @@ +- Feature Name: `expose-fn-type` +- Start Date: 2023-08-20 +- RFC PR: [rust-lang/rfcs#3476](https://github.com/rust-lang/rfcs/pull/3476) +- Rust Issue: N/A + +# Summary +[summary]: #summary + +This exposes the ghost-/inner-/localtype of a function to the user. + +# Motivation +[motivation]: #motivation + +I was trying to make something similar to bevy's system functions. And for safety reasons, they check for conflicts between SystemParams, so that a function requiring `Res` and `ResMut` [panic](https://github.com/bevyengine/bevy/blob/main/crates/bevy_ecs/src/system/system_param.rs#L421). + +Then after I heard about axum's [`#[debug_handler]`](https://docs.rs/axum/latest/axum/attr.debug_handler.html) I wanted to do something similar to my copy of bevy systems, so that I get compile time errors when there is a conflict. I wanted even more, I wanted to force the user to mark the function with a specific proc attribute macro in order to make it possible to pass it into my code and call itself a system. + +For that, I would need to mark the type behind the function, for example, with a trait. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +As we all know, you can refer to a struct by its name and for example implement a trait +```rust +struct Timmy; +impl Person for Timmy { + fn greet() { + println!("Hey it's me, Timmy!"); + } +} +``` +When we want to target a specific function for a trait implementation, we somehow need to get to the type behind it. That is being done with the `fn` keyword as follows +```rust +fn my_function() {} +impl MyTrait for fn my_function { + /* ... */ +} +``` +--- +For a better understanding, imagine you have a struct like this: +```rust +struct FnContainer { + inner: F, +} +fn goods() { } + +let contained_goods = FnContainer { + inner: goods +}; +``` +Here, we make a `FnContainer` which can hold every function with the signature `() -> ()` via generics. +But what about explicitly designing the `FnContainer` for a specific function, just like the compiler does when resolving the generics. This will work the same as with the trait impl from above: +```rust +struct GoodsContainer { + inner: fn goods, +} +fn goods() {} + +let contained_goods = GoodsContainer { + inner: goods, +} +``` +--- +A function with a more complex signature, like with parameters, modifiers or a return type, is still just referenced by its name, because it's already unique +```rust +async fn request_name(id: PersonID) -> String { .. } + +impl Requestable for fn request_name { + /* ... */ +} +``` + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +As described in the **Guide-level explanation**, with the syntax `fn `, we can reference the type behind the named function. + +When the function is for example in a different mod, it should be referenced by its path +```rust +mod sub { + fn sub_mod_fn() { .. } +} +impl fn sub::sub_mod_fn { + /* ... */ +} +``` + +It should be also possible to get the type of functions inside impl blocks: + +```rust +struct MyStruct; +impl MyStruct { + fn new() -> Self { Self } +} +impl fn MyStruct::new { + /* ... */ +} +``` + +Just as structs and enums have the possibility to derive traits to automatically generate code, function type do too + +```rust +#[derive(DbgSignature)] +fn signature_test(val: i32) -> bool { + /* ... */ +} + +// Expands to + +fn signature_test(val: i32) -> bool { + /* ... */ +} +impl DbgSignature for fn signature_test { + fn dbg_signature() -> &'static str { + "fn signature_test(val: i32) -> bool" + } +} +``` + +Other than that, it should behave like every other type does. + +# Drawbacks +[drawbacks]: #drawbacks + +- When introducing the derive feature, it could lead to parsing problems with proc macros having an older `syn` crate version. + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +The type behind functions already exists, we just need to expose it to the user. +The hard part would be allowing derives, because that may break some things. + +# Prior art +[prior-art]: #prior-art + +i dont know any + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +- Is the syntax good? It could create confusion between a function pointer. +- What about closures? They don't even have names so targetting them would be quite difficult. I wouldn't want to use the compiler generated mess of a name like `[closure@src/main.rs:13:18: 13:20]`. It would also contain line numbers which would be changing quite often so thats not ideal. + +# Future possibilities +[future-possibilities]: #future-possibilities + +- Also expose the type of closures diff --git a/text/3476-impl-trait-for-fn.md b/text/3476-impl-trait-for-fn.md deleted file mode 100644 index bc7e090f8dd..00000000000 --- a/text/3476-impl-trait-for-fn.md +++ /dev/null @@ -1,120 +0,0 @@ -- Feature Name: `impl-trait-for-fn` -- Start Date: 2023-08-20 -- RFC PR: [rust-lang/rfcs#3476](https://github.com/rust-lang/rfcs/pull/3476) -- Rust Issue: N/A - -# Summary -[summary]: #summary - -Support for implementing traits on functions - -# Motivation -[motivation]: #motivation - -I was trying to make something similar to bevy's system functions. And for safety reasons, they check for conflicts between SystemParams, so that a function requiring `Res` and `ResMut` [panic](https://github.com/bevyengine/bevy/blob/main/crates/bevy_ecs/src/system/system_param.rs#L421). - -Then after I heard about axum's [`#[debug_handler]`](https://docs.rs/axum/latest/axum/attr.debug_handler.html) I wanted to do something similar to my copy of bevy systems, so that I get compile time errors when there is a conflict. I wanted even more, I wanted to force the user to mark the function with a specific proc attribute macro in order to make it possible to pass it into my code and call itself a system. - -# Guide-level explanation -[guide-level-explanation]: #guide-level-explanation - -As we all know, you can implement a trait for a struct with the following syntax -```rust -struct Timmy; -impl Person for Timmy { - fn greet() { - println!("Hey it's me, Timmy!"); - } -} -``` -And we can also implement a trait for all functions with a specific signature like this -```rust -impl ValidSignature for F - where F: Fn(i32) -> bool -{ - /* ... */ -} -``` -Now we can also implement traits for specific functions only -```rust -fn valid() {} -impl ValidFunction for fn valid { - /* ... */ -} -``` - -# Reference-level explanation -[reference-level-explanation]: #reference-level-explanation - -It gives the possibility to also implement a trait directly scoped to a function instead of generic implementation of multiple. -When writing `impl MyTrait for fn func`, the `MyTrait` trait will be implemented for the type behind the function `func`. - -When the function has parameters, modifiers or a return type, it should not be included in the impl block, because the path is already unique -```rust -async fn request_name(id: PersonID) -> String { .. } - -impl Requestable for fn request_name { - /* ... */ -} -``` -When the function is for example in a different mod, it should be referenced by its path -```rust -mod sub { - fn sub_mod_fn() { .. } -} -impl OutOfNamesRunnable for fn sub::sub_mod_fn { - /* ... */ -} -``` -Just like how a fn inside an impl block still implements the Fn trait, it should also be possible to implement traits for them -```rust -struct MyStruct; -impl MyStruct { - fn new() -> Self { Self } -} -impl Creatable for fn MyStruct::new { - /* ... */ -} -``` -It should also be possible to implement them via proc attribute macros: -```rust -#[impl_debug_name = "Greeting"] -fn greet() { - /* ... */ -} -// should expand to something like -fn greet() { - /* ... */ -} -impl FnDebugName for fn greet { - fn debug_name() -> &'static str { - "Greeting" - } -} -``` - -# Drawbacks -[drawbacks]: #drawbacks - -i dont know any - -# Rationale and alternatives -[rationale-and-alternatives]: #rationale-and-alternatives - -I think it's a easy task because we can already implement traits for a group of functions with the same signature, so why shouldn't we also implement single scoped impls? - -# Prior art -[prior-art]: #prior-art - -i dont know any - -# Unresolved questions -[unresolved-questions]: #unresolved-questions - -- Is the syntax good? I feel like we could drop the `fn` to from `impl Trait for fn function` to `impl Trait for function`. -- What about closures? They don't even have names so targetting them would be quite difficult. I wouldn't want to use the compiler generated mess of a name like `[closure@src/main.rs:13:18: 13:20]`. It would also contain line numbers which would be changing quite often so thats not ideal. - -# Future possibilities -[future-possibilities]: #future-possibilities - -- also make it possible to implement traits for closures directly. From 32dfb826801ae322ec238ab98ceda7b51929d1ed Mon Sep 17 00:00:00 2001 From: Lixou <82600264+DasLixou@users.noreply.github.com> Date: Tue, 22 Aug 2023 19:43:11 +0200 Subject: [PATCH 08/16] Add examples for functions with generics --- text/3476-expose-fn-type.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/text/3476-expose-fn-type.md b/text/3476-expose-fn-type.md index dde650d2f2a..7d25d30e917 100644 --- a/text/3476-expose-fn-type.md +++ b/text/3476-expose-fn-type.md @@ -97,6 +97,22 @@ impl fn MyStruct::new { } ``` +When a function has generics, they will be handled as follows, just like we know it from normal types +```rust +fn send(val: T) {} +impl ParcelStation for fn send { + /* ... */ +} +``` + +When we have an implicit generic, they will be appended in order to the generic list: +```rust +fn implicit_generic(val: impl Clone) -> impl ToString {} +impl for fn implicit_generic { + /* ... */ +} +``` + Just as structs and enums have the possibility to derive traits to automatically generate code, function type do too ```rust @@ -140,6 +156,7 @@ i dont know any - Is the syntax good? It could create confusion between a function pointer. - What about closures? They don't even have names so targetting them would be quite difficult. I wouldn't want to use the compiler generated mess of a name like `[closure@src/main.rs:13:18: 13:20]`. It would also contain line numbers which would be changing quite often so thats not ideal. +- I provided a possible solution for a `fn implicit_generic(val: impl Clone) -> impl ToString` function, but because we currently don't have a defined syntax for those generics in types, thus we can't use `impl Trait` as types for fields in structs, we should think about this more, maybe don't implement exposed types of function for such `fn`s and wait for another RFC? # Future possibilities [future-possibilities]: #future-possibilities From adb2c957ea2321ccbddebed7c5fff08ec09212ae Mon Sep 17 00:00:00 2001 From: Lixou <82600264+DasLixou@users.noreply.github.com> Date: Fri, 25 Aug 2023 12:47:38 +0200 Subject: [PATCH 09/16] Update text/3476-expose-fn-type.md Co-authored-by: Jonathan Chan Kwan Yin --- text/3476-expose-fn-type.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3476-expose-fn-type.md b/text/3476-expose-fn-type.md index 7d25d30e917..5d05672d95d 100644 --- a/text/3476-expose-fn-type.md +++ b/text/3476-expose-fn-type.md @@ -45,7 +45,7 @@ struct FnContainer { fn goods() { } let contained_goods = FnContainer { - inner: goods + inner: goods, }; ``` Here, we make a `FnContainer` which can hold every function with the signature `() -> ()` via generics. From 19dba3644f2e9c556eb116f3499261a0fc85b69e Mon Sep 17 00:00:00 2001 From: Lixou <82600264+DasLixou@users.noreply.github.com> Date: Fri, 25 Aug 2023 12:53:45 +0200 Subject: [PATCH 10/16] Update text/3476-expose-fn-type.md Co-authored-by: Jonathan Chan Kwan Yin --- text/3476-expose-fn-type.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3476-expose-fn-type.md b/text/3476-expose-fn-type.md index 5d05672d95d..183719c0980 100644 --- a/text/3476-expose-fn-type.md +++ b/text/3476-expose-fn-type.md @@ -85,7 +85,7 @@ impl fn sub::sub_mod_fn { } ``` -It should be also possible to get the type of functions inside impl blocks: +It should be also possible to get the type of associated functions: ```rust struct MyStruct; From fc69e90f9c34471b32982234f073df1069fa5ac6 Mon Sep 17 00:00:00 2001 From: Lixou <82600264+DasLixou@users.noreply.github.com> Date: Fri, 25 Aug 2023 13:49:26 +0200 Subject: [PATCH 11/16] Apply suggestions --- text/3476-expose-fn-type.md | 109 ++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 37 deletions(-) diff --git a/text/3476-expose-fn-type.md b/text/3476-expose-fn-type.md index 183719c0980..aa37003caed 100644 --- a/text/3476-expose-fn-type.md +++ b/text/3476-expose-fn-type.md @@ -6,7 +6,7 @@ # Summary [summary]: #summary -This exposes the ghost-/inner-/localtype of a function to the user. +This exposes the function type of a function item to the user. # Motivation [motivation]: #motivation @@ -15,7 +15,19 @@ I was trying to make something similar to bevy's system functions. And for safet Then after I heard about axum's [`#[debug_handler]`](https://docs.rs/axum/latest/axum/attr.debug_handler.html) I wanted to do something similar to my copy of bevy systems, so that I get compile time errors when there is a conflict. I wanted even more, I wanted to force the user to mark the function with a specific proc attribute macro in order to make it possible to pass it into my code and call itself a system. -For that, I would need to mark the type behind the function, for example, with a trait. +For that, I would need to mark the type behind the function item, for example, with a trait. + +# Terminology + +I'll may shorten `function` to `fn` sometimes. + +- **function pointer**: pointer type with the type syntax `fn(?) -> ?` directly pointing at a function, not the type implementing the `Fn[Once/Mut](?) -> ?` traits. +- **function item** (or just function): a declared function in code. free-standing or associated to a type. +- **function group**: many non-specific functions with the same signature (params, return type, etc.) +- **function trait(s)**: the `Fn[Once/Mut](?) -> ?` traits +- **function type**: the type behind a function, which also implements the function traits. +- **fixed type**: directly named type, no generic / `impl Trait`. +- **describe the function type**: write `fn(..) -> ? name` instead of just `fn name`. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation @@ -29,43 +41,46 @@ impl Person for Timmy { } } ``` -When we want to target a specific function for a trait implementation, we somehow need to get to the type behind it. That is being done with the `fn` keyword as follows +When we want to target a specific function for a trait implementation, we somehow need to get to the type behind it. +Refering to the hidden type is achieved via the following syntax ```rust -fn my_function() {} -impl MyTrait for fn my_function { +fn is_positive(a: i32) -> bool { /* ... */ } +impl MyTrait for fn(i32) -> bool is_positive { /* ... */ } ``` ---- -For a better understanding, imagine you have a struct like this: +For function signatures, where every parameter/return-type is a fixed type and can be known just by refering to the function (so no generics or `impl Trait` parameters/return type), we can drop the redundant information: ```rust -struct FnContainer { - inner: F, +fn is_positive(a: i32) -> bool { /* ... */ } +impl MyTrait for fn is_positive { + /* ... */ } -fn goods() { } +``` -let contained_goods = FnContainer { - inner: goods, -}; +> 💡 NOTE: Even when we need to describe the function type but the return type is `()`, we can (just as for function pointers and function traits) drop the `-> ()` from the type. (This should also be added as a lint). + +--- +A function with a more complex signature, like a const function, we just ignore that when naming the type: +```rust +const fn my_fn(a: i32) -> (i16, i16) { .. } +impl MyTrait for fn my_fn {} +// or with explicit declaration +impl MyTrait for fn(i32) -> (i16, i16) my_fn { .. } ``` -Here, we make a `FnContainer` which can hold every function with the signature `() -> ()` via generics. -But what about explicitly designing the `FnContainer` for a specific function, just like the compiler does when resolving the generics. This will work the same as with the trait impl from above: + +When having an async function, we in theory have a `impl Future` as a return type, which should force us to explicitly declare the function type like so ```rust -struct GoodsContainer { - inner: fn goods, -} -fn goods() {} +async fn request_name(id: PersonID) -> String { .. } -let contained_goods = GoodsContainer { - inner: goods, +impl> Requestable for fn(PersonID) -> F request_name { + /* ... */ } ``` ---- -A function with a more complex signature, like with parameters, modifiers or a return type, is still just referenced by its name, because it's already unique +We can take a shortcut and use the `async` keyword, as long as the `Output` assoc type in the Future is still fixed ```rust async fn request_name(id: PersonID) -> String { .. } -impl Requestable for fn request_name { +impl Requestable for async fn request_name { /* ... */ } ``` @@ -73,18 +88,23 @@ impl Requestable for fn request_name { # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -As described in the **Guide-level explanation**, with the syntax `fn `, we can reference the type behind the named function. +As described in the **Guide-level explanation**, with the syntax `[async] fn[(..) -> ?] `, we can reference the type behind the named function. When the function is for example in a different mod, it should be referenced by its path ```rust mod sub { fn sub_mod_fn() { .. } } -impl fn sub::sub_mod_fn { +trait MyTrait {} +impl MyTrait for fn sub::sub_mod_fn { /* ... */ } ``` +> ⚠️ NOTE: The same rules apply here as for normal types. Either the function item or the trait to implement mustn't be foreign for the impl. [Same as E0210](https://github.com/rust-lang/rust/blob/master/compiler/rustc_error_codes/src/error_codes/E0210.md) + +--- + It should be also possible to get the type of associated functions: ```rust @@ -97,26 +117,45 @@ impl fn MyStruct::new { } ``` -When a function has generics, they will be handled as follows, just like we know it from normal types +When the associated function comes from a trait, the same rules as for associated types apply here ([Ambiguous Associated Type, E0223](https://github.com/rust-lang/rust/blob/master/compiler/rustc_error_codes/src/error_codes/E0223.md)): + +```rust +struct MyStruct; +type MyTrait { + fn ambiguous(); +} +impl MyTrait for MyStruct { + fn ambiguous() { } +} +impl fn MyStruct::ambiguous { } // ERROR: ambiguous associated function +// instead: +impl fn ::ambiguous { } // OK +``` + +--- + +When a function has generics, the function type is forced to be described, and the generic should be placed at it's desired position: ```rust -fn send(val: T) {} -impl ParcelStation for fn send { +fn send(val: T, postal_code: u32) {} +impl ParcelStation for fn(T, u32) send { /* ... */ } ``` -When we have an implicit generic, they will be appended in order to the generic list: +When we have an implicit generic, the same rule applies ```rust fn implicit_generic(val: impl Clone) -> impl ToString {} -impl for fn implicit_generic { +impl for fn(T) -> U implicit_generic { /* ... */ } ``` -Just as structs and enums have the possibility to derive traits to automatically generate code, function type do too +--- + +Just as structs and enums have the possibility to derive traits to automatically generate code, function type have similar ways via attribute macros: ```rust -#[derive(DbgSignature)] +#[debug_signature] fn signature_test(val: i32) -> bool { /* ... */ } @@ -138,13 +177,10 @@ Other than that, it should behave like every other type does. # Drawbacks [drawbacks]: #drawbacks -- When introducing the derive feature, it could lead to parsing problems with proc macros having an older `syn` crate version. - # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives The type behind functions already exists, we just need to expose it to the user. -The hard part would be allowing derives, because that may break some things. # Prior art [prior-art]: #prior-art @@ -156,7 +192,6 @@ i dont know any - Is the syntax good? It could create confusion between a function pointer. - What about closures? They don't even have names so targetting them would be quite difficult. I wouldn't want to use the compiler generated mess of a name like `[closure@src/main.rs:13:18: 13:20]`. It would also contain line numbers which would be changing quite often so thats not ideal. -- I provided a possible solution for a `fn implicit_generic(val: impl Clone) -> impl ToString` function, but because we currently don't have a defined syntax for those generics in types, thus we can't use `impl Trait` as types for fields in structs, we should think about this more, maybe don't implement exposed types of function for such `fn`s and wait for another RFC? # Future possibilities [future-possibilities]: #future-possibilities From 95492837b0f7dbf1737feb9a24195b3d698de6f8 Mon Sep 17 00:00:00 2001 From: Lixou <82600264+DasLixou@users.noreply.github.com> Date: Fri, 25 Aug 2023 14:02:10 +0200 Subject: [PATCH 12/16] Fix example --- text/3476-expose-fn-type.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3476-expose-fn-type.md b/text/3476-expose-fn-type.md index aa37003caed..730534602b8 100644 --- a/text/3476-expose-fn-type.md +++ b/text/3476-expose-fn-type.md @@ -145,7 +145,7 @@ impl ParcelStation for fn(T, u32) send { When we have an implicit generic, the same rule applies ```rust fn implicit_generic(val: impl Clone) -> impl ToString {} -impl for fn(T) -> U implicit_generic { +impl for fn(T) -> U implicit_generic { /* ... */ } ``` From 9428e8804e365ec9287612a6a834d5097d57af03 Mon Sep 17 00:00:00 2001 From: Lixou <82600264+DasLixou@users.noreply.github.com> Date: Sat, 26 Aug 2023 07:39:48 +0200 Subject: [PATCH 13/16] Update 3476-expose-fn-type.md Co-authored-by: Mads Marquart --- text/3476-expose-fn-type.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3476-expose-fn-type.md b/text/3476-expose-fn-type.md index 730534602b8..94c66fbdcb3 100644 --- a/text/3476-expose-fn-type.md +++ b/text/3476-expose-fn-type.md @@ -60,7 +60,7 @@ impl MyTrait for fn is_positive { > 💡 NOTE: Even when we need to describe the function type but the return type is `()`, we can (just as for function pointers and function traits) drop the `-> ()` from the type. (This should also be added as a lint). --- -A function with a more complex signature, like a const function, we just ignore that when naming the type: +A function with a more complex signature, like a function that specifies `const`, `unsafe` or `extern "ABI"`, we just ignore that when naming the type: ```rust const fn my_fn(a: i32) -> (i16, i16) { .. } impl MyTrait for fn my_fn {} From 55b39fca964a0e9f74258b0f67f95abd6ec901fb Mon Sep 17 00:00:00 2001 From: Lixou <82600264+DasLixou@users.noreply.github.com> Date: Sat, 26 Aug 2023 11:49:48 +0200 Subject: [PATCH 14/16] Example and Motivation work --- text/3476-expose-fn-type.md | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/text/3476-expose-fn-type.md b/text/3476-expose-fn-type.md index 94c66fbdcb3..ac30a998200 100644 --- a/text/3476-expose-fn-type.md +++ b/text/3476-expose-fn-type.md @@ -11,12 +11,20 @@ This exposes the function type of a function item to the user. # Motivation [motivation]: #motivation +### DasLixou + I was trying to make something similar to bevy's system functions. And for safety reasons, they check for conflicts between SystemParams, so that a function requiring `Res` and `ResMut` [panic](https://github.com/bevyengine/bevy/blob/main/crates/bevy_ecs/src/system/system_param.rs#L421). Then after I heard about axum's [`#[debug_handler]`](https://docs.rs/axum/latest/axum/attr.debug_handler.html) I wanted to do something similar to my copy of bevy systems, so that I get compile time errors when there is a conflict. I wanted even more, I wanted to force the user to mark the function with a specific proc attribute macro in order to make it possible to pass it into my code and call itself a system. For that, I would need to mark the type behind the function item, for example, with a trait. +### madsmtm + +In Swift, some functions have an associated selector that you can access with [`#selector`](https://developer.apple.com/documentation/swift/using-objective-c-runtime-features-in-swift). + +In my crate `objc2`, it would be immensely beautiful (and useful) to be able to do something similar, e.g. access a function's selector using something like `MyClass::my_function::Selector` or `selector(MyClass::my_function)`, instead of having to know the selector name (which might be something completely different than the function name). + # Terminology I'll may shorten `function` to `fn` sometimes. @@ -132,6 +140,19 @@ impl fn MyStruct::ambiguous { } // ERROR: ambiguous associated function impl fn ::ambiguous { } // OK ``` +When the type of the associated function has generics, they will be handles as follows + +```rust +struct MyStruct(T); +impl MyStruct { + fn get() -> T { .. } +} + +impl fn MyStruct::::get { } +// or fully described: +impl fn() -> T MyStruct::::get { } +``` + --- When a function has generics, the function type is forced to be described, and the generic should be placed at it's desired position: @@ -152,6 +173,24 @@ impl for fn(T) -> U implicit_generic { --- +When functions have lifetimes, they have to be included in the types +```rust +fn log(text: &str) { .. } +impl<'a> Logger for fn(&'a str) log { + /* ... */ +} +``` + +When the lifetime is explicitly defined on the function signature and there's no other rule forcing us to describe the function type, we can take a shortcut as follows +```rust +fn log<'a>(text: &'a str) { .. } // explicit lifetime 'a +impl<'a> Logger for fn<'a> log { + /* ... */ +} +``` + +--- + Just as structs and enums have the possibility to derive traits to automatically generate code, function type have similar ways via attribute macros: ```rust From 9a89cad9d9f93a487545b9db801dc9da2bc706a2 Mon Sep 17 00:00:00 2001 From: Lixou <82600264+DasLixou@users.noreply.github.com> Date: Sat, 26 Aug 2023 11:58:46 +0200 Subject: [PATCH 15/16] Add Todo for consistent syntax --- text/3476-expose-fn-type.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/text/3476-expose-fn-type.md b/text/3476-expose-fn-type.md index ac30a998200..2ba348e8d2c 100644 --- a/text/3476-expose-fn-type.md +++ b/text/3476-expose-fn-type.md @@ -213,6 +213,37 @@ impl DbgSignature for fn signature_test { Other than that, it should behave like every other type does. +# Additional ToDo's + +## Change the fn type syntax for consistency + +When we try to compile the current [code snippet](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=cba2a1391c3e431a7499c6bf427b350d) +```rust +fn cool<'a, T: Clone>(val: &'a T) -> (i32, bool) { + todo!() +} + +fn main() { + let _a: () = cool; +} +``` +we get following error: +``` +error[E0308]: mismatched types + --> src/main.rs:6:18 + | +6 | let _a: () = cool; + | -- ^^^^ expected `()`, found fn item + | | + | expected due to this + | + = note: expected unit type `()` + found fn item `for<'a> fn(&'a _) -> (i32, bool) {cool::<_>}` + +For more information about this error, try `rustc --explain E0308`. +``` +For consistency, we should change the syntax to `for<'a, T: Clone> fn(&'a T) -> (i32, bool) cool` (I'm not sure if we should put generics in the for) + # Drawbacks [drawbacks]: #drawbacks From 0804b52d52507ae2a3b12a6540514ef3a0f0fcb2 Mon Sep 17 00:00:00 2001 From: Lixou <82600264+DasLixou@users.noreply.github.com> Date: Fri, 1 Sep 2023 12:27:00 +0200 Subject: [PATCH 16/16] I think we can drop those --- text/3476-expose-fn-type.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/3476-expose-fn-type.md b/text/3476-expose-fn-type.md index 2ba348e8d2c..15154f91803 100644 --- a/text/3476-expose-fn-type.md +++ b/text/3476-expose-fn-type.md @@ -148,9 +148,9 @@ impl MyStruct { fn get() -> T { .. } } -impl fn MyStruct::::get { } +impl fn MyStruct::get { } // or fully described: -impl fn() -> T MyStruct::::get { } +impl fn() -> T MyStruct::get { } ``` ---