rust 按元素类型过滤向量

nxagd54h  于 2023-11-19  发布在  其他
关注(0)|答案(2)|浏览(123)

有没有办法实现下面的功能?

trait X {}

struct A;
struct B;

impl X for A {}
impl X for B {}

/// Only keep elements of type A
fn filter(list: Vec<Box<dyn X>>) -> Vec<Box<A>> {
    todo!();
}

字符串
在dart中,这可能要容易得多:

List<A> filter(list: List<X>) {
     return list.whereType<A>();
}

7ajki6be

7ajki6be1#

你需要使用某种向下转换。如How to get a reference to a concrete type from a trait object?中所解释的,稳定的方法是添加一个as_any()方法:

use std::any::Any;

trait X {
    fn as_any_box(self: Box<Self>) -> Box<dyn Any>;
}

struct A;
struct B;

impl X for A {
    fn as_any_box(self: Box<Self>) -> Box<dyn Any> {
        self
    }
}
impl X for B {
    fn as_any_box(self: Box<Self>) -> Box<dyn Any> {
        self
    }
}

/// Only keep elements of type A
fn filter(list: Vec<Box<dyn X>>) -> Vec<Box<A>> {
    list.into_iter()
        .filter_map(|v| v.as_any_box().downcast::<A>().ok())
        .collect()
}

字符串

yk9xbfzb

yk9xbfzb2#

Chayim对你的问题给出了很好的回答,但我想补充一点,如果你能稍微改变你的需求,使用 enum dispatch 而不是 dynamic dispatch,你的问题就会变得微不足道。

use std::iter::Extend;

trait X {}

struct A;
struct B;

impl X for A {}
impl X for B {}

enum DispatchX {
    A(A),
    B(B),
}

impl X for DispatchX {
    // delegate all X's methods to present variant
}

fn filterA(list: Vec<DispatchX>) -> Vec<A> {
    let mut result = Vec::with_capacity(list.len());
    let data = list
        .into_iter()
        .filter_map(|x| {
            match x {
                DispatchX::A(a) => Some(a),
                _ => None,
         }});
    result.extend(data);
    data
}

字符串
你可以用crate enum_dispatch自动创建DispatchX。当然,这只会在trait X的实现者有限并且都在你的crate本地的情况下起作用(或者至少你不希望任何消费者创建一个)。
还要注意的是,我选择了extend,而不是collecting的迭代器。这将导致没有重新分配,但如果你有很少的A项目在你的list,你可能会分配更多的内存比你需要的。如果A s到B s的比例是可预测的,你可以操纵给Vec::with_capacity的容量来提高内存使用率。

相关问题