The trait solver sits at the core of Rust's type checking. It is the component that proves whether T: Trait holds and what type <T as Trait>::Assoc resolves to, and it is about to be replaced wholesale. The stabilization PR for -Znext-solver=globally, rust-lang/rust#164209, was opened on 2026-10-12. The T-lang design meeting discussed it on 2026-10-21. Its author, lcnr, calls it "the largest single change to Rust since version 1.0". The new solver has been on by default in nightly since late August 2026.
The debate centers on one question: should Rust accept breakage that comes with no prior warning? In the latest crater run (a regression build of the crates.io ecosystem), there were 114 non-spurious failures, affecting fewer than 150 crates. Among the top 10,000 crates, 422 compile more than 10% slower and 96 compile more than 10% faster. Bevy was reported to go from 1.5 GB to 11 GB of memory. Even so, most voices in the meeting said to ship it and keep fixing. The reason is clear: long-blocked features such as TAIT (impl Trait in type aliases), ATPIT, and RTN (Return Type Notation) have been waiting on this solver.
This article explains what the new solver changes at the conceptual level. It then lays out where everyday Rust code feels the impact.
Years of incremental patches left the old solver answering the same question in several different places.
<T as Trait>::Assoc that cannot be simplified any further is called rigid. The old solver had this concept, but the type representation did not record it.impl Trait). Opaques were sometimes treated as rigid even inside their defining scope. MIR needed special cases such as OpaqueCast.As a result, there was no foundation for stabilizing features like TAIT correctly. The new solver came out of years of experience with Chalk and four years of focused work, and it rebuilds all of this into a single model.
The new solver puts every goal into canonical form each time it solves one. Inference variables become numbered bound variables, and generic parameters and regions become placeholders. That way Vec<?3>: Clone and Vec<?7>: Clone count as the same question, and one global cache can share the result.
A side effect is that cycles are detected even when the inference variables differ. The report's example is impl<T> Foo for Wrapper<Wrapper<T>> where Wrapper<T>: Foo. Proving Wrapper<?a>: Foo produces Wrapper<?b>: Foo, and in canonical form those two goals are identical. The new solver first assigns a provisional result to the head of the cycle. It then re-solves until the final result matches that provisional result. In other words, it runs a fixpoint iteration. The iteration limit is 8, but the report notes that goals usually reach a fixpoint after one iteration or fail with overflow.
The rule for when a cycle is accepted as coinductive (treated as success) matches the old solver. The cycle must contain at least one Sized or auto-trait goal, and every other goal must be either a coinductive trait goal or a Projection goal.
This is the biggest conceptual change. The new solver records explicitly in the type representation whether an alias is rigid in the current environment (ParamEnv plus TypingMode). When relating types (equality or subtyping) hits a non-rigid alias, the solver replaces it on the spot with an inference variable. It then emits a Projection goal saying "normalizing this alias yields this variable." This is on-demand normalization.
Without the rigid marker, aliases had to be renormalized at every relation. The solver also used an AliasRelate goal with three candidates, depending on which side to normalize. The report calls this approach a "horrible idea": it performed badly and failed with ambiguity for subtle reasons. With the marker, rigid aliases are compared structurally and only the rest get solved.