-
Notifications
You must be signed in to change notification settings - Fork 143
Update transitioning chapter. #255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
36530e2
2187a65
cacf1c4
73ae835
02583b6
890abe6
7ab6ad2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,205 @@ | ||||||
| # Advanced migration strategies | ||||||
|
|
||||||
| ## How migrations work | ||||||
|
|
||||||
| [`cargo fix --edition`][`cargo fix`] works by running the equivalent of [`cargo check`] on your project with special [lints] enabled which will detect code that may not compile in the next edition. | ||||||
| These lints include instructions on how to modify the code to make it compatible on both the current and the next edition. | ||||||
| `cargo fix` applies these changes to the source code, and then runs `cargo check` again to verify that the fixes work. | ||||||
| If the fixes fail, then it will back out the changes and display a warning. | ||||||
|
|
||||||
| Changing the code to be simultaneously compatible with both the current and next edition makes it easier to incrementally migrate the code. | ||||||
| If the automated migration does not completely succeed, or requires manual help, you can iterate while staying on the original edition before changing `Cargo.toml` to use the next edition. | ||||||
|
|
||||||
| The lints that `cargo fix --edition` apply are part of a [lint group]. | ||||||
| For example, when migrating from 2018 to 2021, Cargo uses the `rust-2021-compatibility` group of lints to fix the code. | ||||||
| Check the [Partial migration](#partial-migration-with-broken-code) section below for tips on using individual lints to help with migration. | ||||||
|
|
||||||
| `cargo fix` may run `cargo check` multiple times. | ||||||
| For example, after applying one set of fixes, this may trigger new warnings which require further fixes. | ||||||
| Cargo repeats this until no new warnings are generated. | ||||||
|
|
||||||
| ## Migrating multiple configurations | ||||||
|
|
||||||
| `cargo fix` can only work with a single configuration at a time. | ||||||
| If you use [Cargo features] or [conditional compilation], then you may need to run `cargo fix` multiple times with different flags. | ||||||
|
|
||||||
| For example, if you have code that uses `#[cfg]` attributes to include different code for different platforms, you may need to run `cargo fix` with the `--target` option to fix for different targets. | ||||||
| This may require moving your code between machines if you don't have cross-compiling available. | ||||||
|
|
||||||
| Similarly, if you have conditions on Cargo features, like `#[cfg(feature = "my-optional-thing")]`, it is recommended to use the `--all-features` flag to allow `cargo fix` to migrate all the code behind those feature gates. | ||||||
| If you want to migrate feature code individually, you can use the `--features` flag to migrate one at a time. | ||||||
|
|
||||||
| ## Migrating a large project or workspace | ||||||
|
|
||||||
| You can migrate a large project incrementally to make the process easier if you run into problems. | ||||||
|
|
||||||
| In a [Cargo workspace], each package defines its own edition, so the process naturally involves migrating one package at a time. | ||||||
|
|
||||||
| Within a [Cargo package], you can either migrate the entire package at once, or migrate individual [Cargo targets] one at a time. | ||||||
| For example, if you have multiple binaries, tests, and examples, you can use specific target selection flags with `cargo fix --edition` to migrate just that one target. | ||||||
| By default, `cargo fix` uses `--all-targets`. | ||||||
|
|
||||||
| For even more advanced cases, you can specify the edition for each individual target in `Cargo.toml` like this: | ||||||
|
|
||||||
| ```toml | ||||||
| [[bin]] | ||||||
| name = "my-binary" | ||||||
| edition = "2018" | ||||||
| ``` | ||||||
|
|
||||||
| This usually should not be required, but is an option if you have a lot of targets and are having difficulty migrating them all together. | ||||||
|
|
||||||
| ## Partial migration with broken code | ||||||
|
|
||||||
| Sometimes the fixes suggested by the compiler may fail to work. | ||||||
| When this happens, Cargo will report a warning indicating what happened and what the error was. | ||||||
| However, by default it will automatically back out the changes it made. | ||||||
| It can be helpful to keep the code in the broken state and manually resolve the issue. | ||||||
| Some of the fixes may have been correct, and the broken fix maybe be *mostly* correct, but just need minor tweaking. | ||||||
|
|
||||||
| In this situation, use the `--broken-code` option with `cargo fix` to tell Cargo not to back out the changes. | ||||||
| Then, you can go manually inspect the error and investigate what is needed to fix it. | ||||||
|
|
||||||
| Another option to incrementally migrate a project is to apply individual fixes separately, one at a time. | ||||||
| You can do this by adding the individual lints as warnings, and then either running `cargo fix` (without the `--edition` flag) or using your editor or IDE to apply its suggestions if it supports "Quick Fixes". | ||||||
|
|
||||||
| For example, the 2018 edition uses the [`keyword-idents`] lint to fix any conflicting keywords. | ||||||
| You can add `#![warn(keyword_idents)]` to the top of each crate (like at the top of `src/lib.rs` or `src/main.rs`). | ||||||
| Then, running `cargo fix` will apply just the suggestions for that lint. | ||||||
|
|
||||||
| You can see the list of lints enabled for each edition in the [lint group] page, or run the `rustc -Whelp` command. | ||||||
|
|
||||||
| ## Migrating macros | ||||||
|
|
||||||
| Some macros may require manual work to fix them for the next edition. | ||||||
| For example, a macro that generates syntax that only works on the previous edition, then `cargo fix` will not be able to fix it. | ||||||
|
|
||||||
| This may be a problem for both [proc macros] and `macro_rules`-style macros. | ||||||
| `macro_rules` macros can be updated if the macro is used within the same crate, but if it is exported with `#[macro_export]`, then it may not be able to fix it. | ||||||
| Proc macros in general cannot be fixed at all. | ||||||
|
|
||||||
| For example, this (contrived) macro won't get fixed when migrating to 2018. | ||||||
|
||||||
| For example, this (contrived) macro won't get fixed when migrating to 2018. | |
| For example, if we migrate the crate containing the (contrived) macro `foo` to 2018, `foo` would not be automatically fixed (note though that macro hygiene, discussed below, means it is possible to migrate other crates that use `foo` to 2018 without changes). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This text might not actually be right, but I got a bit confused here about the details in reading it, and I imagine others might, too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, yea after re-reading that section, it was pretty confusing (and misleading in places). I reworked it to hopefully make it a little clearer and more correct.
(I noticed that the keyword_idents lint doesn't work for macros at all due to the way the parser works, so cargo fix won't work even if you call the macro from the same crate.)
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| However, it won't work when called from another crate. | |
| However, `foo` won't work when called from another crate. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we can note again that migration works by migrates code to a state that compiles on both the current edition and the next edition. This is why you migrate, then update the edition in Cargo.toml.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, sounds good. I wanted to make it clear that it is compatible on both editions, but maybe wasn't explicit enough. I added another paragraph trying to explain why it works that way.