Rust – Enabling the next-generation trait solver on nightly

afdbcreid1 pts0 comments

Enabling the next-generation trait solver on nightly | Rust Blog

Aug. 21, 2026 &middot; lcnr<br>on behalf of The Rustc Trait System Refactor Initiative

After nearly 4 years of active development, the next-generation trait solver is close to stabilization. We are enabling it by default on nightly to surface any remaining issues and plan to stabilize it in the next months. This is the largest single change to the Rust compiler since its initial release. It completely replaces how we prove where-clauses, normalize associated types, and much more. Please try out the latest nightly and open an issue if you encounter any bugs or regressions.

This is an internal component of the compiler. The main benefits of this rework will come in the future. The removal of the old implementation will unblock features such as Type Alias Impl Trait and Return Type Notation, allow us to add new implicit default trait bounds (e.g., Move and Forget), and enable us to fix the remaining type system unsoundnesses.

Even so, this already fixes a huge number of issues. As an underapproximation, we currently know of more than 200 issues on GitHub fixed by this change. This also has a significant impact on compile times; more on that later. When developing on nightly, you may accidentally rely on behavior only supported by the new trait solver.

This is an incredibly big change which results in a non-trivial amount of breakage. Most of these changes are intended improvements to type inference or the removal of undesirable behavior. We are tracking the known issues and breakage in a pinned GitHub issue.

What can I do?

Please update to the latest nightly version by using rustup update nightly and use it to test your existing projects and libraries.

⚠️ While the next-generation trait solver has been enabled on our main branch, this change will only be accessible on the nightly channel starting from Saturday 22nd August. You can already test it before then by providing -Znext-solver=globally as a command-line argument ⚠️

Please tell us if you encounter any breakage, compile-time performance regression, or bad diagnostics. We have not yet spent too much time on error messages for the next-generation trait solver, so we would also appreciate you using this nightly for development to find poor diagnostics and other bugs in our error handling.

If you encounter any issue, take a quick look at the pinned GitHub issue to see if the affected crate is already listed, and if not, please open a new issue! To disable the next-generation trait solver on nightly, you can pass -Znext-solver=coherence to rustc, use RUSTFLAGS=-Znext-solver=coherence, or change your project's .cargo/config.toml configuration file:

[build]<br>rustflags = ["-Znext-solver=coherence"]<br>What exactly does this mean?

We will go into more detail about the next-generation trait solver, how we got here, and what it changes when fully stabilizing it. This is a quick summary of its main impact.

impl Trait handling

The way opaque types — return-position impl Trait (RPIT), but also the unstable Type Alias Impl Trait (TAIT) and Return Type Notation (RTN) — are handled in the type system has nearly completely changed. This fixes a lot of bugs and edge cases with them and should make their behavior a lot more consistent in general. This change is why the next-generation trait solver is necessary to stabilize TAIT and RTN.

The implementation change mostly does not matter for RPIT as we special-cased impl Trait from the method signature when type checking the method body. This means the only way to observe the old behavior is via recursive function calls. The following snippet errors with the existing implementation, but compiles with -Znext-solver enabled: godbolt

fn foo(b: bool) -> impl Sized {<br>if b {<br>// The old implementation errored here.<br>foo(false) + 1<br>} else {<br>Associated types in higher-ranked types

The most impactful change is way we handle associated types referencing bound variables, i.e., lifetimes from a for binder, for example, the type for fn(::Assoc). While most users don't encounter such types directly, there are widely used crates which do. This change impacts existing code by removing incorrect type inference, such as in bevy and minijinja.

It also fixes a bunch of unnecessary errors like in the following example: godbolt

trait OtherTrait {<br>type Assoc'a>;<br>impl OtherTrait for u32 {<br>type Assoc'a> = &'a u32;

trait Trait {}<br>implT: OtherTrait> Trait for (T, for'a> fn(T as OtherTrait>::Assoc'a>)) {}

fn implsT: Trait>() {}

fn main() {<br>// The old implementation failed to prove<br>// the where-bound of `impls`.<br>impls::(u32, for'a> fn(&'a u32))>();<br>Compile-time performance

co-authored with jana :3

We've spent a lot of time on the compile-time performance of the next-generation trait solver. There have been many cases where it performed quadratically or even exponentially slower than the old solver.

Especially the last few weeks were mainly spent on improving performance....

trait solver type next nightly generation

Related Articles