Trait 境界と Projection 境界を分離すること
T: Foo<AssocA = u32, AssocB = i32> という where 境界が与えられた場合、現在はこれを Trait(Foo<T>) と、別々の Projection(<T as Foo>::AssocA, u32) および Projection(<T as Foo>::AssocB, i32) 境界へ lower しています。
代わりに、なぜこれを単一の Trait(Foo[T], [AssocA = u32, AssocB = u32] 境界として表現しないのでしょうか?
Projection 境界を証明する方法は、対応する Trait 境界を証明することに直接依存しています: 旧ソルバー 新ソルバー。
trait が実装されているかどうかをチェックし、その関連型(を計算する方法)を返す単一の実装だけを持つほうが理にかなっているように感じられます。
残念ながら、これはかなり困難です。対応する trait 境界とは異なる候補を正規化に使う可能性があるためです。 alias-bound と where-bound および グローバル where 境界と impl を参照してください。
そうできない理由には、他にも微妙なものがいくつかあります。 最も愚かなものは rigid alias に関するものです。 それらを正規化しようとしても、trait 境界を証明する際の lifetime 制約は一切考慮されません。 これは binder に関する仮定が不足しているために必要です - https://github.com/rust-lang/trait-system-refactor-initiative/issues/177 - そして長期的には修正されるべきです。
別の問題として、現時点では、
Trait ゴールや shadow された Projection 候補で関連型の type_of を取得すると、RPITIT でクエリサイクルが発生する可能性があります。
https://github.com/rust-lang/trait-system-refactor-initiative/issues/185 を参照してください。
また、一部の builtin impl の候補間にはわずかな違いがあります。これらはどれも一般的には望ましくないように見え、ここで統一されたアプローチがあれば修正されるバグだと私は考えています。
最後に、この分割がないと where-clause の lowering がより面倒になります。 現在のシステムでは、重複した where-clause があっても問題にはならず、super trait 境界を展開するときに簡単に発生し得ます。 その場合、すべての関連型制約をマージするようにしなければなりません。例:
#![allow(unused)]
fn main() {
trait Super {
type A;
type B;
}
trait Trait: Super<A = i32> {}
// Trait<B = u32> をどのように展開するか
}
あるいは、さらに悪い例です
#![allow(unused)]
fn main() {
trait Super<'a> {
type A;
type B;
}
trait Trait<'a>: Super<'a, A = i32> {}
// どのように展開するか
// T: Trait<'a> + for<'b> Super<'b, B = u32>
}