I have two traits:
trait Foo {}
trait Bar {}
struct FooImpl;
impl Foo for FooImpl {}
struct BarImpl;
impl Bar for BarImpl {}
And a third type I want to convert into:
struct Baz;
trait IntoBaz {
fn into(self) -> Baz;
}
I can't define two impl
s of IntoBaz
for the two traits because of coherence, so I wrap one instead:
struct FooWrapper<F>(F)
where
F: Sized;
impl<F: Foo + Sized> From<F> for FooWrapper<F> {
fn from(f: F) -> FooWrapper<F> {
FooWrapper(f)
}
}
impl<F: Foo + Sized> IntoBaz for FooWrapper<F> {
fn into(self) -> Baz {
Baz
}
}
And I don't wrap the other:
impl<B: Bar> IntoBaz for B {
fn into(self) -> Baz {
Baz
}
}
fn do_thing<B: IntoBaz>(b: &B) {}
fn main() {
do_thing(&BarImpl);
}
So far so good, but why doesn't this line work?
fn main() {
do_thing(&FooImpl);
}
I'm trying to add io::Write
support to a library with fmt::Write
support without introducing a breaking change.
The easiest way would be to define some internal Write
trait which covers the shared behaviour, but the coherence problem means I can't just write From<io::Write>
instances to the internal trait.
I've tried wrapping io::Write
instances, to make the coercion explicit so the compiler prioritises the shorter path and avoids the incoherence, but it won't auto-coerce using the From
instance.
Look at the error message:
error[E0277]: the trait bound `FooImpl: Bar` is not satisfied
--> src/main.rs:48:5
|
48 | do_thing(&FooImpl);
| ^^^^^^^^ the trait `Bar` is not implemented for `FooImpl`
|
= note: required because of the requirements on the impl of `IntoBaz` for `FooImpl`
note: required by `do_thing`
--> src/main.rs:45:1
|
45 | fn do_thing<B: IntoBaz>(b: &B) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
It's saying that FooImpl
doesn't have an implementation of Bar
, which is a requirement of your blanket IntoBaz for B
implementation.
The FooWrapper
implementation is not relevant because FooImpl
is not the same as FooWrapper
. The From
and Into
traits provide a way to convert between types, but it doesn't happen automatically.
You might try adding an implementation for things that can be converted into FooWrapper
, but this won't work because the implementations could overlap (and specialization is not stable yet).
But you can define an IntoBaz
implementation for just FooImpl
:
impl IntoBaz for FooImpl {
fn into(self) -> Baz {
IntoBaz::into(FooWrapper::from(self))
}
}
Which will make your code compile:
fn main() {
do_thing(&BarImpl);
do_thing(&FooImpl);
}