차세대 트레이트 솔버가 나이틀리에서 기본 활성화되었습니다. 주요 변경 사항, 성능 영향, 테스트 및 문제 보고 방법을 안내합니다.
거의 4년간의 활발한 개발 끝에, 차세대 트레이트 솔버가 안정화에 가까워졌습니다. 남아 있는 문제를 찾아내기 위해 나이틀리에서 기본으로 활성화하며, 향후 몇 달 안에 안정화할 계획입니다. 이는 Rust 컴파일러가 최초로 출시된 이후 이루어진 가장 큰 단일 변경입니다. 이는 where 절을 증명하고 연관 타입을 정규화하는 방식 등을 완전히 대체합니다. 최신 나이틀리를 사용해 보고, 버그나 회귀를 발견하면 이슈를 등록해 주세요.
이는 컴파일러의 내부 구성 요소입니다. 이 재작업의 주요 이점은 앞으로 나타날 것입니다. 이전 구현을 제거하면 Type Alias Impl Trait 및 Return Type Notation 같은 기능의 도입이 가능해지고, 새로운 암묵적 기본 트레이트 경계(예: Move 및 Forget)를 추가할 수 있으며, 남아 있는 타입 시스템의 불건전성을 수정할 수 있게 됩니다.
그럼에도 이것만으로도 매우 많은 문제가 해결됩니다. 과소 추정치로, 현재 이 변경으로 해결된 GitHub 이슈가 200개 이상인 것으로 파악하고 있습니다. 이는 컴파일 시간에도 상당한 영향을 미치며, 이에 대해서는 뒤에서 더 설명하겠습니다. 나이틀리에서 개발할 때는 새 트레이트 솔버에서만 지원되는 동작에 의도치 않게 의존할 수 있습니다.
이는 엄청나게 큰 변경이므로, 적지 않은 호환성 문제가 발생합니다. 이러한 변경 대부분은 타입 추론의 의도된 개선이거나 바람직하지 않은 동작의 제거입니다. 알려진 문제와 호환성 문제는 고정된 GitHub 이슈에서 추적하고 있습니다.
rustup update nightly를 사용해 최신 나이틀리 버전으로 업데이트하고, 기존 프로젝트와 라이브러리를 테스트하는 데 사용해 주세요.
⚠️ 차세대 트레이트 솔버는
main브랜치에서 활성화되었지만, 이 변경은 8월 22일 토요일부터 나이틀리 채널에서만 사용할 수 있습니다. 그 전에도 명령줄 인수로-Znext-solver=globally를 제공하여 테스트할 수 있습니다. ⚠️
호환성 문제, 컴파일 시간 성능 저하 또는 좋지 않은 진단 메시지를 발견하면 알려 주세요. 아직 차세대 트레이트 솔버의 오류 메시지에는 많은 시간을 들이지 못했으므로, 이 나이틀리를 개발에 사용하여 부정확한 진단과 오류 처리의 다른 버그를 찾아 주시면 감사하겠습니다.
문제를 발견하면, 먼저 고정된 GitHub 이슈를 빠르게 확인하여 영향을 받은 크레이트가 이미 나열되어 있는지 살펴보고, 없다면 새 이슈를 등록해 주세요! 나이틀리에서 차세대 트레이트 솔버를 비활성화하려면 rustc에 -Znext-solver=coherence를 전달하거나, RUSTFLAGS=-Znext-solver=coherence를 사용하거나, 프로젝트의 .cargo/config.toml 설정 파일을 변경하면 됩니다.
[build]
rustflags = ["-Znext-solver=coherence"]
차세대 트레이트 솔버와 여기까지 오게 된 과정, 그리고 완전히 안정화되었을 때 무엇이 바뀌는지 더 자세히 설명하겠습니다. 다음은 주요 영향에 대한 간략한 요약입니다.
impl Trait 처리불투명 타입, 즉 반환 위치 impl Trait(RPIT)뿐 아니라 불안정 기능인 Type Alias Impl Trait (TAIT) 및 Return Type Notation (RTN)을 타입 시스템에서 처리하는 방식이 거의 완전히 바뀌었습니다. 이로써 이들과 관련된 많은 버그와 예외 사례가 수정되며, 전반적으로 동작이 훨씬 더 일관되게 될 것입니다. 이 변경 때문에 TAIT와 RTN을 안정화하려면 차세대 트레이트 솔버가 필요합니다.
구현 변경은 메서드 본문을 타입 검사할 때 메서드 시그니처의 impl Trait를 특수 처리했기 때문에 대체로 RPIT에는 중요하지 않습니다. 즉, 이전 동작을 관찰할 수 있는 유일한 방법은 재귀 함수 호출입니다. 다음 코드는 기존 구현에서는 오류가 나지만, -Znext-solver를 활성화하면 컴파일됩니다. godbolt
fn foo(b: bool) -> impl Sized {
if b {
// The old implementation errored here.
foo(false) + 1
} else {
0
}
}
가장 영향력이 큰 변경은 바인딩된 변수를 참조하는 연관 타입, 즉 예를 들어 타입 for<'a> fn(<T as Trait>::Assoc<'a>)에서처럼 for<'a> 바인더의 라이프타임을 처리하는 방식입니다. 대부분의 사용자는 이러한 타입을 직접 접하지 않지만, 널리 사용되는 크레이트 중에는 이를 사용하는 것이 있습니다. 이 변경은 bevy 및 minijinja에서처럼 잘못된 타입 추론을 제거하여 기존 코드에 영향을 줍니다.
또한 다음 예시와 같은 불필요한 오류도 여럿 수정합니다. godbolt
trait OtherTrait {
type Assoc<'a>;
}
impl OtherTrait for u32 {
type Assoc<'a> = &'a u32;
}
trait Trait {}
impl<T: OtherTrait> Trait for (T, for<'a> fn(<T as OtherTrait>::Assoc<'a>)) {}
fn impls<T: Trait>() {}
fn main() {
// The old implementation failed to prove
// the where-bound of `impls`.
impls::<(u32, for<'a> fn(&'a u32))>();
}
jana와 공동 작성 :3
차세대 트레이트 솔버의 컴파일 시간 성능에 많은 시간을 투자했습니다. 이전 솔버보다 이차적으로, 심지어 지수적으로 느리게 동작한 사례가 많았습니다.
특히 지난 몇 주는 주로 성능 개선에 집중했습니다. 이 작업에는 많은 사람이 참여했으며, Nick Nethercote, jana, Rémy Rakic, mira가 크게 기여했습니다.
이 노력의 일환으로 Rémy Rakic는 crates.io의 상위 20,000개 크레이트에서 두 구현의 성능을 비교했습니다. 아래는 지난 두 달 동안의 성능 변화를 시각화한 것입니다.

