我想将迭代器传递给一个函数,然后该函数从这些迭代器中计算一些值。我不确定这样一个函数的健壮签名会是什么样子。假设我想迭代 f64。您可以在操场上找到代码:https://play.rust-lang.org/?version =stable&mode=debug&edition=2018&gist=c614429c541f337adb102c14518cf39e
我的第一次尝试是
fn dot(a : impl std::iter::Iterator<Item = f64>,b : impl std::iter::Iterator<Item = f64>) -> f64 {
a.zip(b).map(|(x,y)| x*y).sum()
}
Run Code Online (Sandbox Code Playgroud)
如果我们尝试迭代切片,则编译失败
所以你可以做
fn dot<'a>(a : impl std::iter::Iterator<Item = &'a f64>,b : impl std::iter::Iterator<Item = &'a f64>) -> f64 {
a.zip(b).map(|(x,y)| x*y).sum()
}
Run Code Online (Sandbox Code Playgroud)
如果我尝试迭代映射的范围,则无法编译。(为什么编译器在这里需要livetime参数?)
所以我尝试接受参考文献,而不是一般的参考文献:
pub fn dot<T : Borrow<f64>, U : Borrow<f64>>(a : impl std::iter::Iterator::<Item = T>, b: impl std::iter::Iterator::<Item = U>) -> f64 {
a.zip(b).map(|(x,y)| x.borrow()*y.borrow()).sum()
}
Run Code Online (Sandbox Code Playgroud)
这适用于我尝试过的所有组合,但它非常冗长,我并不真正理解它的每个方面。
还有更多案例吗?
解决这个问题的最佳实践是什么?
没有正确的方法来编写可以接受 s 的函数Iterator,但是我们可以应用一些通用原则来使您的函数通用且易于使用。
impl IntoIterator<...>. 因为所有Iterators 都实现了IntoIterator,所以这比仅接受的函数更通用impl Iterator<...>。Borrow<T>T是抽象和的正确方法&T。where当特征界限变得冗长时,如果将它们写在子句中而不是行内,通常会更容易阅读。考虑到这些,我可能会这样写dot:
fn dot<I, J>(a: I, b: J) -> f64
where
I: IntoIterator,
J: IntoIterator,
I::Item: Borrow<f64>,
J::Item: Borrow<f64>,
{
a.into_iter()
.zip(b)
.map(|(x, y)| x.borrow() * y.borrow())
.sum()
}
Run Code Online (Sandbox Code Playgroud)
不过,我也同意TobiP64 的回答,因为这种通用性可能并不是在每种情况下都需要。这dot很好,因为它可以接受广泛的参数,所以你可以调用它dot(&some_vec, some_iterator),它就可以工作。它针对调用站点的可读性进行了优化。另一方面,如果您发现该Borrow特征使定义过于复杂,那么优化定义的可读性并强制调用者有时添加 a 并没有什么问题.iter().copied()。关于第一个函数,我唯一肯定会改变的dot就是替换Iterator为IntoIterator.