Email me

Rust errors / Ownership and borrowing

Rust compiler error · checked with rustc 1.99.0

Rust error E0382: borrow of moved value

What it meansYou used a variable after giving it away. A non-Copy value (String, Vec, a struct) has one owner; passing it by value to a function, assigning it to another variable, or looping over it with for x in v moves it, and the old name is dead.

What rustc prints

error[E0382]: assign to part of moved value: `x`
 --> main.rs:6:5
  |
4 |     let mut x = MyStruct{ s: 5u32 };
  |         ----- move occurs because `x` has type `MyStruct`, which does not implement the `Copy` trait
5 |     let y = x;
  |             - value moved here
6 |     x.s = 6;
  |     ^^^^^^^ value partially assigned here after move
  |
note: if `MyStruct` implemented `Clone`, you could clone the value
 --> main.rs:1:1
  |
1 | struct MyStruct { s: u32 }
  | ^^^^^^^^^^^^^^^ consider implementing `Clone` for this type
...
5 |     let y = x;
  |             - you could clone this value

Real output of rustc 1.99.0 compiling the official example below (2021 edition). Your names and line numbers will differ; the first line is the part to match.

Why it happens

  • Passing a String or Vec to a function that takes it by value, then using it again.
  • for item in vec (moves the Vec) followed by another use of vec.
  • Moving a value into a closure with move or into thread::spawn, then using it outside.
  • Calling a method that takes self (like into_iter() or unwrap() on an owned Option) and then using the value.

How to fix it

1. Borrow instead of moving

Take a reference in the function signature and pass &value.

fn print_len(s: &String) { println!("{}", s.len()); }

let s = String::from("hi");
print_len(&s);
println!("{s}"); // still ours

2. Iterate by reference

for x in &v borrows the Vec, so it is still usable after the loop.

let v = vec![1, 2, 3];
for x in &v { println!("{x}"); }
println!("{}", v.len());

3. Clone when you really need two copies

Costs an allocation, but is the honest fix when both owners need the data.

let a = String::from("hi");
let b = a.clone();
println!("{a} {b}");

4. Share it with Rc or Arc

For data that many parts of the program read, wrap it once and clone the pointer, not the data.

use std::sync::Arc;
let cfg = Arc::new(load_config());
let c2 = Arc::clone(&cfg);
std::thread::spawn(move || use_cfg(&c2));
use_cfg(&cfg);

Other wordings of this error

borrow of moved value, use of moved value, value moved here, value used here after move, assign to part of moved value

E0382Ownership and borrowing
Activein rustc 1.99.0
8related errors linked below

The compiler team's explanation

A variable was used after its contents have been moved elsewhere.

Erroneous code example:

struct MyStruct { s: u32 }

fn main() {
    let mut x = MyStruct{ s: 5u32 };
    let y = x;
    x.s = 6;
    println!("{}", x.s);
}

Since MyStruct is a type that is not marked Copy, the data gets moved out of x when we set y. This is fundamental to Rust's ownership system: outside of workarounds like Rc, a value cannot be owned by more than one variable.

Sometimes we don't need to move the value. Using a reference, we can let another function borrow the value without changing its ownership. In the example below, we don't actually have to move our string to calculate_length, we can give it a reference to it with & instead.

fn main() {
    let s1 = String::from("hello");

    let len = calculate_length(&s1);

    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

A mutable reference can be created with &mut.

Sometimes we don't want a reference, but a duplicate. All types marked Clone can be duplicated by calling .clone(). Subsequent changes to a clone do not affect the original variable.

Most types in the standard library are marked Clone. The example below demonstrates using clone() on a string. s1 is first set to "many", and then copied to s2. Then the first character of s1 is removed, without affecting s2. "any many" is printed to the console.

fn main() {
    let mut s1 = String::from("many");
    let s2 = s1.clone();
    s1.remove(0);
    println!("{} {}", s1, s2);
}

If we control the definition of a type, we can implement Clone on it ourselves with #[derive(Clone)].

Some types have no ownership semantics at all and are trivial to duplicate. An example is i32 and the other number types. We don't have to call .clone() to clone them, because they are marked Copy in addition to Clone. Implicit cloning is more convenient in this case. We can mark our own types Copy if all their members also are marked Copy.

In the example below, we implement a Point type. Because it only stores two integers, we opt-out of ownership semantics with Copy. Then we can let p2 = p1 without p1 being moved.

#[derive(Copy, Clone)]
struct Point { x: i32, y: i32 }

fn main() {
    let mut p1 = Point{ x: -1, y: 2 };
    let p2 = p1;
    p1.x = 1;
    println!("p1: {}, {}", p1.x, p1.y);
    println!("p2: {}, {}", p2.x, p2.y);
}

Alternatively, if we don't control the struct's definition, or mutable shared ownership is truly required, we can use Rc and RefCell:

use std::cell::RefCell;
use std::rc::Rc;

struct MyStruct { s: u32 }

fn main() {
    let mut x = Rc::new(RefCell::new(MyStruct{ s: 5u32 }));
    let y = x.clone();
    x.borrow_mut().s = 6;
    println!("{}", x.borrow().s);
}

With this approach, x and y share ownership of the data via the Rc (reference count type). RefCell essentially performs runtime borrow checking: ensuring that at most one writer or multiple readers can access the data at any one time.

If you wish to learn more about ownership in Rust, start with the Understanding Ownership chapter in the Book.

From rustc --explain E0382 (1.99.0), part of the Rust project, MIT or Apache-2.0. Official page.

Related errors

All 518 Rust error codes · Rust crate data · Glossary

Up next

Often hit nextE0499: cannot borrow as mutable more than once at a timeA related error from the same family.Read it next →

Did this answer your question?

Get the receipts by email.

What my AI agents tried this week, with the real numbers: what worked, what flopped, what made money. One email when there is something worth reading.

No spam, never sold. Every email has a one-click unsubscribe.

Popular in the shop

All 124 tools →

Browse: Bundles and kitsHome, family and schoolMoney and businessAI toolsSports and fitnessMaking and hobbiesFood and drinkIndie dev and launchTrading and quant

Free: Landing page teardown iPhone app cost calculator LLM API cost calculator Paid online: $49 Launch Check App builds Articles FAQ