Cannot move out of *, behind a mutable reference

Hi, I tried to get an entry from HashMap, update the entry, then insert it back it HashMap. But compile failed, any suggestion? Thanks!

use std::collections::HashMap;

struct Team {}

impl Team {
    fn new() -> Self {
        Team {}
    }
    fn update(mut self) -> Self {
        self
    }
}

fn main() {
    let mut team_map: HashMap<String, Team> = HashMap::new();
    let team1 = team_map
                                .entry("Team1".to_string())
                                .or_insert(Team::new());
    let team1 = team1.update();
    team_map.insert("Team1".to_string(), team1);
}
error[E0507]: cannot move out of `*team1` which is behind a mutable reference
  --> src\main.rs:19:17
   |
19 |     let team1 = team1.update();
   |                 ^^^^^ move occurs because `*team1` has type `Team`, which does not implement the `Copy` trait


or_insert returns a mutable reference, since it is a reference you don't own the value it references and you can't move it. In this case, your update function should probably take &mut self (and not return anything) rather than moving the value in and out.

Also, you don't need to insert it back, it's already in the map, you just got a mutable reference to something in the map.

1 Like

I agree with @erelde that the best solution is probably to change update from

fn update(mut self) -> Self {

to

fn update(&mut self) {

Some explanation as to why and what the alternative would be:

The two signatures are actually very similar, the mutable reference approach has e.g. the advantage that no value has to be moved around (lots of moving can have some overhead), the main difference however is the behavior on a panic. One might expect that something like

let team1 = team_map.entry("Team1".to_string()).or_insert(Team::new());
*team1 = team1.update();

ought to compile with the (mut self) -> Self version of update, the problem however is: what happens if update panics? In this case, there would be no Team value left inside of the HashMap, which is problematic i.e. not okay. There are crates like replace_with that allow you to move out and then move back into a mutable reference by handling the panic case with a default value (or e.g. by aborting the program if you use replace_with_or_abort). With that, you can do something like this:

use std::collections::HashMap;
use replace_with::replace_with;

struct Team {}

impl Team {
    fn new() -> Self {
        Team {}
    }
    fn update(mut self) -> Self {
        self
    }
}

fn main() {
    let mut team_map: HashMap<String, Team> = HashMap::new();
    let team1 = team_map.entry("Team1".to_string()).or_insert_with(Team::new);
    replace_with(team1, Team::new, |team| team.update());
    //                  ^^^^^^^^^ only called if `update` panics
}

Anyways, all this is a lot easier if you have an fn update(&mut self) instead, as with this signature, the update function guarantees that there’s still some (potentially “garbage”-valued) Team in place in case of a panic.

use std::collections::HashMap;

struct Team {}

impl Team {
    fn new() -> Self {
        Team {}
    }
    fn update(&mut self) {
        
    }
}

fn main() {
    let mut team_map: HashMap<String, Team> = HashMap::new();
    let team1 = team_map.entry("Team1".to_string()).or_insert(Team::new());
    team1.update();
}

By the way, a useful thing to keep in mind is the function mem::replace (and its friends mem::take and mem::swap). If you’re fine with always taking the overhead of constructing and moving a dummy value into the mutable reference, you can use mem::replace as an alternative approach similar to replace_with: Rust Playground

3 Likes

@steffahn Interesting addendum re how to safely swap a value. What values are easier to update by taking ownership instead of mutating in place?

Thank you!