Reputation: 885
I want to iterate over a collection type in a struct and remove some values, but Rust prevents me from destroying the collection:
fn some_method(&mut self) {
self.collection = self
.collection
.into_iter()
.filter(/* ... */
.collect();
}
I could clone all of the values to build another collection, but that's not efficient. What's the idiomatic way of removing a value from the collection in place in Rust?
Upvotes: 1
Views: 788
Reputation: 161517
Since collection
is a BTreeMap
and does not have .drain()
or .retain
, and you're fine with emptying the tree during processing, the thing to do would be to move the tree out of self.collection
, manipulate it how you want, and then put it back.
As you've seen, Rust doesn't allow that with simple assignment, because if there were a panic during the time when your snippet runs, self.collection
would be left in an inconsistent state. Instead, you need to do that explicitly using std::mem::replace
, which allows you to take ownership of the content in a mutable reference by providing a replacement for it, so the reference continues to point at valid data.
fn some_method(&mut self) {
self.collection = std::mem::replace(&mut self.collection, BTreeMap::new())
.into_iter()
.filter(|_| true)
.collect();
}
Upvotes: 1
Reputation: 1575
A full example (also with into_iter
):
#[derive(Debug)]
struct Scores {
collection: Vec<i32>,
}
impl Scores {
fn new() -> Scores {
return Scores {
collection: Vec::new(),
};
}
fn filter_in_above_50(&mut self) {
self.collection = self
.collection
.drain(..)
.filter(|score| score > &50)
.collect();
}
fn filter_in_above_50_using_into_iter(&mut self) {
let coll: &mut Vec<i32> = self.collection.as_mut();
let coll: Vec<i32> = coll
.into_iter()
.filter(|score| score > &&mut 50i32)
.map(|&mut x| x)
.collect();
self.collection = coll;
}
}
And the tests:
#[test]
fn score_test() {
let mut s = Scores::new();
s.collection.push(199);
s.collection.push(11);
s.filter_in_above_50();
assert_eq!(s.collection, vec![199]);
}
#[test]
fn score_test_using_into_iter() {
let mut s = Scores::new();
s.collection.push(199);
s.collection.push(11);
s.filter_in_above_50_using_into_iter();
assert_eq!(s.collection, vec![199]);
}
Upvotes: 1