Hello,
I'm struggling to get a blanket impl to work on a trait that has tighter bounds on one of the methods. I'm trying to blanket impl the trait on a mutable reference to T, if T itself implements the trait.
Here is a simplified version of the code:
trait TraitOne<T> {}
trait TraitTwo {
fn bounded<T>(&mut self) -> usize
where Self: TraitOne<T>;
}
impl<T> TraitOne<T> for u64 {}
impl TraitTwo for u64 {
fn bounded<T>(&mut self) -> usize
where Self: TraitOne<T>
{ 0 }
}
impl<T, U> TraitOne<T> for &mut U where U: TraitOne<T> {}
impl<U> TraitTwo for &mut U where U: TraitTwo {
fn bounded<T>(&mut self) -> usize
where Self: TraitOne<T>
{
(**self).bounded()
}
}
It won't let me put tighter bounds on that one method, because that now conflicts with the trait definition.
I figured I could bound the whole impl by the tighter bound, but that doesn't work either because the tighter bound has a generic that is only relevant in the context of the one method, so I get an "unconstrained parameter" error.
Is there a way to solve this without modifying the trait definitions?
Thanks for any ideas.
I don’t see a way without lifting T
to also be a parameter of TraitTwo
. Depending on the specifics of how these traits are used and the types they need to be implemented for, I suppose there might be some dyn Any
tricks available; not in the fully-general case you’ve asked about, though.
1 Like
Here’s the best I could come up with. The only change to the trait definitions is an additional method on TraitOne
with a default implementation, and it has the restriction that &mut T
will only forward T::TraitOne<…>
if T
also implements TraitTwo
:
trait TraitOne<T> {
fn bounded_impl(&mut self) -> usize where Self:TraitTwo {
self.bounded::<T>()
}
}
trait TraitTwo {
fn bounded<T>(&mut self) -> usize
where Self: TraitOne<T>;
}
impl<T, U> TraitOne<T> for &mut U where U: TraitOne<T>+TraitTwo {
fn bounded_impl(&mut self) -> usize {
<U as TraitTwo>::bounded::<T>(&mut **self)
}
}
impl<U> TraitTwo for &mut U where U: TraitTwo {
fn bounded<T>(&mut self) -> usize
where Self: TraitOne<T>
{
self.bounded_impl()
}
}
2 Likes
A little indirection does the job.
Edit: Narrator: "It did not do the job."
Incorrect previous reply
trait Helper<T> {
fn help_bounded(self) -> usize;
}
impl<T, U> Helper<T> for &mut U where U: TraitTwo + TraitOne<T> {
fn help_bounded(self) -> usize {
// This auto-refs and you end up with mutual recursion
(*self).bounded()
}
}
impl<U> TraitTwo for &mut U where U: TraitTwo {
fn bounded<T>(&mut self) -> usize
where Self: TraitOne<T>
{
self.help_bounded()
}
}
Demonstration of recursion.
4 Likes
Ignore my previous reply, it doesn't do what I thought. It adds an auto-ref and recurses forever.
2 Likes
TL;DR: The OP can't work because downstream can implement traits for &mut LocalType
when LocalType
does not implement the trait, meaning the blanket implementations are not "if and only if"s.
For more context, something seemed off in having the bound succeed when it was part of a trait implementation where
clause, but not when it was part of a method where
clause. That is, both the OP and the Helper<T>
trait implementation are trying to prove that U: TraitOne<T>
given U: TraitTwo
and &mut U: TraitOne<T>
.
Eventually it occurred to me that downstream could implement TraitOne<T>
for &mut LocalType
without implementing TraitOne<T>
for LocalType
because &mut LocalType
is considered to be a local type, and you can make use of negative reasoning for coherence with local types -- that is, the implementation is allowed even though your blanket implementation exists, so long as your blanket implementation doesn't apply.
(Similarly you can implement Display
for &LocalType
despite there being a blanket implementation for shared references in std
, so long as you don't also implement Display
for LocalType
.)
I doubted there was a trait solver bug that big, so at that point I actually tested my playground and found out where I'd gone wrong.
But at any rate, the scenario above means that the compiler error in the OP is correct: you can't conclude that U: TraitOne<T>
from &mut U: TraitOne<T>
.
2 Likes