Skip to content

Commit 76f9991

Browse files
committed
Added RFC import-trait-methods
1 parent 6591585 commit 76f9991

File tree

1 file changed

+199
-0
lines changed

1 file changed

+199
-0
lines changed

text/0000-import-trait-methods.md

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
- Feature Name: `import-trait-methods`
2+
- Start Date: 2024-03-19
3+
- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000)
4+
- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
Allow importing methods from traits and then using them like regular functions.
10+
11+
# Motivation
12+
[motivation]: #motivation
13+
14+
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.
15+
16+
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.
17+
18+
# Guide-level explanation
19+
[guide-level-explanation]: #guide-level-explanation
20+
21+
Importing a method from a trait is the same as importing a method from any module:
22+
```rust
23+
use Default::default;
24+
```
25+
26+
Once you have done this, the method is made available in your current scope just like any other regular function.
27+
28+
```rust
29+
struct S {
30+
a: HashMap<i32, i32>,
31+
}
32+
33+
impl S {
34+
fn new() -> S {
35+
S {
36+
a: default()
37+
}
38+
}
39+
}
40+
```
41+
42+
You can also use this with trait methods that take a `self` argument:
43+
44+
```rust
45+
use num_traits::float::Float::{sin, cos}
46+
47+
fn eulers_formula(theta: f64) -> (f64, f64) {
48+
(cos(theta), sin(theta))
49+
}
50+
```
51+
52+
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:
53+
54+
```rust
55+
mod a {
56+
trait A {
57+
fn new() -> Self;
58+
fn do_something(&self);
59+
}
60+
}
61+
62+
mod b {
63+
use super::a::A::{self, new}
64+
65+
struct B();
66+
67+
impl A for B {
68+
fn new() -> Self {
69+
B()
70+
}
71+
72+
fn do_something(&self) {
73+
}
74+
}
75+
76+
fn f() {
77+
let b: B = new();
78+
b.do_something();
79+
}
80+
}
81+
```
82+
83+
You cannot import a parent trait method from a sub-trait.
84+
85+
```rust
86+
use num_traits::float::Float::zero; // Error: try `use num_traits::identities::Zero::zero` instead.
87+
88+
fn main() {
89+
let x : f64 = zero();
90+
println!("{}",x);
91+
}
92+
```
93+
94+
# Reference-level explanation
95+
[reference-level-explanation]: #reference-level-explanation
96+
97+
When
98+
99+
```rust
100+
use Trait::method as m;
101+
```
102+
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:
103+
104+
```rust
105+
use Default::default;
106+
107+
struct S {
108+
a: HashMap<i32, i32>,
109+
}
110+
111+
impl S {
112+
fn new() -> S {
113+
S {
114+
a: default()
115+
}
116+
}
117+
}
118+
```
119+
desugars to
120+
```rust
121+
struct S {
122+
a: HashMap<i32, i32>,
123+
}
124+
125+
impl S {
126+
fn new() -> S {
127+
S {
128+
a: Default::default()
129+
}
130+
}
131+
}
132+
```
133+
And a call
134+
```rust
135+
use Trait::method as m;
136+
m(x, y, z);
137+
```
138+
desugars to
139+
```rust
140+
Trait::method(x, y, z);
141+
```
142+
143+
Additionally, the syntax
144+
```rust
145+
use Trait::self;
146+
```
147+
is sugar for
148+
```rust
149+
use Trait;
150+
```
151+
152+
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.
153+
154+
# Drawbacks
155+
[drawbacks]: #drawbacks
156+
157+
Calls to `default` are less explicit than calls to `Default::default`, likewise for any other trait.
158+
159+
# Rationale and alternatives
160+
[rationale-and-alternatives]: #rationale-and-alternatives
161+
162+
## Why is this design the best in the space of possible designs?
163+
164+
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.
165+
166+
## What other designs have been considered and what is the rationale for not choosing them?
167+
168+
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:
169+
170+
1. It does not - this may be unexpected, but maybe not
171+
2. It does - then `value.other_method_from_the_same_trait()` will work as well, this may be unexpected too
172+
3. It does, but only for method, that's something new for the language (need new more fine-grained tracking of traits in scope)
173+
174+
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.
175+
176+
## What is the impact of not doing this?
177+
178+
Users of the language continue to create helper methods to access trait methods with infix syntax.
179+
180+
## 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?
181+
182+
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.
183+
184+
# Prior art
185+
[prior-art]: #prior-art
186+
187+
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.
188+
189+
[This issue](https://github.com/rust-lang/rfcs/issues/1995) also lists some further motivation for this feature.
190+
191+
# Unresolved questions
192+
[unresolved-questions]: #unresolved-questions
193+
194+
- Is specifying this in terms of desugaring sufficient to give the desired semantics?
195+
196+
# Future possibilities
197+
[future-possibilities]: #future-possibilities
198+
199+
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.

0 commit comments

Comments
 (0)