From 76f999143ba8502ccff4721a7f2b8381d1bd0714 Mon Sep 17 00:00:00 2001 From: Josiah Bills Date: Tue, 19 Mar 2024 17:51:35 -0400 Subject: [PATCH 01/13] Added RFC import-trait-methods --- text/0000-import-trait-methods.md | 199 ++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 text/0000-import-trait-methods.md diff --git a/text/0000-import-trait-methods.md b/text/0000-import-trait-methods.md new file mode 100644 index 00000000000..f64ccc28e26 --- /dev/null +++ b/text/0000-import-trait-methods.md @@ -0,0 +1,199 @@ +- Feature Name: `import-trait-methods` +- Start Date: 2024-03-19 +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) + +# Summary +[summary]: #summary + +Allow importing methods from traits and then using them like regular functions. + +# Motivation +[motivation]: #motivation + +There has for a long time been a desire to shorten the duplication needed to access certain methods, such as `Default::default`. Codebases like [Bevy](https://github.com/bevyengine/bevy/blob/7c7d1e8a6442a4258896b6c605beb1bf50399396/crates/bevy_utils/src/default.rs#L27) provide wrapper methods to shorten this call, and a previous, now-rejected, [RFC](https://github.com/rust-lang/rust/pull/73001) aimed to provide this method as part of the standard library. This RFC was rejected with a note that there is a desire for a more general capability to import any trait method. + +Additionally, if you pull in a crate like [num_traits](https://docs.rs/num-traits/latest/num_traits/), then this feature will allow access to numeric methods such as `sin` using the infix syntax that is more common in mathematics. More generally, it will make infix calls to a trait method shorter without having to write a wrapper function. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +Importing a method from a trait is the same as importing a method from any module: +```rust +use Default::default; +``` + +Once you have done this, the method is made available in your current scope just like any other regular function. + +```rust +struct S { + a: HashMap, +} + +impl S { + fn new() -> S { + S { + a: default() + } + } +} +``` + +You can also use this with trait methods that take a `self` argument: + +```rust +use num_traits::float::Float::{sin, cos} + +fn eulers_formula(theta: f64) -> (f64, f64) { + (cos(theta), sin(theta)) +} +``` + +Importing a method from a trait does not import the trait. If you want to call `self` methods on a trait or `impl` it, then you can import the trait as well as methods in the trait: + +```rust +mod a { + trait A { + fn new() -> Self; + fn do_something(&self); + } +} + +mod b { + use super::a::A::{self, new} + + struct B(); + + impl A for B { + fn new() -> Self { + B() + } + + fn do_something(&self) { + } + } + + fn f() { + let b: B = new(); + b.do_something(); + } +} +``` + +You cannot import a parent trait method from a sub-trait. + +```rust +use num_traits::float::Float::zero; // Error: try `use num_traits::identities::Zero::zero` instead. + +fn main() { + let x : f64 = zero(); + println!("{}",x); +} +``` + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +When + +```rust +use Trait::method as m; +``` +occurs, a new item `m` is made available in the function namespace of the current module. Any attempts to call this item are treated calling the trait method explicitly qualified. As always, the `as` qualifier is optional, in which case the name of the new item is identical with the name of the method in the trait. In other words, the example: + +```rust +use Default::default; + +struct S { + a: HashMap, +} + +impl S { + fn new() -> S { + S { + a: default() + } + } +} +``` +desugars to +```rust +struct S { + a: HashMap, +} + +impl S { + fn new() -> S { + S { + a: Default::default() + } + } +} +``` +And a call +```rust +use Trait::method as m; +m(x, y, z); +``` +desugars to +```rust +Trait::method(x, y, z); +``` + +Additionally, the syntax +```rust +use Trait::self; +``` +is sugar for +```rust +use Trait; +``` + +The restriction on importing parent trait methods is a consequence of this desugaring, see https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=51bef9ba69ce1fc20248e987bf106bd4 for examples of the errors you get when you try to call parent trait methods through a child trait. We will likely want better error messages than this if a user tries to import a parent method. + +# Drawbacks +[drawbacks]: #drawbacks + +Calls to `default` are less explicit than calls to `Default::default`, likewise for any other trait. + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +## Why is this design the best in the space of possible designs? + +This design is minimalist, it adds no extra syntax, instead providing a natural extension of existing syntax to support a feature that is frequently requested. Users might very well already expect this feature, with this exact syntax, to be present in Rust, and surprised when it isn't. + +## What other designs have been considered and what is the rationale for not choosing them? + +In [Zulip](https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Writing.20an.20RFC.20for.20.60use.20Default.3A.3Adefault.60/near/427795694), there was some discussion of whether `use Trait::method` should bring `Trait` into scope. There are three possibilities: + +1. It does not - this may be unexpected, but maybe not +2. It does - then `value.other_method_from_the_same_trait()` will work as well, this may be unexpected too +3. It does, but only for method, that's something new for the language (need new more fine-grained tracking of traits in scope) + +Option 1 is what is proposed here. It has the simplest semantics, and I believe it best matches the user intent when they import a trait method; the desire is to make that method available as-if it were a regular function. Furthermore, it is more minimalist than the other two options in the sense that you can get to option 2 simply by importing the trait also. Option 3 seems like extra complexity for almost no added value. + +## What is the impact of not doing this? + +Users of the language continue to create helper methods to access trait methods with infix syntax. + +## If this is a language proposal, could this be done in a library or macro instead? Does the proposed change make Rust code easier or harder to read, understand, and maintain? + +A library solution has already been rejected for this. This solves the same problem as a library solution in a much more general way, that doesn't require adding new library methods every time we want infix/shorthand access to trait method names. + +# Prior art +[prior-art]: #prior-art + +As mentioned in [motivation], there was a rejected [RFC](https://github.com/rust-lang/rust/pull/73001) for adding a method `std::default::default` to make calling `Default::default` less repetitive. This RFC was rejected, with a desire to see something like what this RFC proposes replace it. + +[This issue](https://github.com/rust-lang/rfcs/issues/1995) also lists some further motivation for this feature. + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +- Is specifying this in terms of desugaring sufficient to give the desired semantics? + +# Future possibilities +[future-possibilities]: #future-possibilities + +This RFC does not propose the ability to import `Type::method` where `method` is contained in an `impl` block. Such a feature would be a natural extension of this work, and would enable numeric features like that discussed in [motivation] without the need for the [num_traits](https://docs.rs/num-traits/latest/num_traits/) crate. This feature is not proposed in this RFC since initial investigations revealed that it would be [difficult](https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Writing.20an.20RFC.20for.20.60use.20Default.3A.3Adefault.60/near/427804375) to implement in today's rustc. From 9737ae5c7093f5220566aea65f50ccae081509d0 Mon Sep 17 00:00:00 2001 From: Josiah Bills Date: Tue, 19 Mar 2024 18:35:41 -0400 Subject: [PATCH 02/13] Added information about import renaming to the guide level explanation. --- text/0000-import-trait-methods.md | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/text/0000-import-trait-methods.md b/text/0000-import-trait-methods.md index f64ccc28e26..2f8cc8a6128 100644 --- a/text/0000-import-trait-methods.md +++ b/text/0000-import-trait-methods.md @@ -80,7 +80,24 @@ mod b { } ``` -You cannot import a parent trait method from a sub-trait. +Trait methods can also be renamed when they are imported using the usual `as` syntax: +```rust +use Default::default as gimme + +struct S { + a: HashMap, +} + +impl S { + fn new() -> S { + S { + a: gimme() + } + } +} +``` + +You cannot import a parent trait method from a sub-trait: ```rust use num_traits::float::Float::zero; // Error: try `use num_traits::identities::Zero::zero` instead. @@ -142,11 +159,11 @@ Trait::method(x, y, z); Additionally, the syntax ```rust -use Trait::self; +use some_module::Trait::self; ``` is sugar for ```rust -use Trait; +use some_module::Trait; ``` The restriction on importing parent trait methods is a consequence of this desugaring, see https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=51bef9ba69ce1fc20248e987bf106bd4 for examples of the errors you get when you try to call parent trait methods through a child trait. We will likely want better error messages than this if a user tries to import a parent method. From 98834a47d65a0a83b74146d5adb7ec2f08dc9df5 Mon Sep 17 00:00:00 2001 From: Josiah Bills Date: Wed, 20 Mar 2024 04:44:45 -0400 Subject: [PATCH 03/13] Altered RFC to allow super-trait imports. --- text/0000-import-trait-methods.md | 32 ++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/text/0000-import-trait-methods.md b/text/0000-import-trait-methods.md index 2f8cc8a6128..346d927bf93 100644 --- a/text/0000-import-trait-methods.md +++ b/text/0000-import-trait-methods.md @@ -53,7 +53,7 @@ Importing a method from a trait does not import the trait. If you want to call ` ```rust mod a { - trait A { + pub trait A { fn new() -> Self; fn do_something(&self); } @@ -97,10 +97,10 @@ impl S { } ``` -You cannot import a parent trait method from a sub-trait: +You can also import a parent trait method from a sub-trait: ```rust -use num_traits::float::Float::zero; // Error: try `use num_traits::identities::Zero::zero` instead. +use num_traits::float::Float::zero; fn main() { let x : f64 = zero(); @@ -116,7 +116,8 @@ When ```rust use Trait::method as m; ``` -occurs, a new item `m` is made available in the function namespace of the current module. Any attempts to call this item are treated calling the trait method explicitly qualified. As always, the `as` qualifier is optional, in which case the name of the new item is identical with the name of the method in the trait. In other words, the example: +occurs, we first find the supertrait in which `method` occurs. +A new item `m` is made available in the function namespace of the current module. Any attempts to call this item are treated calling the (super-)trait method explicitly qualified. As always, the `as` qualifier is optional, in which case the name of the new item is identical with the name of the method in the trait. In other words, the example: ```rust use Default::default; @@ -166,7 +167,26 @@ is sugar for use some_module::Trait; ``` -The restriction on importing parent trait methods is a consequence of this desugaring, see https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=51bef9ba69ce1fc20248e987bf106bd4 for examples of the errors you get when you try to call parent trait methods through a child trait. We will likely want better error messages than this if a user tries to import a parent method. +Finally, given traits +```rust +trait Super { + fn f(); +} + +trait Sub : Super { +} +``` +the usage +```rust +use module::Sub::f; +f(); +``` +desugars to +```rust +use module::Sub::f; +Super::f(); +``` +**not** `Sub::f();` as that desugaring will cause compiler errors, see https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=51bef9ba69ce1fc20248e987bf106bd4. # Drawbacks [drawbacks]: #drawbacks @@ -190,6 +210,8 @@ In [Zulip](https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Wr Option 1 is what is proposed here. It has the simplest semantics, and I believe it best matches the user intent when they import a trait method; the desire is to make that method available as-if it were a regular function. Furthermore, it is more minimalist than the other two options in the sense that you can get to option 2 simply by importing the trait also. Option 3 seems like extra complexity for almost no added value. +An earlier version of this RFC proposed not allowing `use Trait::super_trait_method`. This was changed because comments indicated this usecase would be common. + ## What is the impact of not doing this? Users of the language continue to create helper methods to access trait methods with infix syntax. From d40c696dc19694d1d9fe5168ea9b2ddc86ed5a35 Mon Sep 17 00:00:00 2001 From: Josiah Bills Date: Fri, 22 Mar 2024 00:28:02 -0400 Subject: [PATCH 04/13] Addressed RFC review comments. --- text/0000-import-trait-methods.md | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/text/0000-import-trait-methods.md b/text/0000-import-trait-methods.md index 346d927bf93..9cd1d08a794 100644 --- a/text/0000-import-trait-methods.md +++ b/text/0000-import-trait-methods.md @@ -13,7 +13,7 @@ Allow importing methods from traits and then using them like regular functions. There has for a long time been a desire to shorten the duplication needed to access certain methods, such as `Default::default`. Codebases like [Bevy](https://github.com/bevyengine/bevy/blob/7c7d1e8a6442a4258896b6c605beb1bf50399396/crates/bevy_utils/src/default.rs#L27) provide wrapper methods to shorten this call, and a previous, now-rejected, [RFC](https://github.com/rust-lang/rust/pull/73001) aimed to provide this method as part of the standard library. This RFC was rejected with a note that there is a desire for a more general capability to import any trait method. -Additionally, if you pull in a crate like [num_traits](https://docs.rs/num-traits/latest/num_traits/), then this feature will allow access to numeric methods such as `sin` using the infix syntax that is more common in mathematics. More generally, it will make infix calls to a trait method shorter without having to write a wrapper function. +Additionally, if you pull in a crate like [num_traits](https://docs.rs/num-traits/latest/num_traits/), then this feature will allow access to numeric methods such as `sin` using the `sin(x)` syntax that is more common in mathematics. More generally, it will make calls to trait methods shorter without having to write a wrapper function. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation @@ -116,8 +116,10 @@ When ```rust use Trait::method as m; ``` -occurs, we first find the supertrait in which `method` occurs. -A new item `m` is made available in the function namespace of the current module. Any attempts to call this item are treated calling the (super-)trait method explicitly qualified. As always, the `as` qualifier is optional, in which case the name of the new item is identical with the name of the method in the trait. In other words, the example: +occurs, we first find the supertrait in which `method` occurs. If it occurs in multiple supertraits, or in the trait and in a supertrait, then error. +A new item `m` is made available in the function namespace of the current module. Any attempts to call this item are treated calling the (super-)trait method explicitly qualified. As always, the `as` qualifier is optional, in which case the name of the new item is identical with the name of the method in the trait. + +In other words, the example: ```rust use Default::default; @@ -191,7 +193,14 @@ Super::f(); # Drawbacks [drawbacks]: #drawbacks -Calls to `default` are less explicit than calls to `Default::default`, likewise for any other trait. +Calls to `default` are less explicit than calls to `Default::default` or to `T::default`, likewise for any other trait. Some users may see this lack of explicitness as bad style. + +To expand on this, [the book](https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#creating-idiomatic-use-paths) currently recommends that methods should be called using their parent module's name: +> Although both Listing 7-11 and 7-13 accomplish the same task, Listing 7-11 is the idiomatic way to bring a function into scope with use. Bringing the function’s parent module into scope with use means we have to specify the parent module when calling the function. Specifying the parent module when calling the function makes it clear that the function isn’t locally defined while still minimizing repetition of the full path. + +This recommendation makes the most sense when there is a possibility of ambiguity in the mind of the reader. For example, a function like `sin` is unlikely to be ambiguous, because there is only one mathematical function of that name. If a codebase is likely to be making use of multiple different implementations of `sin`, then it makes more sense to require specifically naming the one you are going to use. Similar considerations apply to traits like `Default::default`, or more generally in cases like `Frobnicator::frobnicate`. + +Because of this context sensitivity, we should allow developers to choose when removing the extra context makes sense for their codebase. # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives @@ -214,11 +223,17 @@ An earlier version of this RFC proposed not allowing `use Trait::super_trait_met ## What is the impact of not doing this? -Users of the language continue to create helper methods to access trait methods with infix syntax. +Users of the language continue to create helper methods to access trait methods with regular function syntax. More specifically, each such instance requires a minimum of three lines when using normal rust formatting, corresponding to the following example: +```rust +fn my_trait_method(args) -> ret { + MyTrait::my_trait_method(args) +} +``` +Such code is boilerplate that serves nobody's time to have to write repeatedly. ## If this is a language proposal, could this be done in a library or macro instead? Does the proposed change make Rust code easier or harder to read, understand, and maintain? -A library solution has already been rejected for this. This solves the same problem as a library solution in a much more general way, that doesn't require adding new library methods every time we want infix/shorthand access to trait method names. +A library solution has already been rejected for this. This solves the same problem as a library solution in a much more general way, that doesn't require adding new library methods every time we want shorthand access to trait method names. # Prior art [prior-art]: #prior-art From e125c716518b66c04f1256b2ed3324866802a7c0 Mon Sep 17 00:00:00 2001 From: Josiah Bills Date: Sat, 30 Mar 2024 22:16:36 -0400 Subject: [PATCH 05/13] Revert "Altered RFC to allow super-trait imports." This reverts commit 98834a47d65a0a83b74146d5adb7ec2f08dc9df5. --- text/0000-import-trait-methods.md | 34 +++++-------------------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/text/0000-import-trait-methods.md b/text/0000-import-trait-methods.md index 9cd1d08a794..e2e840f7d20 100644 --- a/text/0000-import-trait-methods.md +++ b/text/0000-import-trait-methods.md @@ -53,7 +53,7 @@ Importing a method from a trait does not import the trait. If you want to call ` ```rust mod a { - pub trait A { + trait A { fn new() -> Self; fn do_something(&self); } @@ -97,10 +97,10 @@ impl S { } ``` -You can also import a parent trait method from a sub-trait: +You cannot import a parent trait method from a sub-trait: ```rust -use num_traits::float::Float::zero; +use num_traits::float::Float::zero; // Error: try `use num_traits::identities::Zero::zero` instead. fn main() { let x : f64 = zero(); @@ -116,10 +116,7 @@ When ```rust use Trait::method as m; ``` -occurs, we first find the supertrait in which `method` occurs. If it occurs in multiple supertraits, or in the trait and in a supertrait, then error. -A new item `m` is made available in the function namespace of the current module. Any attempts to call this item are treated calling the (super-)trait method explicitly qualified. As always, the `as` qualifier is optional, in which case the name of the new item is identical with the name of the method in the trait. - -In other words, the example: +occurs, a new item `m` is made available in the function namespace of the current module. Any attempts to call this item are treated calling the trait method explicitly qualified. As always, the `as` qualifier is optional, in which case the name of the new item is identical with the name of the method in the trait. In other words, the example: ```rust use Default::default; @@ -169,26 +166,7 @@ is sugar for use some_module::Trait; ``` -Finally, given traits -```rust -trait Super { - fn f(); -} - -trait Sub : Super { -} -``` -the usage -```rust -use module::Sub::f; -f(); -``` -desugars to -```rust -use module::Sub::f; -Super::f(); -``` -**not** `Sub::f();` as that desugaring will cause compiler errors, see https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=51bef9ba69ce1fc20248e987bf106bd4. +The restriction on importing parent trait methods is a consequence of this desugaring, see https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=51bef9ba69ce1fc20248e987bf106bd4 for examples of the errors you get when you try to call parent trait methods through a child trait. We will likely want better error messages than this if a user tries to import a parent method. # Drawbacks [drawbacks]: #drawbacks @@ -219,8 +197,6 @@ In [Zulip](https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Wr Option 1 is what is proposed here. It has the simplest semantics, and I believe it best matches the user intent when they import a trait method; the desire is to make that method available as-if it were a regular function. Furthermore, it is more minimalist than the other two options in the sense that you can get to option 2 simply by importing the trait also. Option 3 seems like extra complexity for almost no added value. -An earlier version of this RFC proposed not allowing `use Trait::super_trait_method`. This was changed because comments indicated this usecase would be common. - ## What is the impact of not doing this? Users of the language continue to create helper methods to access trait methods with regular function syntax. More specifically, each such instance requires a minimum of three lines when using normal rust formatting, corresponding to the following example: From 336841470ea76471d77c3ca5280c97ff094dc678 Mon Sep 17 00:00:00 2001 From: Josiah Bills Date: Sat, 30 Mar 2024 22:27:05 -0400 Subject: [PATCH 06/13] More precisely distinguish methods from functions and associated functions. --- text/0000-import-trait-methods.md | 50 +++++++++++++++---------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/text/0000-import-trait-methods.md b/text/0000-import-trait-methods.md index e2e840f7d20..4f5f91f89e3 100644 --- a/text/0000-import-trait-methods.md +++ b/text/0000-import-trait-methods.md @@ -1,4 +1,4 @@ -- Feature Name: `import-trait-methods` +- Feature Name: `import-trait-associated-functions` - Start Date: 2024-03-19 - RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) @@ -6,24 +6,24 @@ # Summary [summary]: #summary -Allow importing methods from traits and then using them like regular functions. +Allow importing associated functions from traits and then using them like regular functions. # Motivation [motivation]: #motivation -There has for a long time been a desire to shorten the duplication needed to access certain methods, such as `Default::default`. Codebases like [Bevy](https://github.com/bevyengine/bevy/blob/7c7d1e8a6442a4258896b6c605beb1bf50399396/crates/bevy_utils/src/default.rs#L27) provide wrapper methods to shorten this call, and a previous, now-rejected, [RFC](https://github.com/rust-lang/rust/pull/73001) aimed to provide this method as part of the standard library. This RFC was rejected with a note that there is a desire for a more general capability to import any trait method. +There has for a long time been a desire to shorten the duplication needed to access certain associated functions, such as `Default::default`. Codebases like [Bevy](https://github.com/bevyengine/bevy/blob/7c7d1e8a6442a4258896b6c605beb1bf50399396/crates/bevy_utils/src/default.rs#L27) provide wrapper functions to shorten this call, and a previous, now-rejected, [RFC](https://github.com/rust-lang/rust/pull/73001) aimed to provide this function as part of the standard library. This RFC was rejected with a note that there is a desire for a more general capability to import any trait associated functions. -Additionally, if you pull in a crate like [num_traits](https://docs.rs/num-traits/latest/num_traits/), then this feature will allow access to numeric methods such as `sin` using the `sin(x)` syntax that is more common in mathematics. More generally, it will make calls to trait methods shorter without having to write a wrapper function. +Additionally, if you pull in a crate like [num_traits](https://docs.rs/num-traits/latest/num_traits/), then this feature will allow access to numeric functions such as `sin` using the `sin(x)` syntax that is more common in mathematics. More generally, it will make calls to trait associated functions shorter without having to write a wrapper function. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation -Importing a method from a trait is the same as importing a method from any module: +Importing an associated functions from a trait is the same as importing a function from any module: ```rust use Default::default; ``` -Once you have done this, the method is made available in your current scope just like any other regular function. +Once you have done this, the function is made available in your current scope just like any other regular function. ```rust struct S { @@ -39,7 +39,7 @@ impl S { } ``` -You can also use this with trait methods that take a `self` argument: +You can also use this with trait methods (i.e. associated functions that take a `self` argument): ```rust use num_traits::float::Float::{sin, cos} @@ -49,7 +49,7 @@ fn eulers_formula(theta: f64) -> (f64, f64) { } ``` -Importing a method from a trait does not import the trait. If you want to call `self` methods on a trait or `impl` it, then you can import the trait as well as methods in the trait: +Importing an associated function from a trait does not import the trait. If you want to call `self` methods on a trait or `impl` it, then you can import the trait and its associated functions in a single import statement: ```rust mod a { @@ -80,7 +80,7 @@ mod b { } ``` -Trait methods can also be renamed when they are imported using the usual `as` syntax: +Associated functions can also be renamed when they are imported using the usual `as` syntax: ```rust use Default::default as gimme @@ -97,7 +97,7 @@ impl S { } ``` -You cannot import a parent trait method from a sub-trait: +You cannot import a parent trait associated function from a sub-trait: ```rust use num_traits::float::Float::zero; // Error: try `use num_traits::identities::Zero::zero` instead. @@ -114,9 +114,9 @@ fn main() { When ```rust -use Trait::method as m; +use Trait::func as m; ``` -occurs, a new item `m` is made available in the function namespace of the current module. Any attempts to call this item are treated calling the trait method explicitly qualified. As always, the `as` qualifier is optional, in which case the name of the new item is identical with the name of the method in the trait. In other words, the example: +occurs, a new item `m` is made available in the function namespace of the current module. Any attempts to call this item are treated calling the associated function explicitly qualified. As always, the `as` qualifier is optional, in which case the name of the new item is identical with the name of the associated function in the trait. In other words, the example: ```rust use Default::default; @@ -149,12 +149,12 @@ impl S { ``` And a call ```rust -use Trait::method as m; +use Trait::func as m; m(x, y, z); ``` desugars to ```rust -Trait::method(x, y, z); +Trait::func(x, y, z); ``` Additionally, the syntax @@ -166,14 +166,14 @@ is sugar for use some_module::Trait; ``` -The restriction on importing parent trait methods is a consequence of this desugaring, see https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=51bef9ba69ce1fc20248e987bf106bd4 for examples of the errors you get when you try to call parent trait methods through a child trait. We will likely want better error messages than this if a user tries to import a parent method. +The restriction on importing parent trait associated functions is a consequence of this desugaring, see https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=51bef9ba69ce1fc20248e987bf106bd4 for examples of the errors you get when you try to call parent trait associated functions through a child trait. We will likely want better error messages than this if a user tries to import a parent function. # Drawbacks [drawbacks]: #drawbacks Calls to `default` are less explicit than calls to `Default::default` or to `T::default`, likewise for any other trait. Some users may see this lack of explicitness as bad style. -To expand on this, [the book](https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#creating-idiomatic-use-paths) currently recommends that methods should be called using their parent module's name: +To expand on this, [the book](https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#creating-idiomatic-use-paths) currently recommends that functions should be called using their parent module's name: > Although both Listing 7-11 and 7-13 accomplish the same task, Listing 7-11 is the idiomatic way to bring a function into scope with use. Bringing the function’s parent module into scope with use means we have to specify the parent module when calling the function. Specifying the parent module when calling the function makes it clear that the function isn’t locally defined while still minimizing repetition of the full path. This recommendation makes the most sense when there is a possibility of ambiguity in the mind of the reader. For example, a function like `sin` is unlikely to be ambiguous, because there is only one mathematical function of that name. If a codebase is likely to be making use of multiple different implementations of `sin`, then it makes more sense to require specifically naming the one you are going to use. Similar considerations apply to traits like `Default::default`, or more generally in cases like `Frobnicator::frobnicate`. @@ -189,32 +189,32 @@ This design is minimalist, it adds no extra syntax, instead providing a natural ## What other designs have been considered and what is the rationale for not choosing them? -In [Zulip](https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Writing.20an.20RFC.20for.20.60use.20Default.3A.3Adefault.60/near/427795694), there was some discussion of whether `use Trait::method` should bring `Trait` into scope. There are three possibilities: +In [Zulip](https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Writing.20an.20RFC.20for.20.60use.20Default.3A.3Adefault.60/near/427795694), there was some discussion of whether `use Trait::func` should bring `Trait` into scope. There are three possibilities: 1. It does not - this may be unexpected, but maybe not -2. It does - then `value.other_method_from_the_same_trait()` will work as well, this may be unexpected too -3. It does, but only for method, that's something new for the language (need new more fine-grained tracking of traits in scope) +2. It does - then `value.other_func_from_the_same_trait()` will work as well, this may be unexpected too +3. It does, but only for `func`, that's something new for the language (need new more fine-grained tracking of traits in scope) -Option 1 is what is proposed here. It has the simplest semantics, and I believe it best matches the user intent when they import a trait method; the desire is to make that method available as-if it were a regular function. Furthermore, it is more minimalist than the other two options in the sense that you can get to option 2 simply by importing the trait also. Option 3 seems like extra complexity for almost no added value. +Option 1 is what is proposed here. It has the simplest semantics, and I believe it best matches the user intent when they import an associated function; the desire is to make that function available as-if it were a regular function. Furthermore, it is more minimalist than the other two options in the sense that you can get to option 2 simply by importing the trait also. Option 3 seems like extra complexity for almost no added value. ## What is the impact of not doing this? -Users of the language continue to create helper methods to access trait methods with regular function syntax. More specifically, each such instance requires a minimum of three lines when using normal rust formatting, corresponding to the following example: +Users of the language continue to create helper functions to access associated with regular function syntax. More specifically, each such instance requires a minimum of three lines when using normal rust formatting, corresponding to the following example: ```rust -fn my_trait_method(args) -> ret { - MyTrait::my_trait_method(args) +fn my_trait_func(args) -> ret { + MyTrait::my_trait_func(args) } ``` Such code is boilerplate that serves nobody's time to have to write repeatedly. ## If this is a language proposal, could this be done in a library or macro instead? Does the proposed change make Rust code easier or harder to read, understand, and maintain? -A library solution has already been rejected for this. This solves the same problem as a library solution in a much more general way, that doesn't require adding new library methods every time we want shorthand access to trait method names. +A library solution has already been rejected for this. This solves the same problem as a library solution in a much more general way, that doesn't require adding new library functions every time we want shorthand access to trait function names. # Prior art [prior-art]: #prior-art -As mentioned in [motivation], there was a rejected [RFC](https://github.com/rust-lang/rust/pull/73001) for adding a method `std::default::default` to make calling `Default::default` less repetitive. This RFC was rejected, with a desire to see something like what this RFC proposes replace it. +As mentioned in [motivation], there was a rejected [RFC](https://github.com/rust-lang/rust/pull/73001) for adding a function `std::default::default` to make calling `Default::default` less repetitive. This RFC was rejected, with a desire to see something like what this RFC proposes replace it. [This issue](https://github.com/rust-lang/rfcs/issues/1995) also lists some further motivation for this feature. From 001f3e160ba5bb2f34881f664b78c98791058e9c Mon Sep 17 00:00:00 2001 From: Josiah Bills Date: Sat, 30 Mar 2024 22:27:48 -0400 Subject: [PATCH 07/13] Renamed RFC to match clarifications in previous commit. --- ...trait-methods.md => 0000-import-trait-associated-functions.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename text/{0000-import-trait-methods.md => 0000-import-trait-associated-functions.md} (100%) diff --git a/text/0000-import-trait-methods.md b/text/0000-import-trait-associated-functions.md similarity index 100% rename from text/0000-import-trait-methods.md rename to text/0000-import-trait-associated-functions.md From 1303392a47d1a8d59aa27a2a9f5d694f74c4f5d2 Mon Sep 17 00:00:00 2001 From: Josiah Bills Date: Sat, 30 Mar 2024 22:33:49 -0400 Subject: [PATCH 08/13] Addressed a couple more feedback items. --- text/0000-import-trait-associated-functions.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/text/0000-import-trait-associated-functions.md b/text/0000-import-trait-associated-functions.md index 4f5f91f89e3..f99b6dc9858 100644 --- a/text/0000-import-trait-associated-functions.md +++ b/text/0000-import-trait-associated-functions.md @@ -116,7 +116,7 @@ When ```rust use Trait::func as m; ``` -occurs, a new item `m` is made available in the function namespace of the current module. Any attempts to call this item are treated calling the associated function explicitly qualified. As always, the `as` qualifier is optional, in which case the name of the new item is identical with the name of the associated function in the trait. In other words, the example: +occurs, a new item `m` is made available in the value namespace of the current module. Any attempts to call this item are treated calling the associated function explicitly qualified. As always, the `as` qualifier is optional, in which case the name of the new item is identical with the name of the associated function in the trait. In other words, the example: ```rust use Default::default; @@ -165,6 +165,10 @@ is sugar for ```rust use some_module::Trait; ``` +to allow importing a trait and its methods at the same time, e.g: +```rust +use Default::{self, default}; +``` The restriction on importing parent trait associated functions is a consequence of this desugaring, see https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=51bef9ba69ce1fc20248e987bf106bd4 for examples of the errors you get when you try to call parent trait associated functions through a child trait. We will likely want better error messages than this if a user tries to import a parent function. From 021681c8fd5cfb303ce0d4d85125294b98af2786 Mon Sep 17 00:00:00 2001 From: Josiah Bills Date: Sat, 30 Mar 2024 22:56:09 -0400 Subject: [PATCH 09/13] Added discussion on complexity. --- text/0000-import-trait-associated-functions.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/0000-import-trait-associated-functions.md b/text/0000-import-trait-associated-functions.md index f99b6dc9858..82bf415487c 100644 --- a/text/0000-import-trait-associated-functions.md +++ b/text/0000-import-trait-associated-functions.md @@ -184,6 +184,8 @@ This recommendation makes the most sense when there is a possibility of ambiguit Because of this context sensitivity, we should allow developers to choose when removing the extra context makes sense for their codebase. +Another drawback mentioned during review for this RFC was that this adds more complication to the name resolution rules. On an implementation side, I am assured that this feature is straightforward to implement. From a user perspective, the name lookup rules for the function name are exactly the same as those used to look up any other function name. The lookup rules used to resolve the `impl` are also exactly the same ones used for non-fully qualified trait function calls. There is no fundamentally new kind of lookup happening here, just a remixing of existing lookup rules. + # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives From f8cb64c6eb6fef08ab0038aca0cf7d113b6fc99c Mon Sep 17 00:00:00 2001 From: Josiah Bills Date: Mon, 27 May 2024 12:17:37 -0400 Subject: [PATCH 10/13] Added note about importing parent traits. --- text/0000-import-trait-associated-functions.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/0000-import-trait-associated-functions.md b/text/0000-import-trait-associated-functions.md index 82bf415487c..c62f3e99c99 100644 --- a/text/0000-import-trait-associated-functions.md +++ b/text/0000-import-trait-associated-functions.md @@ -203,6 +203,8 @@ In [Zulip](https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Wr Option 1 is what is proposed here. It has the simplest semantics, and I believe it best matches the user intent when they import an associated function; the desire is to make that function available as-if it were a regular function. Furthermore, it is more minimalist than the other two options in the sense that you can get to option 2 simply by importing the trait also. Option 3 seems like extra complexity for almost no added value. +We considered allowing `use Trait::parent_method`, but decided against it, as you can always explicitly import from the parent instead. + ## What is the impact of not doing this? Users of the language continue to create helper functions to access associated with regular function syntax. More specifically, each such instance requires a minimum of three lines when using normal rust formatting, corresponding to the following example: @@ -233,3 +235,5 @@ As mentioned in [motivation], there was a rejected [RFC](https://github.com/rust [future-possibilities]: #future-possibilities This RFC does not propose the ability to import `Type::method` where `method` is contained in an `impl` block. Such a feature would be a natural extension of this work, and would enable numeric features like that discussed in [motivation] without the need for the [num_traits](https://docs.rs/num-traits/latest/num_traits/) crate. This feature is not proposed in this RFC since initial investigations revealed that it would be [difficult](https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Writing.20an.20RFC.20for.20.60use.20Default.3A.3Adefault.60/near/427804375) to implement in today's rustc. + +If we add a compatibility mechanism to implement a supertrait method when implementing its subtrait, without having to separately implement the supertrait (such that a new supertrait can be extracted from a trait without breaking compatibility), we would also need to lift the limitation on using a supertrait method via a subtrait. \ No newline at end of file From ff679c005c57373397275352760a26aea6918727 Mon Sep 17 00:00:00 2001 From: Josiah Bills Date: Mon, 27 May 2024 12:18:34 -0400 Subject: [PATCH 11/13] Fixed typo --- text/0000-import-trait-associated-functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-import-trait-associated-functions.md b/text/0000-import-trait-associated-functions.md index c62f3e99c99..a796f3b58c6 100644 --- a/text/0000-import-trait-associated-functions.md +++ b/text/0000-import-trait-associated-functions.md @@ -116,7 +116,7 @@ When ```rust use Trait::func as m; ``` -occurs, a new item `m` is made available in the value namespace of the current module. Any attempts to call this item are treated calling the associated function explicitly qualified. As always, the `as` qualifier is optional, in which case the name of the new item is identical with the name of the associated function in the trait. In other words, the example: +occurs, a new item `m` is made available in the value namespace of the current module. Any attempts to call this item are treated as calling the associated function explicitly qualified. As always, the `as` qualifier is optional, in which case the name of the new item is identical with the name of the associated function in the trait. In other words, the example: ```rust use Default::default; From 6d1c8f2ee1912051d70f0778f3261d184796329e Mon Sep 17 00:00:00 2001 From: Josiah Bills Date: Mon, 27 May 2024 12:22:59 -0400 Subject: [PATCH 12/13] Addressed some review comments. --- text/0000-import-trait-associated-functions.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/text/0000-import-trait-associated-functions.md b/text/0000-import-trait-associated-functions.md index a796f3b58c6..6478ae93d77 100644 --- a/text/0000-import-trait-associated-functions.md +++ b/text/0000-import-trait-associated-functions.md @@ -159,15 +159,12 @@ Trait::func(x, y, z); Additionally, the syntax ```rust -use some_module::Trait::self; +use Trait::{self, func}; ``` is sugar for ```rust use some_module::Trait; -``` -to allow importing a trait and its methods at the same time, e.g: -```rust -use Default::{self, default}; +use some_module::Trait::func; ``` The restriction on importing parent trait associated functions is a consequence of this desugaring, see https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=51bef9ba69ce1fc20248e987bf106bd4 for examples of the errors you get when you try to call parent trait associated functions through a child trait. We will likely want better error messages than this if a user tries to import a parent function. @@ -184,7 +181,7 @@ This recommendation makes the most sense when there is a possibility of ambiguit Because of this context sensitivity, we should allow developers to choose when removing the extra context makes sense for their codebase. -Another drawback mentioned during review for this RFC was that this adds more complication to the name resolution rules. On an implementation side, I am assured that this feature is straightforward to implement. From a user perspective, the name lookup rules for the function name are exactly the same as those used to look up any other function name. The lookup rules used to resolve the `impl` are also exactly the same ones used for non-fully qualified trait function calls. There is no fundamentally new kind of lookup happening here, just a remixing of existing lookup rules. +Another drawback mentioned during review for this RFC was that this adds more complication to the name resolution rules. On an implementation side, I am assured that this feature is straightforward to implement in rustc. From a user perspective, the name lookup rules for the function name are exactly the same as those used to look up any other function name. The lookup rules used to resolve the `impl` are also exactly the same ones used for non-fully qualified trait function calls. There is no fundamentally new kind of lookup happening here, just a remixing of existing lookup rules. # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives From 3f17ff8ec8ab6a5bd39a972c8ea70709956ca0f8 Mon Sep 17 00:00:00 2001 From: Josiah Bills Date: Mon, 27 May 2024 12:45:07 -0400 Subject: [PATCH 13/13] First pass at adding associated constants to the RFC. --- .../0000-import-trait-associated-functions.md | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/text/0000-import-trait-associated-functions.md b/text/0000-import-trait-associated-functions.md index 6478ae93d77..555bebe8aeb 100644 --- a/text/0000-import-trait-associated-functions.md +++ b/text/0000-import-trait-associated-functions.md @@ -6,7 +6,7 @@ # Summary [summary]: #summary -Allow importing associated functions from traits and then using them like regular functions. +Allow importing associated functions and constants from traits and then using them like regular items. # Motivation [motivation]: #motivation @@ -15,6 +15,8 @@ There has for a long time been a desire to shorten the duplication needed to acc Additionally, if you pull in a crate like [num_traits](https://docs.rs/num-traits/latest/num_traits/), then this feature will allow access to numeric functions such as `sin` using the `sin(x)` syntax that is more common in mathematics. More generally, it will make calls to trait associated functions shorter without having to write a wrapper function. +Similarly, associated constants, which act much like constant functions, can be imported to give easier access to them. + # Guide-level explanation [guide-level-explanation]: #guide-level-explanation @@ -108,15 +110,33 @@ fn main() { } ``` +Importing an associated constant is allowed too: +```rust +mod m { + trait MyNumTrait { + const ZERO: Self; + const ONE: Self; + } + + // Impl for every numeric type... +} + +use m::MyNumTrait::ZERO; + +fn f() -> u32 { + ZERO +} +``` + # Reference-level explanation [reference-level-explanation]: #reference-level-explanation When ```rust -use Trait::func as m; +use Trait::item as m; ``` -occurs, a new item `m` is made available in the value namespace of the current module. Any attempts to call this item are treated as calling the associated function explicitly qualified. As always, the `as` qualifier is optional, in which case the name of the new item is identical with the name of the associated function in the trait. In other words, the example: +occurs, a new item `m` is made available in the value namespace of the current module. Any attempts to use this item are treated as using the associated item explicitly qualified. `item` must be either an associated function or an associated constant. As always, the `as` qualifier is optional, in which case the name of the new item is identical with the name of the associated item in the trait. In other words, the example: ```rust use Default::default;