Skip to content

Commit 288f611

Browse files
authored
Merge pull request #251 from rylev/prelude
Add Rust 2021 prelude migration details
2 parents 76c51bd + 91cb3eb commit 288f611

File tree

1 file changed

+134
-10
lines changed

1 file changed

+134
-10
lines changed

src/rust-2021/prelude.md

Lines changed: 134 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
## Summary
44

5-
- `TryInto`, `TryFrom` and `FromIterator` are now part of the prelude.
6-
- This might change the meaning of e.g. `x.try_into()` depending on types and imports.
5+
- The `TryInto`, `TryFrom` and `FromIterator` traits are now part of the prelude.
6+
- This might make calls to trait methods ambiguous which could make some code fail to compile.
77

88
## Details
99

@@ -17,12 +17,11 @@ For example, if you have a crate or module called `example` containing a `pub st
1717
then `use example::*;` will make `Option` unambiguously refer to the one from `example`;
1818
not the one from the standard library.
1919

20-
However, adding a *trait* to the prelude can break existing code in a subtle way.
21-
A call to `x.try_into()` using a `MyTryInto` trait might become ambiguous and
22-
fail to compile if `std`'s `TryInto` is also imported,
23-
since it provides a method with the same name.
24-
This is the reason we haven't added `TryInto` to the prelude yet,
25-
since there is a lot of code that would break this way.
20+
However, adding a _trait_ to the prelude can break existing code in a subtle way.
21+
For example, a call to `x.try_into()` which comes from a `MyTryInto` trait might fail
22+
to compile if `std`'s `TryInto` is also imported, because the call to `try_into` is now
23+
ambiguous and could come from either trait. This is the reason we haven't added `TryInto`
24+
to the prelude yet, since there is a lot of code that would break this way.
2625

2726
As a solution, Rust 2021 will use a new prelude.
2827
It's identical to the current one, except for three new additions:
@@ -31,5 +30,130 @@ It's identical to the current one, except for three new additions:
3130
- [`std::convert::TryFrom`](https://doc.rust-lang.org/stable/std/convert/trait.TryFrom.html)
3231
- [`std::iter::FromIterator`](https://doc.rust-lang.org/stable/std/iter/trait.FromIterator.html)
3332

34-
The library team still needs to formally approve these, which will likely happen soon.
35-
<!-- TODO: hopefully this happens before we publish this -->
33+
The tracking issue [can be found here](https://github.com/rust-lang/rust/issues/85684).
34+
35+
## Migration to Rust 2021
36+
37+
As a part of the 2021 edition a migration lint, `rust_2021_prelude_collisions`, has been added in order to aid in automatic migration of Rust 2018 codebases to Rust 2021.
38+
39+
In order to have `rustfix` migrate your code to be Rust 2021 Edition compatible, run:
40+
41+
```sh
42+
cargo fix --edition
43+
```
44+
45+
The lint detects cases where functions or methods are called that have the same name as the methods defined in one of the new prelude traits. In some cases, it may rewrite your calls in various ways to ensure that you continue to call the same function you did before.
46+
47+
If you'd like to migrate your code manually or better understand what `rustfix` is doing, below we've outlined the situations where a migration is needed along with a counter example of when it's not needed.
48+
49+
### Migration needed
50+
51+
#### Conflicting trait methods
52+
53+
When two traits that are in scope have the same method name, it is ambiguous which trait method should be used. For example:
54+
55+
```rust
56+
trait MyTrait<A> {
57+
// This name is the same as the `from_iter` method on the `FromIterator` trait from `std`.
58+
fn from_iter(x: Option<A>);
59+
}
60+
61+
impl<T> MyTrait<()> for Vec<T> {
62+
fn from_iter(_: Option<()>) {}
63+
}
64+
65+
fn main() {
66+
// Vec<T> implements both `std::iter::FromIterator` and `MyTrait`
67+
// If both traits are in scope (as would be the case in Rust 2021),
68+
// then it becomes ambiguous which `from_iter` method to call
69+
<Vec<i32>>::from_iter(None);
70+
}
71+
```
72+
73+
We can fix this by using fully qualified syntax:
74+
75+
```rust,ignore
76+
fn main() {
77+
// Now it is clear which trait method we're referring to
78+
<Vec<i32> as MyTrait<i32>>::from_iter(None);
79+
}
80+
```
81+
82+
#### Inherent methods on `dyn Trait` objects
83+
84+
Some users invoke methods on a `dyn Trait` value where the method name overlaps with a new prelude trait:
85+
86+
```rust
87+
mod submodule {
88+
pub trait MyTrait {
89+
// This has the same name as `TryInto::try_into`
90+
fn try_into(&self) -> Result<u32, ()>;
91+
}
92+
}
93+
94+
// `MyTrait` isn't in scope here and can only be referred to through the path `submodule::MyTrait`
95+
fn bar(f: Box<dyn submodule::MyTrait>) {
96+
// If `std::convert::TryInto` is in scope (as would be the case in Rust 2021),
97+
// then it becomes ambiguous which `try_into` method to call
98+
f.try_into();
99+
}
100+
```
101+
102+
Unlike with static dispatch methods, calling a trait method on a trait object does not require that the trait be in scope. The code above works
103+
as long as there is no trait in scope with a conflicting method name. When the `TryInto` trait is in scope (which is the case in Rust 2021),
104+
this causes an ambiguity. Should the call be to `MyTrait::try_into` or `std::convert::TryInto::try_into`?
105+
106+
In these cases, we can fix this by adding an additional dereferences or otherwise clarify the type of the method receiver. This ensures that
107+
the `dyn Trait` method is chosen, versus the methods from the prelude trait. For example, turning `f.try_into()` above into `(&*f).try_into()`
108+
ensures that we're calling `try_into` on the `dyn MyTrait` which can only refer to the `MyTrait::try_into` method.
109+
110+
### No migration needed
111+
112+
#### Inherent methods
113+
114+
Many types define their own inherent methods with the same name as a trait method. For instance, below the struct `MyStruct` implements `from_iter` which shares the same name with the method from the trait `FromIterator` found in the standard library:
115+
116+
```rust
117+
use std::iter::IntoIterator;
118+
119+
struct MyStruct {
120+
data: Vec<u32>
121+
}
122+
123+
impl MyStruct {
124+
// This has the same name as `std::iter::FromIterator::from_iter`
125+
fn from_iter(iter: impl IntoIterator<Item = u32>) -> Self {
126+
Self {
127+
data: iter.into_iter().collect()
128+
}
129+
}
130+
}
131+
132+
impl std::iter::FromIterator<u32> for MyStruct {
133+
fn from_iter<I: IntoIterator<Item = u32>>(iter: I) -> Self {
134+
Self {
135+
data: iter.into_iter().collect()
136+
}
137+
}
138+
}
139+
```
140+
141+
Inherent methods always take precedent over trait methods so there's no need for any migration.
142+
143+
### Implementation Reference
144+
145+
The lint needs to take a couple of factors into account when determining whether or not introducing 2021 Edition to a codebase will cause a name resolution collision (thus breaking the code after changing edition). These factors include:
146+
147+
- Is the call a [fully-qualified call] or does it use [dot-call method syntax]?
148+
- This will affect how the name is resolved due to auto-reference and auto-dereferencing on method call syntax. Manually dereferencing/referencing will allow specifying priority in the case of dot-call method syntax, while fully-qualified call requires specification of the type and the trait name in the method path (e.g. `<Type as Trait>::method`)
149+
- Is this an [inherent method] or [a trait method]?
150+
- Inherent methods that take `self` will take priority over `TryInto::try_into` as inherent methods take priority over trait methods, but inherent methods that take `&self` or `&mut self` won't take priority due to requiring a auto-reference (while `TryInto::try_into` does not, as it takes `self`)
151+
- Is the origin of this method from `core`/`std`? (As the traits can't have a collision with themselves)
152+
- Does the given type implement the trait it could have a collision against?
153+
- Is the method being called via dynamic dispatch? (i.e. is the `self` type `dyn Trait`)
154+
- If so, trait imports don't affect resolution, and no migration lint needs to occur
155+
156+
[fully-qualified call]: https://doc.rust-lang.org/reference/expressions/call-expr.html#disambiguating-function-calls
157+
[dot-call method syntax]: https://doc.rust-lang.org/reference/expressions/method-call-expr.html
158+
[inherent method]: https://doc.rust-lang.org/reference/items/implementations.html#inherent-implementations
159+
[a trait method]: https://doc.rust-lang.org/reference/items/implementations.html#trait-implementations

0 commit comments

Comments
 (0)