RustArena

Rust error E0382: borrow of moved value (use of moved value)

Fix Rust's E0382 borrow of moved value and use of moved value errors: why values move, the common causes, and the right fix for each.

E0382 means you used a variable after its value was moved somewhere else. A move transfers ownership, and the old variable can't be used afterwards. The two wordings are the same error:

Cause 1: assignment moves

fn main() {
    let name = String::from("Ferris");
    let player = name;
    println!("{name} joined as {player}");
}
error[E0382]: borrow of moved value: `name`
 --> src/main.rs:4:16
  |
2 |     let name = String::from("Ferris");
  |         ---- move occurs because `name` has type `String`, which does not implement the `Copy` trait
3 |     let player = name;
  |                  ---- value moved here
4 |     println!("{name} joined as {player}");
  |                ^^^^ value borrowed here after move

The message tells the whole story: the type isn't Copy, where it moved, and where you used it afterwards. Fix: borrow (let player = &name;) or clone (let player = name.clone();) if you truly need two owned copies.

Cause 2: a for loop over a collection

fn main() {
    let tags = vec![String::from("rust"), String::from("arena")];
    for t in tags {
        println!("{t}");
    }
    println!("{} tags", tags.len());
}
error[E0382]: borrow of moved value: `tags`

for t in tags calls into_iter(), which consumes the vector. Loop over a reference instead: for t in &tags (each t is a &String).

Cause 3: moving inside a loop

fn archive(log: String) {
    println!("archived {} bytes", log.len());
}

fn main() {
    let log = String::from("boot ok");
    for _ in 0..2 {
        archive(log);
    }
}
error[E0382]: use of moved value: `log`

rustc adds "value moved here, in previous iteration of loop". The first call took ownership, so there's nothing left to give the second time. Change the parameter to log: &str and call archive(&log). rustc even points at the parameter: "consider changing this parameter type in function archive to borrow instead if owning the value isn't necessary".

Cause 4: methods that take self

Some methods consume their receiver: into_iter(), into_bytes(), unwrap() on an Option<String>, a builder's build(self). After calling one, the variable is gone. Look for an as_ref(), iter() or &-taking alternative, or call it last.

Cause 5: closures with move

A move closure (common with thread::spawn) takes ownership of every variable it uses. Clone the value before the closure if you still need it afterwards: let name2 = name.clone(); then use name2 inside.

Choosing the fix

  1. Does the receiver only read the value? Borrow: &value, and change the parameter to &str / &[T] / &T. Best in most cases.
  2. Does it need to change it? Pass &mut value.
  3. Does it need to keep it (store in a struct, send to a thread)? Then it must own it. Clone if you also need it, or restructure so the move is the last use.
  4. Is the type small and plain? Derive Clone, Copy for it, and it will be copied instead of moved.

Example

fn archive(log: &str) {
    println!("archived {} bytes", log.len());
}

fn main() {
    let name = String::from("Ferris");
    let player = &name; // borrow instead of move
    println!("{name} joined as {player}");

    let tags = vec![String::from("rust"), String::from("arena")];
    for t in &tags {
        println!("tag {t}");
    }
    println!("{} tags", tags.len());

    let log = String::from("boot ok");
    for _ in 0..2 {
        archive(&log);
    }

    let backup = log.clone(); // two owners need two copies
    let moved_log = log; // `log` is moved here, and not used afterwards
    println!("{moved_log} / {backup}");
}

Output:

Ferris joined as Ferris
tag rust
tag arena
2 tags
archived 7 bytes
archived 7 bytes
boot ok / boot ok

Practice it