Skip to content

Commit 66b7561

Browse files
committed
Fix mistakes in prelude docs
1 parent fbf63eb commit 66b7561

File tree

1 file changed

+74
-31
lines changed

1 file changed

+74
-31
lines changed

src/rust-2021/prelude.md

Lines changed: 74 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -36,64 +36,107 @@ The tracking issue [can be found here](https://github.com/rust-lang/rust/issues/
3636

3737
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.
3838

39-
In order to have rustfix migrate your code to be Rust 2021 Edition compatible, run:
39+
In order to have `rustfix` migrate your code to be Rust 2021 Edition compatible, run:
4040

41-
```ignore
41+
```sh
4242
cargo fix --edition
4343
```
4444

4545
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.
4646

47-
### Fully qualified calls to inherent methods
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.
4848

49-
Many types define their own inherent methods with the name `from_iter` which shares the same name with `std::iter::FromIterator::from_iter`:
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:
5054

5155
```rust
52-
struct MyStruct {
53-
data: Vec<u32>
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>);
5459
}
5560

56-
impl MyStruct {
57-
// This has the same name as `std::iter::FromIterator::from_iter`
58-
fn from_iter(x: impl Iterator<Item = u32>) -> Self {
59-
Self {
60-
data: x.collect()
61-
}
62-
}
61+
impl<T> MyTrait<()> for Vec<T> {
62+
fn from_iter(_: Option<()>) {}
6363
}
64-
```
6564

66-
In case that already use a fully qualified inherent method syntax (e.g., calls like `MyStruct::from_iter`), there is not ambiguity with calls to trait methods (e.g., `<MyStruct as FromIter>::from_iter`), because inherent methods take precedence over trait methods. Therefore, no migration is needed.
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+
```
6772

68-
A migration is necessary when using "dot method" syntax where the method name is the same as the trait method name for a trait which is now
69-
in scope. For example, a call like `my_struct.into_iter()` where `into_iter()` used to refer to an inherent method on `MyStruct` but now conflicts with
70-
the trait method from `IntoIter`. To make these calls unambiguous, fully qualified inherent method syntax must be used:
73+
We can fix this by using fully qualified syntax:
7174

7275
```rust,ignore
73-
// Before:
74-
my_struct.into_iter();
75-
// After:
76-
MyStruct::into_iter(my_struct);
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+
}
7780
```
7881

79-
Note: this only impacts methods which take `&self` and `&mut self` but not `self`.
80-
81-
### Inherent methods on `dyn Trait` objects
82+
#### Inherent methods on `dyn Trait` objects
8283

8384
Some users invoke methods on a `dyn Trait` value where the method name overlaps with a new prelude trait:
8485

8586
```rust
86-
trait MyTrait {
87-
// This has the same name as `std::iter::IntoIterator::into_iter`
88-
fn into_iter(&self);
87+
mod submodule {
88+
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+
struct MyStruct {
118+
data: Vec<u32>
89119
}
90120

91-
fn bar(f: &dyn MyTrait) {
92-
f.into_iter();
121+
impl MyStruct {
122+
// This has the same name as `std::iter::FromIterator::from_iter`
123+
fn from_iter(iter: impl Iterator<Item = u32>) -> Self {
124+
Self {
125+
data: iter.collect()
126+
}
127+
}
128+
}
129+
130+
impl FromIterator<u32> for MyStruct {
131+
fn from_iter<I: IntoIterator<Item = u32>>(iter: I) -> Self {
132+
Self {
133+
data: iter.collect()
134+
}
135+
}
93136
}
94137
```
95138

96-
In these cases, the lint will sometimes rewrite to introduce additional dereferences or otherwise clarify the type of the method receiver. This ensures that the `dyn Trait` method is chosen, versus the methods from the prelude trait. For example, `f.into_iter()` above would become `(*f).into_iter()`.
139+
Inherent methods always take precedent over trait methods so there's no need for any migration.
97140

98141
### Implementation Reference
99142

0 commit comments

Comments
 (0)