闭包类型签名
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.
我正在尝试向 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.