By making it part of the edition, most users eventually updated to the new edition and were
handled by the migration.
Switching to `Warn` only impacted a few stragglers who did not update.
### Lints and stability
Lints can be marked as being unstable, which can be helpful when developing a new edition feature,
and you want to test out a migration lint.
The feature gate can be specified in the lint's declaration like this:
```rust,ignore
declare_lint! {
pub SOME_LINT_NAME,
Allow,
"my cool lint",
@feature_gate = sym::my_feature_name;
}
```
Then, the lint will only fire if the user has the appropriate `#![feature(my_feature_name)]`.
Just beware that when it comes time to do crater runs testing the migration that the feature gate
will need to be removed.
Alternatively, you can implement an allow-by-default [migration lint] for an upcoming unstable
edition without a feature gate.
Although users may technically be able to enable the lint before the edition is stabilized, most
will not notice the new lint exists, and it should not disrupt anything or cause any breakage.
### Idiom lints
In the 2018 edition, there was a concept of "idiom lints" under the `rust-2018-idioms` lint group.
The concept was to have new idiomatic styles under a different lint group separate from the forced
migrations under the `rust-2018-compatibility` lint group, giving some flexibility as to how people
opt-in to certain edition changes.
Overall this approach did not seem to work very well,
and it is unlikely that we will use the idiom groups in the future.
## Standard library changes
### Preludes
Each edition comes with a specific prelude of the standard library.
These are implemented as regular modules in [`core::prelude`] and [`std::prelude`].
New items can be added to the prelude, just beware that this can conflict with user's pre-existing
code.
Usually a [migration lint] should be used to migrate existing code to avoid the conflict.
For example, [`rust_2021_prelude_collisions`] is used to handle the collisions with the new traits
in 2021.
### Customized language behavior
Usually it is not possible to make breaking changes to the standard library.
In some rare cases, the teams may decide that the behavior change is important enough to break this
rule.
The downside is that this requires special handling in the compiler to be able to distinguish when
the old and new signatures or behaviors should be used.
One example is the change in method resolution for [`into_iter()` of arrays][into-iter].
This was implemented with the `#[rustc_skip_array_during_method_dispatch]` attribute on the
`IntoIterator` trait which then tells the compiler to consider an alternate trait resolution choice
based on the edition.
Another example is the [`panic!` macro changes][panic-macro].
This required defining multiple panic macros, and having the built-in panic macro implementation
determine the appropriate way to expand it.
This also included the [`non_fmt_panics`] [migration lint] to adjust old code to the new form, which