【问题标题】:How to iterate over and filter an array?如何迭代和过滤数组?
【发布时间】:2015-08-08 15:09:01
【问题描述】:

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

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);
}

(Rust playground)

在第一个函数中,我遵循该范围内的迭代器没有所有权,所以我必须在filter 的 lambda 中取一个&amp;x,但我不明白为什么第二个示例带有数组行为不同。

【问题讨论】:

    标签: rust


    【解决方案1】:

    在这种情况下,强制编译器告诉你变量的类型非常有用。让我们通过将闭包参数分配给不兼容的类型来触发类型错误:

    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 的类型是&amp;&amp;{integer} - 对一些 类整数的引用。然后我们可以与之匹配:

    fn hooray() {
        let array = [1, 4, 3, 2, 2];
        let array_iter = array.into_iter();
        array_iter.filter(|&&x| x == 2);
    }
    

    现在的问题变成了“为什么它是对引用的引用”?简短的版本是iterator of an array returns references(参见type Item = &amp;'a T 部分)。此外,Iterator::filter passes a reference 到闭包以防止移动和随后丢失非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);
    }
    

    【讨论】:

    • 谢谢!我选择了这个答案,因为它引导我了解了我可能如何使用编译器自己解决这个问题。我也很欣赏这些链接以了解更多信息。
    【解决方案2】:

    数组是 Rust 中的 [T; N] 类型,适用于任何元素类型 T 和常量数 N。这是一个固定大小的数组。

    Rust 目前没有为数组实现IntoIterator。所有数组都强制切片(类型[T]),因此切片方法在数组上可用。数组还获得切片的迭代器,称为 std::slice::Iter&lt;'a, T&gt; 并具有 &amp;'a T 类型的元素:它通过引用进行迭代!

    这就是为什么Range&lt;i32&gt; 上的into_iter() 会生成i32 的迭代器,而[i32; 5] 上的into_iter() 会生成&amp;i32 的迭代器。

    如果您需要数组的值迭代器和

    • 您使用的是 Rust 1.51 或更新版本,您可以使用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);
      }
      
    • 您使用的是旧版本的 Rust,按值迭代器已在更广泛的生态系统中实现,请参阅 arrayvecliterator

    【讨论】:

      【解决方案3】:

      正如 Shepmaster 和 bluss 所说,您可以查看documentation for the array type,其中提到:

      大小从 0 到 32(含)的数组实现以下功能 如果元素类型允许,则为特征:

      • IntoIterator(针对&amp;[T; N]&amp;mut [T; N]实现)

      正如它所说,这仅用于参考,并反映在其Item 类型中:type Item = &amp;'a Ttype Item = &amp;'a mut T

      【讨论】:

      • Rust 1.47起一般提升32的最大长度。
      猜你喜欢
      • 1970-01-01
      • 2019-06-13
      • 2020-12-23
      • 2017-04-25
      • 1970-01-01
      • 2018-07-30
      • 1970-01-01
      • 2022-11-22
      • 2015-09-25
      相关资源
      最近更新 更多