From 8acd8a68a8f9da44796a2d955b8a7e009d63f81f Mon Sep 17 00:00:00 2001 From: jam1garner <8260240+jam1garner@users.noreply.github.com> Date: Fri, 2 Jul 2021 16:08:12 -0400 Subject: [PATCH 1/8] Add Rust 2021 prelude migration details --- src/rust-2021/prelude.md | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/rust-2021/prelude.md b/src/rust-2021/prelude.md index a62993b6..e2ceba3a 100644 --- a/src/rust-2021/prelude.md +++ b/src/rust-2021/prelude.md @@ -31,5 +31,26 @@ It's identical to the current one, except for three new additions: - [`std::convert::TryFrom`](https://doc.rust-lang.org/stable/std/convert/trait.TryFrom.html) - [`std::iter::FromIterator`](https://doc.rust-lang.org/stable/std/iter/trait.FromIterator.html) -The library team still needs to formally approve these, which will likely happen soon. - +The tracking issue [can be found here](https://github.com/rust-lang/rust/issues/85684). + +## Migration + +As a part of the 2021 edition a migration lint, currently `future_prelude_collision`, has been added in order to aid in automatic migration of Rust 2018 codebases to Rust 2021. + +### Implementation Reference + +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: + +- Is the call a [fully-qualified call] or does it use [dot-call method syntax]? + - 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. `::method`) +- Is this an [inherent method] or [a trait method]? + - 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`) +- Is the origin of this method from `core`/`std`? (As the traits can't have a collision with themselves) +- Does the given type implement the trait it could have a collision against? +- Is the method being called via dynamic dispatch? (i.e. is the `self` type `dyn Trait`) + - If so, trait imports don't affect resolution, and no migration lint needs to occur + +[fully-qualified call]: https://doc.rust-lang.org/reference/expressions/call-expr.html#disambiguating-function-calls +[dot-call method syntax]: https://doc.rust-lang.org/reference/expressions/method-call-expr.html +[inherent method]: https://doc.rust-lang.org/reference/items/implementations.html#inherent-implementations +[a trait method]: https://doc.rust-lang.org/reference/items/implementations.html#trait-implementations From c60e2af81dc0089ee7257173ff0787dfc088700c Mon Sep 17 00:00:00 2001 From: jam1garner <8260240+jam1garner@users.noreply.github.com> Date: Fri, 2 Jul 2021 16:28:33 -0400 Subject: [PATCH 2/8] Fix `rust_2021_prelude_collisions` name in 2021 prelude details --- src/rust-2021/prelude.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rust-2021/prelude.md b/src/rust-2021/prelude.md index e2ceba3a..9427263e 100644 --- a/src/rust-2021/prelude.md +++ b/src/rust-2021/prelude.md @@ -35,7 +35,7 @@ The tracking issue [can be found here](https://github.com/rust-lang/rust/issues/ ## Migration -As a part of the 2021 edition a migration lint, currently `future_prelude_collision`, has been added in order to aid in automatic migration of Rust 2018 codebases to Rust 2021. +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. ### Implementation Reference From 466f68efd2ca6f309ae0969d6b38df7128787944 Mon Sep 17 00:00:00 2001 From: jam1garner <8260240+jam1garner@users.noreply.github.com> Date: Mon, 5 Jul 2021 17:38:31 -0400 Subject: [PATCH 3/8] Add cargo fix command to prelude migration section --- src/rust-2021/prelude.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rust-2021/prelude.md b/src/rust-2021/prelude.md index 9427263e..1bf240a5 100644 --- a/src/rust-2021/prelude.md +++ b/src/rust-2021/prelude.md @@ -37,6 +37,12 @@ The tracking issue [can be found here](https://github.com/rust-lang/rust/issues/ 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. +In order to have rustfix migrate your code to be Rust 2021 Edition compatible, run: + +``` +cargo fix --edition +``` + ### Implementation Reference 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: From 67ec8b8c000464b246ad8631e007aec86dbd6134 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 6 Jul 2021 12:32:15 -0400 Subject: [PATCH 4/8] prototype an example-based approach to explaining the rewrites --- src/rust-2021/prelude.md | 45 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/src/rust-2021/prelude.md b/src/rust-2021/prelude.md index 1bf240a5..bc8c3c3c 100644 --- a/src/rust-2021/prelude.md +++ b/src/rust-2021/prelude.md @@ -17,7 +17,7 @@ For example, if you have a crate or module called `example` containing a `pub st then `use example::*;` will make `Option` unambiguously refer to the one from `example`; not the one from the standard library. -However, adding a *trait* to the prelude can break existing code in a subtle way. +However, adding a _trait_ to the prelude can break existing code in a subtle way. A call to `x.try_into()` using a `MyTryInto` trait might become ambiguous and fail to compile if `std`'s `TryInto` is also imported, since it provides a method with the same name. @@ -33,7 +33,7 @@ It's identical to the current one, except for three new additions: The tracking issue [can be found here](https://github.com/rust-lang/rust/issues/85684). -## Migration +## Migration to Rust 2021 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. @@ -43,6 +43,47 @@ In order to have rustfix migrate your code to be Rust 2021 Edition compatible, r cargo fix --edition ``` +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: + +| Scenario | Example | Rewrite (if any) | +| ------------------------------------------------------------------------ | --------------------- | -------------------- | +| [Fully qualified inherent](#fully-qualified-calls-to-inherent-methods) | `Foo::from_iter(...)` | none | +| [Inherent methods on `dyn Trait`](inherent-methods-on-dyn-trait-objects) | `foo.into_iter()` | `(*foo).into_iter`() | + +### Fully qualified calls to inherent methods + +Many types define their own inherent methods with the name `from_iter`: + +```rust +struct Foo { + data: Vec +} +impl Foo { + fn from_iter(x: impl Iterator) -> Self { + Foo { + data: x.collect() + } + } +} +``` + +Calls like `Foo::from_iter` cannot be confused with calls to `::from_iter`, because inherent methods take precedence over trait methods. + +### Inherent methods on `dyn Trait` objects + +Some users invoke methods on a `dyn Trait` value where the method name overlaps with a new prelude trait: + +```rust +trait Foo { + fn into_iter(&self); +} +fn bar(f: &dyn Foo) { + f.into_iter(); +} +``` + +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()`. + ### Implementation Reference 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: From 1dd0767432319c3a0f389cefadb7f690f48d7812 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Tue, 13 Jul 2021 12:21:09 +0200 Subject: [PATCH 5/8] Apply feedback on PR --- src/rust-2021/prelude.md | 51 ++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/src/rust-2021/prelude.md b/src/rust-2021/prelude.md index bc8c3c3c..2ea6ed4e 100644 --- a/src/rust-2021/prelude.md +++ b/src/rust-2021/prelude.md @@ -2,8 +2,8 @@ ## Summary -- `TryInto`, `TryFrom` and `FromIterator` are now part of the prelude. -- This might change the meaning of e.g. `x.try_into()` depending on types and imports. +- The `TryInto`, `TryFrom` and `FromIterator` traits are now part of the prelude. +- This might make calls to trait methods ambiguous which could make some code fail to compile. ## Details @@ -18,11 +18,10 @@ then `use example::*;` will make `Option` unambiguously refer to the one from `e not the one from the standard library. However, adding a _trait_ to the prelude can break existing code in a subtle way. -A call to `x.try_into()` using a `MyTryInto` trait might become ambiguous and -fail to compile if `std`'s `TryInto` is also imported, -since it provides a method with the same name. -This is the reason we haven't added `TryInto` to the prelude yet, -since there is a lot of code that would break this way. +For example, a call to `x.try_into()` which comes from a `MyTryInto` trait might fail +to compile if `std`'s `TryInto` is also imported, because the call to `try_into` is now +ambiguous and could come from either trait. This is the reason we haven't added `TryInto` +to the prelude yet, since there is a lot of code that would break this way. As a solution, Rust 2021 will use a new prelude. It's identical to the current one, except for three new additions: @@ -43,41 +42,53 @@ In order to have rustfix migrate your code to be Rust 2021 Edition compatible, r cargo fix --edition ``` -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: - -| Scenario | Example | Rewrite (if any) | -| ------------------------------------------------------------------------ | --------------------- | -------------------- | -| [Fully qualified inherent](#fully-qualified-calls-to-inherent-methods) | `Foo::from_iter(...)` | none | -| [Inherent methods on `dyn Trait`](inherent-methods-on-dyn-trait-objects) | `foo.into_iter()` | `(*foo).into_iter`() | +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. ### Fully qualified calls to inherent methods -Many types define their own inherent methods with the name `from_iter`: +Many types define their own inherent methods with the name `from_iter` which shares the same name with `std::iter::FromIterator::from_iter`: ```rust -struct Foo { +struct MyStruct { data: Vec } -impl Foo { + +impl MyStruct { + // This has the same name as `std::iter::FromIterator::from_iter` fn from_iter(x: impl Iterator) -> Self { - Foo { + Self { data: x.collect() } } } ``` -Calls like `Foo::from_iter` cannot be confused with calls to `::from_iter`, because inherent methods take precedence over trait methods. +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., `::from_iter`), because inherent methods take precedence over trait methods. Therefore, no migration is needed. + +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 +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 +the trait method from `IntoIter`. To make these calls unambiguous, fully qualified inherent method syntax must be used: + +```rust +// Before: +my_struct.into_iter(); +// After: +MyStruct::into_iter(my_struct); +``` + +Note: this only impacts methods which take `&self` and `&mut self` but not `self`. ### Inherent methods on `dyn Trait` objects Some users invoke methods on a `dyn Trait` value where the method name overlaps with a new prelude trait: ```rust -trait Foo { +trait MyTrait { + // This has the same name as `std::iter::IntoIterator::into_iter` fn into_iter(&self); } -fn bar(f: &dyn Foo) { + +fn bar(f: &dyn MyTrait) { f.into_iter(); } ``` From fbf63eb63b0808257939d53d78b3b0bbcf5cf1a5 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Tue, 13 Jul 2021 12:26:26 +0200 Subject: [PATCH 6/8] add ignores --- src/rust-2021/prelude.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rust-2021/prelude.md b/src/rust-2021/prelude.md index 2ea6ed4e..9966ea84 100644 --- a/src/rust-2021/prelude.md +++ b/src/rust-2021/prelude.md @@ -38,7 +38,7 @@ As a part of the 2021 edition a migration lint, `rust_2021_prelude_collisions`, In order to have rustfix migrate your code to be Rust 2021 Edition compatible, run: -``` +```ignore cargo fix --edition ``` @@ -69,7 +69,7 @@ A migration is necessary when using "dot method" syntax where the method name is 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 the trait method from `IntoIter`. To make these calls unambiguous, fully qualified inherent method syntax must be used: -```rust +```rust,ignore // Before: my_struct.into_iter(); // After: From 66b7561bd969f1cd36de050ece679708876f69ea Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Tue, 13 Jul 2021 17:45:22 +0200 Subject: [PATCH 7/8] Fix mistakes in prelude docs --- src/rust-2021/prelude.md | 105 +++++++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 31 deletions(-) diff --git a/src/rust-2021/prelude.md b/src/rust-2021/prelude.md index 9966ea84..ee809026 100644 --- a/src/rust-2021/prelude.md +++ b/src/rust-2021/prelude.md @@ -36,64 +36,107 @@ The tracking issue [can be found here](https://github.com/rust-lang/rust/issues/ 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. -In order to have rustfix migrate your code to be Rust 2021 Edition compatible, run: +In order to have `rustfix` migrate your code to be Rust 2021 Edition compatible, run: -```ignore +```sh cargo fix --edition ``` 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. -### Fully qualified calls to inherent methods +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. -Many types define their own inherent methods with the name `from_iter` which shares the same name with `std::iter::FromIterator::from_iter`: +### Migration needed + +#### Conflicting trait methods + +When two traits that are in scope have the same method name, it is ambiguous which trait method should be used. For example: ```rust -struct MyStruct { - data: Vec +trait MyTrait { + // This name is the same as the `from_iter` method on the `FromIterator` trait from `std`. + fn from_iter(x: Option); } -impl MyStruct { - // This has the same name as `std::iter::FromIterator::from_iter` - fn from_iter(x: impl Iterator) -> Self { - Self { - data: x.collect() - } - } +impl MyTrait<()> for Vec { + fn from_iter(_: Option<()>) {} } -``` -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., `::from_iter`), because inherent methods take precedence over trait methods. Therefore, no migration is needed. +fn main() { + // Vec implements both `std::iter::FromIterator` and `MyTrait` + // If both traits are in scope (as would be the case in Rust 2021), + // then it becomes ambiguous which `from_iter` method to call + >::from_iter(None); +} +``` -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 -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 -the trait method from `IntoIter`. To make these calls unambiguous, fully qualified inherent method syntax must be used: +We can fix this by using fully qualified syntax: ```rust,ignore -// Before: -my_struct.into_iter(); -// After: -MyStruct::into_iter(my_struct); +fn main() { + // Now it is clear which trait method we're referring to + as MyTrait>::from_iter(None); +} ``` -Note: this only impacts methods which take `&self` and `&mut self` but not `self`. - -### Inherent methods on `dyn Trait` objects +#### Inherent methods on `dyn Trait` objects Some users invoke methods on a `dyn Trait` value where the method name overlaps with a new prelude trait: ```rust -trait MyTrait { - // This has the same name as `std::iter::IntoIterator::into_iter` - fn into_iter(&self); +mod submodule { + trait MyTrait { + // This has the same name as `TryInto::try_into` + fn try_into(&self) -> Result; + } +} + +// `MyTrait` isn't in scope here and can only be referred to through the path `submodule::MyTrait` +fn bar(f: Box) { + // If `std::convert::TryInto` is in scope (as would be the case in Rust 2021), + // then it becomes ambiguous which `try_into` method to call + f.try_into(); +} +``` + +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 +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), +this causes an ambiguity. Should the call be to `MyTrait::try_into` or `std::convert::TryInto::try_into`? + +In these cases, we can fix this by adding an 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, turning `f.try_into()` above into `(&*f).try_into()` +ensures that we're calling `try_into` on the `dyn MyTrait` which can only refer to the `MyTrait::try_into` method. + +### No migration needed + +#### Inherent methods + +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: + +```rust +struct MyStruct { + data: Vec } -fn bar(f: &dyn MyTrait) { - f.into_iter(); +impl MyStruct { + // This has the same name as `std::iter::FromIterator::from_iter` + fn from_iter(iter: impl Iterator) -> Self { + Self { + data: iter.collect() + } + } +} + +impl FromIterator for MyStruct { + fn from_iter>(iter: I) -> Self { + Self { + data: iter.collect() + } + } } ``` -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()`. +Inherent methods always take precedent over trait methods so there's no need for any migration. ### Implementation Reference From 91cb3ebf572f06c735a3e3acb25385ad584a57df Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Wed, 14 Jul 2021 10:16:36 +0200 Subject: [PATCH 8/8] Fix doc tests --- src/rust-2021/prelude.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/rust-2021/prelude.md b/src/rust-2021/prelude.md index ee809026..97e6df86 100644 --- a/src/rust-2021/prelude.md +++ b/src/rust-2021/prelude.md @@ -85,7 +85,7 @@ Some users invoke methods on a `dyn Trait` value where the method name overlaps ```rust mod submodule { - trait MyTrait { + pub trait MyTrait { // This has the same name as `TryInto::try_into` fn try_into(&self) -> Result; } @@ -114,23 +114,25 @@ ensures that we're calling `try_into` on the `dyn MyTrait` which can only refer 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: ```rust +use std::iter::IntoIterator; + struct MyStruct { data: Vec } impl MyStruct { // This has the same name as `std::iter::FromIterator::from_iter` - fn from_iter(iter: impl Iterator) -> Self { + fn from_iter(iter: impl IntoIterator) -> Self { Self { - data: iter.collect() + data: iter.into_iter().collect() } } } -impl FromIterator for MyStruct { +impl std::iter::FromIterator for MyStruct { fn from_iter>(iter: I) -> Self { Self { - data: iter.collect() + data: iter.into_iter().collect() } } }