Filter Vec in Place

I am trying to filter a Vec in place and keep getting errors and warnings.
In this example I'm trying to filter out the odd indexed items, and only retain the even indexes.
playground example

fn main() {
    let mut list = vec!["some","thing","about","the","color","of","the","sky"];
    list.iter_mut()
         .enumerate()
         .filter(|(index, _)| index % 2 == 0)
         .map(|(_, item)| item.to_owned())
         .collect::<Vec<String>>(); 
    
// this is another version that is similar, and also not working
    // list.iter_mut()
    //     .enumerate()
    //     .filter_map(|(i, s)| if i % 2 == 0 { Some(s.to_owned()) } else { None })
    //     .collect::<Vec<String>>();

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

but i get this warning about the return from collect not being used, and the list is not filtered.

warning: unused return value of `collect` that must be used
 --> src/main.rs:3:5
  |
3 | /     list.iter_mut()
4 | |         .enumerate()
5 | |         .filter(|(index, _)| index % 2 == 0)
6 | |         .map(|(_, item)| item.to_owned())
7 | |         .collect::<Vec<String>>(); 
  | |__________________________________^
  |
  = note: `#[warn(unused_must_use)]` on by default
  = note: if you really need to exhaust the iterator, consider `.for_each(drop)` instead

any help would be appreciated!
edit: added original code

Emphasis added -- you probably want Vec::retain.

1 Like

Example using retain.

collect() gathers the items returned from an iterator into a new container. The iterator could come from many places, not just an existing container. The new container is returned, hence the warning that you must use the return value.

retain(...) modifies a Vec in place.

1 Like

Thanks for the help, I had looked at retain but could not see how to use it since I need to use indexes. @quinedot your example really helped and I think that will work for what I'm trying to do.

Here's a link to the updated playground https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a7a74943137510e4d418e6731a3323a5

Thanks again!

You're still just throwing away the results of your iteration + collect() in that Playground link -- note how the list you print is the same as the list you started with. If you want to keep the original list around and just keep the even indexed words, you can just assign the result to a new variable:

// I used .iter() not .iter_mut() as we don't need to mutate the original
let filtered = list.iter()
        .enumerate()
        .filter(|(index, _)| index % 2 == 0)
        // I added & here so that we clone the &str and not just a reference &&str
        .map(|(_, &item)| item.to_owned())
        .collect::<Vec<String>>();

But your title / original question was about filtering in place. You need retain() to filter in place, you can't filter the results of an iterator and have it directly modify the original container.

However, also note that if you want to go from &strs to Strings, you won't be able to do that in place at all -- they are different data structures.

1 Like

sorry, i think i forgot to save the changes, hopefully i did it right this time.

here is the updated link: playground

edit: added working code

fn main() {
    let mut list = vec!["some","thing","about","the","color","of","the","sky"];
    let mut index = 0;
    list.retain(|_| {
        let keeper = index % 2 == 0;
        index += 1;
        keeper
    });
    println!("{:?}", list);
}

thank you again for your help!

1 Like