如何正确地将迭代器传递给Rust中的函数
我想把迭代器传递给一个函数,然后函数从迭代器中计算出一些值。 我不确定这样一个函数的健壮签名会是什么样子。 假设我想迭代f64。 你可以在操场上找到代码: 我的第一次尝试是如何正确地将迭代器传递给Rust中的函数,rust,iterator,Rust,Iterator,我想把迭代器传递给一个函数,然后函数从迭代器中计算出一些值。 我不确定这样一个函数的健壮签名会是什么样子。 假设我想迭代f64。 你可以在操场上找到代码: 我的第一次尝试是 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() } fn-dot(a:i
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()
}
fn-dot(a:impl-std::iter::Iterator,b:impl-std::iter::iter::Iterator)->f64{
a、 zip(b).map(|(x,y)| x*y).sum()
}
如果我们尝试在片上迭代,则无法编译
所以你可以
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()
}
fn-dot,b:impl-std::iter::Iterator您可以使用第一个dot
实现在片上进行迭代,如下所示:
dot([0,1,2].iter().cloned(),[0,1,2].iter().cloned());
()
或
dot([0,1,2].iter().copied(),[0,1,2].iter().copied());
()
为什么编译器需要这里的livetime参数
据我所知,rust中的每个引用都有一个生命周期,但编译器可以在某些情况下推断出它的简单生命周期。然而,在本例中,编译器还不够智能,因此您需要告诉它迭代器生成的引用的寿命
还有更多的病例吗
您可以始终使用迭代器方法,如上面的解决方案,在f64
上获得迭代器,因此您不必处理生命周期或泛型
解决这个问题的最佳做法是什么
我建议使用第一个版本(因此将迭代器转换为迭代器的任务留给调用方),因为它是最可读的。您可以使用第一个点
实现在片上进行迭代,如下所示:
dot([0,1,2].iter().cloned(),[0,1,2].iter().cloned());
()
或
dot([0,1,2].iter().copied(),[0,1,2].iter().copied());
()
为什么编译器需要这里的livetime参数
据我所知,rust中的每个引用都有一个生命周期,但编译器可以在某些情况下推断出它的简单生命周期。然而,在本例中,编译器还不够智能,因此您需要告诉它迭代器生成的引用的寿命
还有更多的病例吗
您可以始终使用迭代器方法,如上面的解决方案,在f64
上获得迭代器,因此您不必处理生命周期或泛型
解决这个问题的最佳做法是什么
我建议使用第一个版本(因此将迭代器转换为迭代器留给调用方),因为它是最可读的。没有正确的方法编写一个可以接受迭代器的函数,但是,我们可以应用一些一般原则,使您的函数通用且易于使用
编写接受impl到迭代器的函数
。因为所有的迭代器
都实现了到迭代器
,所以严格来说,这比只接受impl迭代器
的函数更通用
借用是对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()
}
fn点(a:I,b:J)->f64
哪里
I:迭代器,
J:迭代器,
I::项目:借用,
J::项目:借用,
{
a、 进入()
.zip(b)
.map(|(x,y)| x.borrow()*y.borrow())
.sum()
}
然而,我也同意这一点,即并非在所有情况下都需要这种普遍性。这个dot
很好,因为它可以接受范围很广的参数,所以您可以调用dot(&somevec,someiterator)
,它就可以工作了。它针对呼叫站点的可读性进行了优化。另一方面,如果您发现借用
特性使定义过于复杂,那么优化定义的可读性并强制调用方有时添加.iter().copied()
也没什么错。关于第一个点
函数,我唯一肯定要改变的是将迭代器
替换为迭代器没有正确的方法来编写一个可以接受迭代器
的函数,但是我们可以应用一些一般原则,使函数通用且易于使用
编写接受impl到迭代器的函数
。因为所有的迭代器
都实现了到迭代器
,所以严格来说,这比只接受impl迭代器
的函数更通用
借用是对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()
}
fn点(a:I,b:J)->f64
哪里
I:迭代器,
J:迭代器,
I::项目:借用,
J::项目:借用,
{
a、 进入()
.zip(b)
.map(|(x,y)| x.borrow()*y.borrow())
.sum()
}
然而,我也同意这一点,即并非在所有情况下都需要这种普遍性。这个dot
很好,因为它可以接受范围很广的参数,所以您可以调用dot(&somevec,someiterator)
,它就可以工作了。它针对呼叫站点的可读性进行了优化。另一方面,如果您发现借用
特性使定义过于复杂,那么优化定义的可读性并强制调用方有时添加.iter().copied()
也没什么错。关于第一个dot
函数,我唯一肯定要改变的是将Iterator
替换为IntoIterator
克隆方法是否实际复制了切片?或