Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does adding mut to passed Iterator reference solve this?

Tags:

rust

In the following Rust snippet:

fn main() {
    let list1: Vec<i32> = vec![0, 1, 2, 3, 4];

    let it1 = list1.iter();
    let tens = it1.map(|x| x * 10).collect::<Vec<i32>>();
    println!("{:?}", tens);
    
    let it2 = list1.iter();
    let doubled_from_iter = scale_it_iter(&it2);

    println!("{:?}", doubled_from_iter);
}

fn scale_it_iter(l: &dyn Iterator<Item = &i32>) -> Vec<i32> {
    l.map(|x| x * 2).collect()
}

Rust Playground Link

I get this error:

error: the `map` method cannot be invoked on a trait object
   --> src/main.rs:15:7
    |
15  |     l.map(|x| x * 2).collect()
    |       ^^^
    |
   ::: /home/xolve/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/iterator.rs:625:15
    |
625 |         Self: Sized,
    |               ----- this has a `Sized` requirement
    |
    = note: you need `&mut dyn Iterator<Item = &i32>` instead of `&dyn Iterator<Item = &i32>`

error: aborting due to previous error

Adding mut as suggested by the compiler solves this. Rust Playground link for working code.

I do not understand why it is needed. It's not needed in main when I call it1.map.

I don't understand the error messages.

  1. the `map` method cannot be invoked on a trait object is solved by adding mut to the trait reference. This seems contradictory.
  2. How is the message about the Sized trait bound related to the error?
like image 375
Xolve Avatar asked Jan 16 '21 19:01

Xolve


1 Answers

The "map method cannot be invoked on a trait object" and "this has a Sized requirement" error messages are because map() consumes the original iterator. dyn Traits cannot be consumed (they are unsized types and cannot be passed to functions by value).

It works for it1 because 1) its not a trait object, its a concrete type Iter and 2) its not a reference so it is consumed.

The reason that &mut dyn Iterator works is because &mut dyn Iterator implements Iterator. The effective difference is just the reference is consumed and the underlying iterator is mutated.


If you want to follow convention, I'd make scale_it_iter consume the iterator like so:

fn scale_it_iter<'a>(l: impl Iterator<Item = &'a i32>) -> Vec<i32> {
    l.map(|x| x * 2).collect()
}
like image 157
kmdreko Avatar answered Oct 25 '22 09:10

kmdreko