对具有相同特征的特征对象进行 impl trait with move 方法

impl trait with move method for trait object of same trait

提问人:Max888 提问时间:9/9/2023 最后编辑:kmdrekoMax888 更新时间:9/21/2023 访问量:48

问:

我正在尝试在具有相同特征的盒装特征对象上实现特征。我以前做过这样的任务,其方法采用哪些工作正常,但不是.&selfself

// 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>
    }
}

我意识到在这个特定示例中,我可以更改函数以返回以解决问题。但是,API 的其余部分具有将输入作为 .如果我不能,那么这将带入 API 的其余部分,并要求所有函数仅将盒装特征对象作为输入,而不是 ,如果可能的话,我想避免这种情况。fn get_*()Box<dyn Group>impl Groupimpl Group for Box<dyn Group>impl Group

性状 所有权 特征对象

评论

1赞 cafce25 9/9/2023
nit:你不需要调用调用,因为 extend 需要 I: IntoIter<Item = T> ( 也有效。into_iter()extendnew_vec.extend(self.0.into_vec())

答:

4赞 cafce25 9/9/2023 #1

我能想到的唯一解决方案,也是通常推荐的解决方案,是采用特征方法而不是:Box<Self>Self

trait Group {
    fn into_vec(self: Box<Self>) -> Vec<Box<dyn Item>>;
}

然后

impl Group for Box<dyn Group> {
    fn into_vec(self: Box<Self>) -> Vec<Box<dyn Item>> {
        (*self).into_vec()
    }
}

正如您在 Playground 上看到的那样工作,因为它不必处理原始dyn Group

评论

0赞 Max888 9/9/2023
谢谢。我已经实现了这一点。这显然意味着我调用 .into_vec() 的任何地方我都需要对接收器进行装箱(如果还没有的话),但我猜没有办法解决这个问题,并且仍然比不能和不得不对任何地方的特征对象进行分组要好得多。impl Group for Box<dyn Group>Box
3赞 Chayim Friedman 9/10/2023 #2

如果您不想为每次通话支付装箱费用,您可以有两种方法:

// 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>>;
    fn into_vec_dyn(self: Box<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)]
    }
    
    fn into_vec_dyn(self: Box<Self>) -> Vec<Box<dyn Item>> {
        self.into_vec()
    }
}

// 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
    }
    
    fn into_vec_dyn(self: Box<Self>) -> Vec<Box<dyn Item>> {
        self.into_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>> {
        <dyn Group>::into_vec_dyn(self)
    }
    
    fn into_vec_dyn(self: Box<Self>) -> Vec<Box<dyn Item>> {
        self.into_vec()
    }
}

当然,为每个实现显式写出方法是......不太好,但我们可以通过拆分特征来解决这个问题:

trait GroupIntoVecDyn {
    fn into_vec_dyn(self: Box<Self>) -> Vec<Box<dyn Item>>;
}

impl<T: Group> GroupIntoVecDyn for T {
    fn into_vec_dyn(self: Box<Self>) -> Vec<Box<dyn Item>> {
        self.into_vec()
    }
}

// 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: GroupIntoVecDyn {
    fn into_vec(self) -> Vec<Box<dyn Item>>;
}

因为是 的超级特征,你可以调用它的方法(甚至在 上),并且由于是一揽子实现,你实际上不必实现它。GroupIntoVecDynGroupdyn Group