闭包类型签名

Closure type signature

我正在尝试向 Vec<T> 类型添加一个便捷方法。我相信使用特征是执行此操作的正确方法,但也可能是错误的。以下是我到目前为止所拥有的,其中抱怨

trait objects must include the `dyn` keyword

我做错了什么?

trait IndexWhere<T> {
    fn index_where(&self, f: Fn((usize, T)) -> bool) -> Vec<usize>;
}
impl<T> IndexWhere<T> for Vec<T> {
    fn index_where(&self, f: Fn((usize, T)) -> bool) -> Vec<usize> {
        self.iter().enumerate().filter(f).map(|x| x.0).collect()
    }
}

编辑:删除了 T 上的边界,因为我不小心添加了它们

编译器实际上是在告诉您如何修复代码:)

签名应该是:

trait IndexWhere<T> 
{
    fn index_where(&self, f: &dyn Fn(&(usize, &T))-> bool) -> Vec<usize>;
}

impl<T> IndexWhere<T> for Vec<T> {
    fn index_where(&self, f: &dyn Fn(&(usize, &T)) -> bool) -> Vec<usize> {
        self.iter().enumerate().filter(f).map(|x| x.0).collect()
    }
}

另请注意,我在 index_where 签名中将 T 更改为 &T,因为 filter(f) 需要 &(usize, &T)

或者,这也可以通过不使用特征对象而使用泛型来实现:

trait IndexWhere<T> {
    fn index_where<F>(&self, f: F) -> Vec<usize>
    where
        F: Fn(&(usize, &T)) -> bool;
}

impl<T> IndexWhere<T> for Vec<T> {
    fn index_where<F>(&self, f: F) -> Vec<usize>
    where
        F: Fn(&(usize, &T)) -> bool,
    {
        self.iter().enumerate().filter(f).map(|x| x.0).collect()
    }
}

甚至:

trait IndexWhere<T> {
    fn index_where(&self, f: impl Fn(&(usize, &T)) -> bool) -> Vec<usize>;
}

impl<T> IndexWhere<T> for Vec<T> {
    fn index_where(&self, f: impl Fn(&(usize, &T)) -> bool) -> Vec<usize> {
        self.iter().enumerate().filter(f).map(|x| x.0).collect()
    }
}

这与之前的解决方案等效,但使用 impl 关键字代替。

你会发现 Rust 标准库使用泛型类型来实现这种情况,并避免了 trait 对象。您可以查看 Iterator::map 实现作为示例:https://doc.rust-lang.org/src/core/iter/traits/iterator.rs.html#682-685

使用 trait 对象的副作用是使您的方法使用 dynamic dispatch 而不是静态分派,这会产生文档中列出的可能后果:

There is a runtime cost when this lookup happens that doesn’t occur with static dispatch. Dynamic dispatch also prevents the compiler from choosing to inline a method’s code, which in turn prevents some optimizations.