Here is a stripped-down reproduction. The idea is to avoid having to repeat a bunch of bounds on an associated type by using a subtrait to add those as implied bounds on the associated type of the supertrait.
It works great (foo
and bar
in the example), but apparently doesn't compose (baz
).
- Is this a fundamental issue with my approach?
- Is this a non-fundamental limitation in rustc that might someday be improved?
- Is there a workaround?
Note that the key issue, afaik, is that rustc understands that W::X = W_Ord::X_Ord, and that W::X = W_Clone::X_Clone, but it has trouble transitively unifying W_Ord::X_Ord = W_Clone::X_Clone
/?version=stable&mode=debug&edition=2024&gist=ab5f8a6621c82c84da2b50b9eb5b3048
trait W {
type X;
}
/// Trait which provides an implied bound that
// W::X: Ord, with an automatic blanket impl
trait W_Ord: W<X=Self::X_Ord> {
type X_Ord: Ord;
}
impl<T:W > W_Ord for T where T::X: Ord {
type X_Ord = T::X;
}
/// Trait which provides an implied bound that
// W::X: Clone, with an automatic blanket impl
trait W_Clone: W<X=Self::X_Clone> {
type X_Clone: Clone;
}
impl<T:W > W_Clone for T where T::X: Clone {
type X_Clone = T::X;
}
impl W for u32 {
type X = u32;
}
/// by using W_Ord, we can rely on T::X: Ord
/// without having to put it in a where clause
fn foo<T: W_Ord>(t: T::X) { t == t; }
/// by using W_Clone, we can rely on T::X: Clone
/// without having to put it in a where clause
fn bar<T: W_Clone>(t: T::X) { t.clone(); }
/// using both at once produces the following error:
/*
error[E0284]: type annotations needed
--> src/lib.rs:27:31
|
51 | fn baz<T: W_Ord + W_Clone>(t: T::X) { t == t.clone(); }
| ^^^^ cannot infer type
|
= note: cannot satisfy `<T as W>::X == _`
error[E0282]: type annotations needed
--> src/lib.rs:27:44
|
51 | fn baz<T: W_Ord + W_Clone>(t: T::X) { t == t.clone(); }
| ^ cannot infer type
*/
fn baz<T: W_Ord + W_Clone>(t: T::X) { t == t.clone(); }
Here is a stripped-down reproduction. The idea is to avoid having to repeat a bunch of bounds on an associated type by using a subtrait to add those as implied bounds on the associated type of the supertrait.
It works great (foo
and bar
in the example), but apparently doesn't compose (baz
).
- Is this a fundamental issue with my approach?
- Is this a non-fundamental limitation in rustc that might someday be improved?
- Is there a workaround?
Note that the key issue, afaik, is that rustc understands that W::X = W_Ord::X_Ord, and that W::X = W_Clone::X_Clone, but it has trouble transitively unifying W_Ord::X_Ord = W_Clone::X_Clone
https://play.rust-lang./?version=stable&mode=debug&edition=2024&gist=ab5f8a6621c82c84da2b50b9eb5b3048
trait W {
type X;
}
/// Trait which provides an implied bound that
// W::X: Ord, with an automatic blanket impl
trait W_Ord: W<X=Self::X_Ord> {
type X_Ord: Ord;
}
impl<T:W > W_Ord for T where T::X: Ord {
type X_Ord = T::X;
}
/// Trait which provides an implied bound that
// W::X: Clone, with an automatic blanket impl
trait W_Clone: W<X=Self::X_Clone> {
type X_Clone: Clone;
}
impl<T:W > W_Clone for T where T::X: Clone {
type X_Clone = T::X;
}
impl W for u32 {
type X = u32;
}
/// by using W_Ord, we can rely on T::X: Ord
/// without having to put it in a where clause
fn foo<T: W_Ord>(t: T::X) { t == t; }
/// by using W_Clone, we can rely on T::X: Clone
/// without having to put it in a where clause
fn bar<T: W_Clone>(t: T::X) { t.clone(); }
/// using both at once produces the following error:
/*
error[E0284]: type annotations needed
--> src/lib.rs:27:31
|
51 | fn baz<T: W_Ord + W_Clone>(t: T::X) { t == t.clone(); }
| ^^^^ cannot infer type
|
= note: cannot satisfy `<T as W>::X == _`
error[E0282]: type annotations needed
--> src/lib.rs:27:44
|
51 | fn baz<T: W_Ord + W_Clone>(t: T::X) { t == t.clone(); }
| ^ cannot infer type
*/
fn baz<T: W_Ord + W_Clone>(t: T::X) { t == t.clone(); }
Share
Improve this question
asked Mar 14 at 18:32
ajpajp
2,52518 silver badges23 bronze badges
1 Answer
Reset to default 1I think you've over-complicated things. This simpler example works:
trait W {
type X;
}
trait W_Ord: W<X: Ord> {}
impl<T: W> W_Ord for T where T::X: Ord {}
trait W_Clone: W<X: Clone> {}
impl<T: W> W_Clone for T where T::X: Clone {}
impl W for u32 {
type X = u32;
}
fn baz<T: W_Ord + W_Clone>(t: T::X) { t == t.clone(); }
I believe by having X_Ord
and X_Clone
as additional associated types and implying the bounds on those separately, its harder for the compiler to link those bounds to T::X
.