如何迭代并过滤一个数组?

58

我正在尝试编写一个涉及对数组进行过滤和折叠的程序。我一直在使用The Rust Programming Language第一版作为参考,但我不理解当我在数组上形成迭代器时会发生什么。这里是一个例子:

fn compiles() {
    let range = (1..6);
    let range_iter = range.into_iter();
    range_iter.filter(|&x| x == 2);
}

fn does_not_compile() {
    let array = [1, 4, 3, 2, 2];
    let array_iter = array.into_iter();
    //13:34 error: the trait `core::cmp::PartialEq<_>` is not implemented for the type `&_` [E0277]
    array_iter.filter(|&x| x == 2);
}

fn janky_workaround() {
    let array = [1, 4, 3, 2, 2];
    let array_iter = array.into_iter();
    // Note the dereference in the lambda body
    array_iter.filter(|&x| *x == 2);
}

在第一个函数中,我注意到范围上的迭代器不会拥有所有权,因此我必须在filter的lambda中使用&x,但我不明白为什么数组的第二个示例表现不同。(在第一函数中,我注意到范围上的迭代器不会拥有所有权,因此我必须在filter的lambda中使用&x,但我不明白为什么数组的第二个示例表现不同。)

(Rust playground)

3个回答

50
在这种情况下,强制编译器告诉您变量的类型非常有用。通过将闭包参数分配给不兼容的类型,让我们触发类型错误:
array_iter.filter(|x| { let _: () = x; x == 2 });

这将会失败,并显示如下错误:

error[E0308]: mismatched types
 --> src/lib.rs:4:41
  |
4 |     array_iter.filter(|x| { let _: () = x; x == 2 });
  |                                    --   ^ expected `()`, found `&&{integer}`
  |                                    |
  |                                    expected due to this

现在我们知道x的类型是&&{integer} - 一个指向某种整数的引用的引用。然后我们可以根据它进行匹配:
fn hooray() {
    let array = [1, 4, 3, 2, 2];
    let array_iter = array.into_iter();
    array_iter.filter(|&&x| x == 2);
}

现在的问题是“为什么是引用的引用”?简而言之,数组的迭代器返回引用(请参见type Item = &'a T 部分)。此外,Iterator::filter 会传递一个引用到闭包中,以防止移动并且在随后丢失非Copy类型。 在Rust 1.51中,您可以使用array::IntoIter获取按值迭代器。
fn hooray() {
    let array = [1, 4, 3, 2, 2];
    let array_iter = std::array::IntoIter::new(array);
    array_iter.filter(|&x| x == 2);
}

1
谢谢!我选择了这个答案,因为它向我展示了如何使用编译器自己找出答案。我也很感激提供的链接以便更深入地学习。 - WillEngler

36

在Rust中,数组是类型[T; N],其中T是任何元素类型,N是一个常量数字。它是一个固定大小的数组。

目前,Rust没有为数组实现IntoIterator。所有数组都可以强制转换为切片(类型[T]),因此可以使用切片方法来操作数组。由于这个原因,数组也会得到切片的迭代器,称为std::slice::Iter<'a, T>,其元素类型为&'a T:它通过引用进行迭代!

这就是为什么对于Range<i32>上的into_iter()会产生一个i32的迭代器,而对于[i32; 5]上的into_iter()会产生一个&i32的迭代器。

如果您需要按值迭代数组和

  • You are using Rust 1.51 or newer, you can use array::IntoIter:

    fn does_now_compile() {
        let array = [1, 4, 3, 2, 2];
        let array_iter = std::array::IntoIter::new(array);
        array_iter.filter(|&x| x == 2);
    }
    
  • You are using older versions of Rust, by-value iterators have been implemented in the broader ecosystem, see arrayvec and literator.


3
如Shepmaster和bluss所说,您可以检查数组类型文档,其中提到:
大小从0到32(包括)的数组,如果元素类型允许,则实现以下特征:
- IntoIterator(对于&[T; N]和&mut [T; N]实现)
正如它所说,这仅适用于引用,并反映在其项类型中:type Item = &'a T和type Item = &'a mut T。

通常情况下,最大长度为32在Rust 1.47中被取消了限制。 - Shepmaster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接