Aaron
Aaron

Reputation: 1009

Vec::iter() converts to borrow of Option

Below is an reduced example from my code that reads a directory, traverses over the files, and appends a comment to the end of each file...

This example shows a Vec<Option<bool>> that after calling iter() the compiler is interpreting the Option<_> as a borrow &Option<_> type. I'd like to understand why I'm seeing this behavior.

fn main() {
    let vec = vec![Some(true), None];

    vec.iter()
        .filter_map(|o| o)
        .count();
}

Compiler output

src/main.rs:50:25: 50:26 error: mismatched types:
 expected `core::option::Option<_>`,
    found `&core::option::Option<bool>`
(expected enum `core::option::Option`,
    found &-ptr) [E0308]
src/main.rs:50         .filter_map(|o| o)

Upvotes: 4

Views: 477

Answers (2)

Akavall
Akavall

Reputation: 86306

How about this:

fn main() {
    let my_vec = vec![Some(true), None];

    println!("{}", my_vec.iter().filter_map(|&x| x).count());
}

When apply filter_map, you can map &x to x, and your code will work.

As @Shepmaster pointed out in the comment this is only possible for types that implement Copy.

Upvotes: 1

Shepmaster
Shepmaster

Reputation: 431919

Vec::iter returns an Iter struct, which implements Iterator as:

impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T;
}

Said another way, the type of the value returned from each call to Iterator::next will be a reference to the item in the vector.

If you change to vec.into_iter(), your code works:

fn main() {
    let vec = vec![Some(true), None];

    vec.into_iter()
        .filter_map(|o| o)
        .count();
}

In this case, you are iterating on an IntoIter struct which implements Iterator differently, returning the object itself:

impl<T> Iterator for IntoIter<T> {
    type Item = T;
}

This is required because only one thing may own each vector item at a time. Generally, you want to let the vector own the item and operate on references. into_iter consumes the vector, transferring ownership of the entire vector to the IntoIter struct. That struct is then allowed to give ownership of each item to the filter_map call.

If your type implements Clone, you could also clone each item. This allows the vector to maintain ownership and creates a new item:

fn main() {
    let vec = vec![Some(true), None];

    vec.iter()
        .cloned()
        .filter_map(|o| o)
        .count();
}

Upvotes: 4

Related Questions