Trait 바운드와 Projection 바운드를 분리해 두는 이유
T: Foo<AssocA = u32, AssocB = i32> where 바운드가 주어지면, 현재는 이를 Trait(Foo<T>) 바운드와 별도의 Projection(<T as Foo>::AssocA, u32) 및 Projection(<T as Foo>::AssocB, i32) 바운드로 로워링합니다. 왜 이를 대신 단일한 Trait(Foo[T], [AssocA = u32, AssocB = u32] 바운드로 표현하지 않습니까?
Projection 바운드를 증명하는 방식은 대응하는 Trait 바운드를 증명하는 데 직접 의존합니다: 기존 솔버 새 솔버.
트레이트가 구현되어 있는지 검사하고 그 연관 타입(또는 그것을 계산하는 방법)을 반환하는 단일 구현만 두는 편이 더 합리적일 것처럼 느껴집니다.
안타깝게도 이는 상당히 어려운데, 정규화에 사용하는 후보가 대응하는 트레이트 바운드에 사용하는 후보와 다를 수 있기 때문입니다. 별칭 바운드 vs where 바운드 및 전역 where 바운드 vs impl을 참조하십시오.
그렇게 할 수 없는 미묘한 이유가 몇 가지 더 있습니다. 가장 어처구니없는 이유는 경직된(rigid) 별칭에 관한 것으로, 이를 정규화하려 할 때 트레이트 바운드를 증명하는 과정에서 생기는 라이프타임 제약을 전혀 고려하지 않는다는 점입니다. 이는 바인더에 대한 가정이 부족하기 때문에 필요한 것이며 - https://github.com/rust-lang/trait-system-refactor-initiative/issues/177 - 장기적으로는 수정되어야 합니다.
별개의 문제로, 현재 Trait 목표에 대해 또는 가려진(shadowed) Projection 후보 안에서 연관 타입의 type_of를 가져오면 RPITIT에 대해 쿼리 순환이 발생할 수 있습니다. https://github.com/rust-lang/trait-system-refactor-initiative/issues/185 를 참조하십시오.
일부 내장 impl의 경우 후보들 사이에 미세한 차이도 있는데, 이러한 차이는 모두 대체로 바람직하지 않아 보이며, 저는 이를 여기서 통합된 접근 방식을 사용했다면 수정되었을 버그라고 봅니다.
마지막으로, 이러한 분리가 없으면 where 절을 로워링하는 작업이 더 번거로워집니다. 현재 시스템에서는 중복된 where 절이 있어도 문제가 되지 않으며, 이는 상위 트레이트 바운드를 전개(elaborate)할 때 쉽게 발생할 수 있습니다. 그렇게 되면 모든 연관 타입 제약을 반드시 병합해야 합니다. 예를 들면 다음과 같습니다.
#![allow(unused)]
fn main() {
trait Super {
type A;
type B;
}
trait Trait: Super<A = i32> {}
// how to elaborate Trait<B = u32>
}
혹은 더 나쁜 경우도 있습니다.
#![allow(unused)]
fn main() {
trait Super<'a> {
type A;
type B;
}
trait Trait<'a>: Super<'a, A = i32> {}
// how to elaborate
// T: Trait<'a> + for<'b> Super<'b, B = u32>
}