Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How properly use Iterator::chain in rust

Tags:

iterator

rust

I was trying to use Chain in Rust and encountered a problem that seems strange to me. I tried two code snippets that one of them works and the other doesn't. I tried to figure out the problem from the error message emitted by the compiler but couldn't find anything useful.

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

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

The first snippet runs successfully but the second fails. The error message is:

   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

I'm new to Rust and not familiar with many details. Can someone explain to me what's the problem here? Why changing the i to i+1 can cause a compile-time error?

like image 467
Amir reza Riahi Avatar asked Oct 18 '25 21:10

Amir reza Riahi


1 Answers

This succeeds in the first case because both iterators are producing &i32 items. However, in the second case the first iterator is producing &i32 items, while the second iterator is producing i32 items -- the addition operation auto-derefs i and produces an integer. To chain two iterators, the item types must match exactly.

To clarify, the closures given to map in both snippets differ in their signature, which may be surprising since they look nearly identical!

  • |i| i accepts an &i32 and returns that &i32. In snippet 1, this is equivalent to |i: &i32| -> &i32 { i }.
  • |i| i + 1 accepts an &i32 and returns an i32, which is not the same type. In snippet 2, this is equivalent to |i: &i32| -> i32 { *i + 1 }.

To fix this, use the copied utility to convert the first iterator from &i32 items to i32 items by copy, which will match the type of the second iterator, allowing chaining:

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

Alternatively, convert the Vec to an interator with into_iter, which will consume the Vec and produce its values directly (not as references).

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

Type inference can be handy, but it can also hide information from you. When you run into "type mismatch" errors like this and you can't figure them out, a good way to diagnose the problem is to start adding type annotations based on what you think the types actually are. Either an added type annotation will fix the problem by forcing a type that was incorrectly inferred, or Rust will complain that the type annotation doesn't match an actual type, which will expose your incorrect assumption and from there you should be able to solve the problem.

In this particular case, adding the return type annotation -> &i32 to the second closure would have led to a much more understandable error:

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)`

The compiler's suggestion is incorrect, but this makes the situation much clearer and helps you see that your closure was returning i32 when you thought from your first snippet that it would return &i32.

like image 181
cdhowie Avatar answered Oct 20 '25 16:10

cdhowie



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!