如何正确地将迭代器传递给Rust中的函数

如何正确地将迭代器传递给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

我想把迭代器传递给一个函数,然后函数从迭代器中计算出一些值。 我不确定这样一个函数的健壮签名会是什么样子。 假设我想迭代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: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

    克隆方法是否实际复制了切片?或