数组的 IntoIterator

总结

  • 在*所有*版本中,数组都实现了 IntoIterator
  • 在 Rust 2015 和 Rust 2018 中,当使用方法调用语法(即 array.into_iter())时,对 IntoIterator::into_iter 的调用是*隐藏*的。因此,array.into_iter() 仍然像以前一样解析为 (&array).into_iter()
  • 在 Rust 2021 中,array.into_iter() 的含义更改为对 IntoIterator::into_iter 的调用。

详情

在 Rust 1.53 之前,只有数组的*引用*实现了 IntoIterator。这意味着您可以迭代 &[1, 2, 3]&mut [1, 2, 3],但不能直接迭代 [1, 2, 3]

for &e in &[1, 2, 3] {} // Ok :)

for e in [1, 2, 3] {} // Error :(

一直是一个长期存在的问题,但解决方案并不像看起来那么简单。仅仅添加特征实现就会破坏现有代码。array.into_iter() 今天已经可以编译了,因为由于方法调用语法的工作原理,它隐式地调用了 (&array).into_iter()。添加特征实现将改变其含义。

通常,这种类型的破坏(添加特征实现)被归类为“轻微”且可接受的。但在这种情况下,会被它破坏的代码太多了。

许多人建议“只在 Rust 2021 中为数组实现 IntoIterator”。然而,这根本不可能。您不能在一个版本中存在特征实现而在另一个版本中不存在,因为版本可以混合。

相反,特征实现是在*所有*版本中添加的(从 Rust 1.53.0 开始),但做了一些小的修改以避免在 Rust 2021 之前出现破坏。在 Rust 2015 和 2018 代码中,编译器仍然会像以前一样将 array.into_iter() 解析为 (&array).into_iter(),就好像特征实现不存在一样。这*仅*适用于 .into_iter() 方法调用语法。它不会影响任何其他语法,例如 for e in [1, 2, 3]iter.zip([1, 2, 3])IntoIterator::into_iter([1, 2, 3])。这些将在*所有*版本中开始工作。

虽然为了避免破坏而需要进行一些小的修改,但这很遗憾,但此解决方案将版本之间的差异降至了绝对最低。

迁移

每当对 into_iter() 的调用在 Rust 2021 中改变了含义时,都会触发 array_into_iter lint。自 1.41 版本以来,array_into_iter lint 在所有版本中默认都是警告(在 1.55 中进行了一些增强)。如果您的代码已经没有警告,那么它应该已经为 Rust 2021 做好了准备!

您可以通过运行以下命令自动将代码迁移到与 Rust 2021 版本兼容,或确保代码已经兼容:

cargo fix --edition

由于版本之间的差异很小,因此迁移到 Rust 2021 相当简单。

对于数组上 into_iter 的方法调用,正在实现的元素将从引用更改为拥有的值。

例如

fn main() {
  let array = [1u8, 2, 3];
  for x in array.into_iter() {
    // x is a `&u8` in Rust 2015 and Rust 2018
    // x is a `u8` in Rust 2021
  }
}

在 Rust 2021 中迁移的最直接方法是通过调用 iter() 来保持与先前版本完全相同的行为,iter() 也通过引用迭代拥有的数组

fn main() {
  let array = [1u8, 2, 3];
  for x in array.iter() { // <- This line changed
    // x is a `&u8` in all editions
  }
}

可选迁移

如果您在以前的版本中使用完全限定的方法语法(即 IntoIterator::into_iter(array)),则可以将其升级为方法调用语法(即 array.into_iter())。