如何在 Rust 中正确使用 Iterator::chain

Ami*_*ahi 3 iterator rust

我尝试Chain在 Rust 中使用,遇到了一个对我来说似乎很奇怪的问题。我尝试了两个代码片段,其中一个有效,另一个无效。我试图从编译器发出的错误消息中找出问题所在,但找不到任何有用的信息。

片段1

fn main() {

    let v: Vec<_> = (1..5).collect();
    let u = v.iter().chain([6, 7, 8, 9, 10].iter().map(|i| i ));
    u.for_each(|i| println!("{i}"));
}
Run Code Online (Sandbox Code Playgroud)

片段2

fn main() {

    let v: Vec<_> = (1..5).collect();
    let u = v.iter().chain([6, 7, 8, 9, 10].iter().map(|i| i+1 ));
    u.for_each(|i| println!("{i}"));
}
Run Code Online (Sandbox Code Playgroud)

第一个片段成功运行,但第二个片段失败。错误信息是:

   Compiling playground v0.0.1 (/playground)
error[E0271]: type mismatch resolving `<[closure@src/main.rs:5:56: 5:63] as FnOnce<(&{integer},)>>::Output == &{integer}`
 --> src/main.rs:5:28
  |
5 |     let u = v.iter().chain([6, 7, 8, 9, 10].iter().map(|i| i+1 ));
  |                      ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&{integer}`, found integer
  |                      |
  |                      required by a bound introduced by this call
  |
  = note: required because of the requirements on the impl of `Iterator` for `Map<std::slice::Iter<'_, {integer}>, [closure@src/main.rs:5:56: 5:63]>`
note: required by a bound in `std::iter::Iterator::chain`

error[E0599]: the method `for_each` exists for struct `std::iter::Chain<std::slice::Iter<'_, {integer}>, Map<std::slice::Iter<'_, {integer}>, [closure@src/main.rs:5:56: 5:63]>>`, but its trait bounds were not satisfied
 --> src/main.rs:6:7
  |
6 |       u.for_each(|i| println!("{i}"));
  |         ^^^^^^^^ method cannot be called on `std::iter::Chain<std::slice::Iter<'_, {integer}>, Map<std::slice::Iter<'_, {integer}>, [closure@src/main.rs:5:56: 5:63]>>` due to unsatisfied trait bounds
  |
  = note: the following trait bounds were not satisfied:
          `<Map<std::slice::Iter<'_, {integer}>, [closure@src/main.rs:5:56: 5:63]> as Iterator>::Item = &{integer}`
          which is required by `std::iter::Chain<std::slice::Iter<'_, {integer}>, Map<std::slice::Iter<'_, {integer}>, [closure@src/main.rs:5:56: 5:63]>>: Iterator`
          `std::iter::Chain<std::slice::Iter<'_, {integer}>, Map<std::slice::Iter<'_, {integer}>, [closure@src/main.rs:5:56: 5:63]>>: Iterator`
          which is required by `&mut std::iter::Chain<std::slice::Iter<'_, {integer}>, Map<std::slice::Iter<'_, {integer}>, [closure@src/main.rs:5:56: 5:63]>>: Iterator`

Some errors have detailed explanations: E0271, E0599.
For more information about an error, try `rustc --explain E0271`.
error: could not compile `playground` due to 2 previous errors
Run Code Online (Sandbox Code Playgroud)

我是 Rust 新手,不熟悉很多细节。有人可以向我解释一下这里有什么问题吗?为什么更改itoi+1会导致编译时错误?

cdh*_*wie 8

在第一种情况下这会成功,因为两个迭代器都在生成&i32项目。然而,在第二种情况下,第一个迭代器正在生成&i32项目,而第二个迭代器正在生成i32项目——加法操作自动取消引用i并生成一个整数。要链接两个迭代器,项目类型必须完全匹配。

澄清一下,map两个片段中给出的闭包的签名不同,这可能会令人惊讶,因为它们看起来几乎相同!

  • |i| i接受&i32并返回&i32。在代码片段 1 中,这相当于|i: &i32| -> &i32 { i }.
  • |i| i + 1接受 an&i32并返回 an i32,这不是同一类型。在代码片段 2 中,这相当于|i: &i32| -> i32 { *i + 1 }.

要解决此问题,请使用该copied实用程序将第一个迭代器从&i32项目转换为i32通过复制的项目,这将与第二个迭代器的类型匹配,从而允许链接:

fn main() {
    let v: Vec<_> = (1..5).collect();
    let u = v.iter().copied().chain([6, 7, 8, 9, 10].iter().map(|i| i+1 ));
    u.for_each(|i| println!("{i}"));
}
Run Code Online (Sandbox Code Playgroud)

或者,使用 转换Vec为迭代器into_iter,它将使用Vec并直接生成其值(而不是作为引用)。

fn main() {
    let v: Vec<_> = (1..5).collect();
    let u = v.into_iter().chain([6, 7, 8, 9, 10].iter().map(|i| i+1 ));
    u.for_each(|i| println!("{i}"));
}
Run Code Online (Sandbox Code Playgroud)

类型推断可能很方便,但它也会向您隐藏信息。当您遇到这样的“类型不匹配”错误并且无法找出它们时,诊断问题的一个好方法是开始根据您认为的实际类型添加类型注释。要么添加类型注释将通过强制错误推断的类型来解决问题,要么 Rust 会抱怨类型注释与实际类型不匹配,这将暴露您的错误假设,从那里您应该能够解决问题。

在这种特殊情况下,将返回类型注释添加-> &i32到第二个闭包将导致更容易理解的错误:

error[E0308]: mismatched types
 --> src/main.rs:6:28
  |
6 |         .map(|i| -> &i32 { i+1 } )
  |                            ^^^
  |                            |
  |                            expected `&i32`, found integer
  |                            help: consider borrowing here: `&(i+1)`
Run Code Online (Sandbox Code Playgroud)

编译器的建议是不正确的,但这使情况更加清晰,并帮助您看到当i32您从第一个代码片段中认为它会返回时,您的闭包正在返回&i32