I want to implement some trait on types which implemented Borrow
:
struct Foo {}
struct Bar {}
trait Baz {}
impl <T: Borrow<Foo>> Baz for T {}
impl <T: Borrow<Bar>> Baz for T {}
But the compiler said the implementation is conflicted.
I didn't figure out where is the overlapped implementations, T: Borrow<Foo>
are types like Arc<Foo>
, Box<Foo>
, and T: Borrow<Bar>
are types like Arc<Bar>
, Box<Bar>
, they are different types.
So where is the overlapping happens?
P.S. Maybe this an X/Y problem, what I want is that the type
pub struct Wrapper<T: Baz> {
baz: T
}
Can accept types like Foo
, Arc<Foo>
, Box<Foo>
(any types which can be refered as Foo
), as long as Foo
implements Baz
. So do the Bar
type.
There's nothing stopping FooBar
from having both Borrow<Foo>
and Borrow<Bar>
.
So in this case, how should I accomplish my goal? The trait Baz
is private, and Foo
and Bar
are types I can control, and there is no situation like FooBar
in my project, any idea?
There's no way, since the FooBar
type might be in the consumer crate.
I update my question, maybe this is an X/Y problem.
It's tedious, but if Baz
is only for local use, then you can implement it for each of the variants that you care about, rather than trying to have a blanket Baz for T
. Macros can make this a little easier.
That's what I'm doing now, but I have a feeling that the Baz
will finally be public someday
I'm seeking unstable features and found one called "associated_type_bounds", but it seems not solving my problem.
You can use Deref
instead of Borrow
, because Deref
can't have conflicting implementations. This doesn't entirely help, because the compiler doesn't fully reason about that to see that they're exclusive, but you can then also say that the Deref
s that qualify are not Derefs
to either Foo
or Bar
but Deref
s to something that implements Baz
— and, given that we're using Deref
, we do need a separate impl Baz for Foo
since Foo
itself doesn't deref to Foo
.
That is, this will compile:
use std::ops::Deref;
struct Foo {}
struct Bar {}
trait Baz {}
impl Baz for Foo {}
impl Baz for Bar {}
impl<T> Baz for T
where
T: Deref,
T::Target: Baz,
{}
However, it's more usual to do this kind of forwarding impl
explicitly for individual smart pointer types, not to write a blanket impl of this type. I don't recall this second whether there's a concrete reason to prefer that, though in general, having a blanket impl narrows what other impls can be written.
1 Like
Generally, how the standard library handles it is to have a separate impl
for each container type. In this case, it would look something like this:
use std::{rc::Rc, sync::Arc};
struct Foo {}
struct Bar {}
trait Baz {}
impl Baz for Foo {}
impl Baz for Bar {}
impl<T: Baz + ?Sized> Baz for &T {}
impl<T: Baz + ?Sized> Baz for &mut T {}
impl<T: Baz + ?Sized> Baz for Box<T> {}
impl<T: Baz + ?Sized> Baz for Rc<T> {}
impl<T: Baz + ?Sized> Baz for Arc<T> {}
1 Like