Returning mutable references

I'm having trouble finding an ergonomic way to do the following:
I have a struct that contains a vector of items. I want to return a "best candidate" item by first filtering the vector and then sorting the vector. I also want the best candidate item to be a mutable reference.
Note: The filtering and sorting in this sample is just dummy code

struct Item {
    value: u8
}

struct Items {
    items: Vec<Item>
}

impl Items {
    fn get_mut_best_item(&mut self) -> Option<&mut Item> {
        let mut subset: Vec<&mut Item> = self.items.iter_mut().filter(|i| i.value % 2 == 0).collect();
        subset.sort_by(|lhs, rhs| lhs.value.partial_cmp(&rhs.value).unwrap());
        subset.first().map(|x| *x)
    }
}

The error I get is at the last line:
68 | subset.first().map(|x| *x)
| ^^ move occurs because *x has type &mut Item, which does not implement the Copy trait

I tried chaining rather than assigning to "subset", but "sort_by" doesn't return a reference to itself, so that didn't work.

1 Like

This is because mutable references are guaranteed to be unique, and hence cannot be copied. So essentially, it fails for the same reason as why this fails:

fn foo() -> Option<String> {
    let strs = vec![
        "foo".to_string(),
        "bar".to_string(),
        "baz".to_string(),
    ];
    
    strs.first().map(|x| *x)
}
error[E0507]: cannot move out of `*x` which is behind a shared reference
 --> src/lib.rs:9:26
  |
9 |     strs.first().map(|x| *x)
  |                          ^^ move occurs because `*x` has type `String`, which does not implement the `Copy` trait

To fix this, you must take ownership of the vector's first item rather than borrowing it mutably. The easiest way to do that is to call .into_iter().next().

impl Items {
    fn get_mut_best_item(&mut self) -> Option<&mut Item> {
        let mut subset: Vec<&mut Item> = self.items.iter_mut().filter(|i| i.value % 2 == 0).collect();
        subset.sort_by(|lhs, rhs| lhs.value.partial_cmp(&rhs.value).unwrap());
        subset.into_iter().next()
    }
}
1 Like

I believe this can be better expressed with:

subset.sort_by_key(|e| e.value);
2 Likes

Thank you! I had been reading about the difference between iter() and into_iter(), but didn't make the connection.

Ah, thank you! While this was dummy code, it still applied to my real code. Much more readable!

And since looks like op just wants the minimum value it could also just do:

self.items.iter_mut().filter(|i| i.value % 2 == 0).min_by_key(|e| e.value)
2 Likes

Even better!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.