I have a trait representing a storage type, with a method to take out that storage as either a boxed value or unboxed value.
I have a second trait to allow conversion from a container using that storage trait into an arbitrary but specific type. I want to do blanket implementations from the container type either boxed or unboxed types.
Here is a simplified example. In my actual code, I'm not actually working with box, but rather with memory allocated in slightly complicated ways, but box demonstrates the issue I've run into:
enum MaybeBoxed<T> {
Boxed(Box<T>),
Unboxed(T),
}
trait Storage {
type Stored;
fn into_inner(self) -> MaybeBoxed<T> where Self: Sized;
}
struct BoxedStorage<T>(Box<T>);
impl<T> Storage for BoxedStorage<T> {
type Stored = T;
fn into_inner(self) -> MaybeBoxed<T> {
MaybeBoxed::Boxed(self.0)
}
}
struct UnboxedStorage<T>(T);
impl<T> Storage for UnboxedStorage<T> {
type Stored = T;
fn into_inner(self) -> MaybeBoxed<T> {
MaybeBoxed::Unboxed(self.0)
}
}
struct Container<S: Storage>(S);
trait MakeInto<T> {
fn make_into(self) -> T;
}
impl<S: Storage> MakeInto<S::Stored> for Container<S> {
fn make_into(self) -> S::Stored {
match self.0.into_inner() {
MaybeBoxed::Boxed(boxed) => *boxed,
MaybeBoxed::Unboxed(unboxed) => unboxed,
}
}
}
impl<S: Storage> MakeInto<Box<S::Stored>> for Container<S> {
fn make_into(self) -> Box<S::Stored> {
match self.0.into_inner() {
MaybeBoxed::Boxed(boxed) => boxed,
MaybeBoxed::Unboxed(unboxed) => Box::new(unboxed),
}
}
}
Now, it seems like these last two impls should be exclusive, because S::Storage is an associated type, so it seems like <Container<S> as MakeInto<S::Stored>> ought to be provably a different type from <Container<S> as MakeInto<Box<S::Stored>>>, since even if S::Stored is Box<U>, the former becomes MakeInto<Box<U>> and the latter becomes MakeInto<Box<Box<U>>.
However, the compiler doesn't agree:
error[E0119]: conflicting implementations of trait `MakeInto<std::boxed::Box<_>>` for type `Container<_>`
--> src/main.rs:47:1
|
38 | impl<S: Storage> MakeInto<S::Stored> for Container<S> {
| ----------------------------------------------------- first implementation here
...
47 | impl<S: Storage> MakeInto<Box<S::Stored>> for Container<S> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Container<_>`
In a previous iteration of this codebase, before I made Storage a generic type, I used a fixed set of storages and defined separate impls for each of them. At that time, I was able to separately define MakeInto<T> and MakeInto<Box<T>> for the same Container<S> with impls like impl<T> MakeInto<T> for Container<BoxedStorage<T>> and impl<T> MakeInto<Box<T>> for Container<BoxedStorage<T>> without running into conflicting impls. playground link.
In current stable rust, is there any way to convince the compiler that these impls are in fact disjoint? Or are they really not disjoint and there's some case where they overlap that I've missed?