I am trying to implement a trait on a boxed trait object of the same trait. I've done this before for trait whose methods take &self
which works fine, but not self
.
// The purpose of this trait is to allow for converting any kind of "group" which might eg be nested tuples like below, and convert it into a flat Vec of Items
trait Group {
fn into_vec(self) -> Vec<Box<dyn Item>>;
}
trait Item: Group {}
// Foo is an Item an can also be made into a group
struct Foo {}
impl Item for Foo {}
impl Group for Foo {
fn into_vec(self) -> Vec<Box<dyn Item>> {
vec![Box::new(self)]
}
}
// impl Group for data structures which contain items or other nested structures containing items
impl<A: Group, B: Group> Group for (A, B) {
fn into_vec(self) -> Vec<Box<dyn Item>> {
let mut new_vec = Vec::new();
new_vec.extend(self.0.into_vec().into_iter());
new_vec.extend(self.1.into_vec().into_iter());
new_vec
}
}
// Can create a single group fine
fn get_group() -> impl Group {
(Foo {}, (Foo {}, Foo {}))
}
// Sometimes I might want to return different groups from different braches so need to box them
// However I'm not sure how to implement this. self.into_vec() is an ifinite recursion, and can't deref self either.
impl Group for Box<dyn Group> {
fn into_vec(self) -> Vec<Box<dyn Item>> {
(*self).into_vec()
}
}
fn get_group_conditonal(condition: bool) -> impl Group {
if condition {
Box::new((Foo {}, (Foo {}, Foo {}))) as Box<dyn Group>
} else {
Box::new(Foo {}) as Box<dyn Group>
}
}
I realise that in this particular example I could change fn get_*()
functions to return Box<dyn Group>
to solve the problem. However, the rest of the API has functions which take inputs as impl Group
. If I can't impl Group for Box<dyn Group>
then this would carry into the rest of the API and require that all functions only take boxed trait objects as inputs, rather than impl Group
, and I want to avoid this if possible.
The only solution I can come up with and that's commonly recommended is to have the trait method take Box<Self>
instead of Self
:
trait Group {
fn into_vec(self: Box<Self>) -> Vec<Box<dyn Item>>;
}
then
impl Group for Box<dyn Group> {
fn into_vec(self: Box<Self>) -> Vec<Box<dyn Item>> {
(*self).into_vec()
}
}
works as you can see on the Playground because it doesn't have to deal with a raw dyn Group