왼쪽과 오른쪽에서 주요 이상치를 확인할 수 있습니다. 여기의 크레이트 표본은 그러한 크레이트 쪽으로 편향되어 있다는 점에 유의하세요. 그것들이 우리에게 더 흥미롭기 때문입니다. 상위 20,000개 중 테스트한 거의 모든 크레이트는 두 구현에서 사실상 동일한 성능을 보였습니다.
이 그래프는 우리가 주로 부정적인 이상치에 노력을 집중했고, 그 부분에서 상당한 진전을 이루었음을 보여 줍니다. 이전에 새 솔버로 컴파일하는 데 두 배 이상 오래 걸리던 많은 크레이트는 여전히 약간 느리지만, 우리의 작업으로 그중 일부는 실제로 이전 솔버보다 더 빠르게 컴파일됩니다.
앞으로 몇 달 동안 성능을 계속 개선할 것이며, 기존 구현과 비교하면 여전히 많은 최적화 기회가 있습니다. 장기적으로는 거의 모든 크레이트가 차세대 트레이트 솔버의 이점을 얻을 것으로 기대합니다. 특히 트레이트를 많이 사용하는 일부 크레이트에서 얻을 막대한 성능 향상이 매우 기대됩니다.
예를 들어, Rust 타입 시스템으로 구현한 체스는 이전 구현에서는 멈추지만 새 구현에서는 1분이 걸립니다. 막대한 성능 이점이 있는 더 실용적인 크레이트도 있습니다. 예를 들어 datafusion 크레이트는 이제 8배 이상 빠르게 컴파일됩니다. 최근 성능 작업에 관한 자세한 내용은 jana의 이 블로그 글을 참고하세요.
다시 한번, 최신 나이틀리로 테스트하고 문제를 발견하면 GitHub 이슈를 등록해 주셔서 감사합니다! 차세대 트레이트 솔버를 곧 완전히 안정화하게 되어 기대됩니다.