按元素类型过滤向量

Filter vector by element type

提问人:Lomírus 提问时间:11/3/2023 更新时间:11/3/2023 访问量:53

问:

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

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>();
}
过滤器 特征

评论


答:

3赞 Chayim Friedman 11/3/2023 #1

你需要使用某种向下的解释。如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()
}
3赞 Aleksander Krauze 11/3/2023 #2

Chayim 对你的确切问题给出了很好的答案,但我想补充一点,如果你能够稍微改变你的要求并使用枚举调度而不是动态调度,你的问题将变得微不足道。

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
}

您可以使用板条箱enum_dispatch自动创建。当然,只有当特征的实现者有限,并且所有实现者都是你的板条箱的本地实现者(或者至少你不希望你的任何消费者创建一个)时,这才有效。DispatchXX

另请注意,我没有选择迭代器,而是扩展先前分配的向量。这将导致不会重新分配,但如果 中的项目很少,则分配的内存可能会比您需要的多得多。如果 s 与 s 的比率是可预测的,则可以操作给定的容量以提高内存使用率。collectingAlistABVec::with_capacity