Error

"use of moved value" — What It Means and How to Fix It

Fix the 'use of moved value' error in Rust by cloning the data or passing a reference instead of moving ownership.

When the variable vanishes

You write a function that takes a String. You call it. You try to print the string again. The compiler yells. error[E0382]: use of moved value. You stare at the code. The variable is right there. It has a name. It has a type. Why can't you use it?

This error is the compiler enforcing Rust's ownership rule. You tried to use a variable after its ownership was transferred to another location. Rust treats heap-allocated data like a physical object. If you hand a physical object to someone else, you don't still have it. You can't hand the same object to a second person at the same time. The compiler tracks this transfer and invalidates the original variable to prevent logic errors.

Trust the error. E0382 is the compiler saving you from a dangling pointer.

The physical object rule

Rust distinguishes between data that lives on the stack and data that lives on the heap. Small values like integers fit on the stack. Large or dynamic values like String and Vec store their data on the heap. The variable on the stack holds a pointer to the heap, a length, and a capacity.

When you pass a String to a function, Rust copies the pointer, length, and capacity to the function's stack frame. Now both the caller and the function point to the same heap data. If both could use the data, both would try to free the heap memory when they go out of scope. That causes a double-free crash, which corrupts memory and crashes the program.

Rust prevents this by moving ownership. The copy of the pointer happens, but the compiler marks the original variable as invalid. The variable name still exists, but it no longer points to valid data. Any attempt to read or write the variable triggers E0382. The compiler catches the error at compile time. Your program never runs with a dangling pointer.

Double-free crashes are a thing of the past. The compiler pays the price so your runtime doesn't have to.

Minimal example

This example shows the error and the fix. The function takes ownership of the String. The caller tries to use the variable after the call.

/// Takes ownership of a String and prints it.
fn take_name(name: String) {
    // `name` now owns the String data.
    // When this function returns, `name` is dropped and the heap memory is freed.
    println!("Got: {name}");
}

fn main() {
    let user = String::from("Alice");
    
    // Move `user` into the function.
    // Ownership transfers from `main` to `take_name`.
    take_name(user);
    
    // ERROR[E0382]: use of moved value.
    // `user` was moved into `take_name`. It no longer points to valid data.
    println!("User is: {user}");
}

To fix this, pass a reference instead of the value. A reference borrows the data without taking ownership. The original variable remains valid.

/// Borrows a String reference and prints it.
fn print_name(name: &str) {
    // `name` is a reference. No ownership transfer.
    // The data stays owned by the caller.
    println!("Got: {name}");
}

fn main() {
    let user = String::from("Alice");
    
    // Borrow `user`. Ownership stays with `main`.
    print_name(&user);
    
    // `user` is still valid.
    println!("User is: {user}");
}

Read the function signature. It tells you who owns the data.

Why numbers don't move

You might notice that integers don't trigger this error. You can assign an integer to a new variable and use the original one.

fn main() {
    let x = 42;
    let y = x; // Copy happens here.
    
    // Both `x` and `y` are valid.
    println!("{x} {y}");
}

This works because i32 implements the Copy trait. Types that implement Copy are bitwise copies. They live entirely on the stack. There is no heap data to manage. There is no risk of double-free. Rust copies the bits and leaves the original variable valid.

String does not implement Copy. It owns heap data. Moving a String would leave two variables pointing to the same heap data, which breaks the safety rules.

Convention: Types that own heap data never implement Copy. String, Vec, Box, and Rc are not Copy. Types that are pure stack data often implement Copy. i32, f64, bool, char, and tuples of Copy types are Copy.

Read the type definition. If it has a String or Vec, it won't be Copy.

Realistic example: Partial moves

The error can also happen when you move a field out of a struct. This is called a partial move. You move one field, and the struct becomes partially invalid. You can't use the struct as a whole anymore.

struct Config {
    host: String,
    port: u16,
}

fn main() {
    let cfg = Config {
        host: String::from("localhost"),
        port: 8080,
    };

    // Move `host` out of `cfg`.
    // `cfg.host` is no longer valid.
    let host = cfg.host;
    
    // ERROR[E0382]: use of partially moved value.
    // `cfg` was partially moved. You can't use `cfg` as a whole.
    println!("Config: {:?}", cfg);
}

You can still use the fields that weren't moved. cfg.port is valid because u16 is Copy. But you can't use cfg itself. The struct is incomplete.

To fix this, borrow the field or clone it. Borrowing is usually the right choice.

fn main() {
    let cfg = Config {
        host: String::from("localhost"),
        port: 8080,
    };

    // Borrow `host`. `cfg` remains valid.
    let host = &cfg.host;
    
    // `cfg` is still valid.
    println!("Config: {:?}", cfg);
}

Check the signature. If it takes T, it takes the value. If it takes &T, it borrows.

Common pitfalls

E0382 shows up in loops and closures too. A for loop over a Vec takes ownership of the vector by default. The iterator consumes the vector. You can't use the vector after the loop.

fn main() {
    let items = vec![1, 2, 3];
    
    // This loop takes ownership of `items`.
    // The iterator moves `items` and yields owned values.
    for item in items {
        println!("{item}");
    }
    
    // ERROR[E0382]: use of moved value.
    // `items` was moved into the iterator.
    println!("Count: {}", items.len());
}

Fix this by iterating over a reference. The iterator yields references instead of owned values.

fn main() {
    let items = vec![1, 2, 3];
    
    // Borrow `items`. The iterator yields references.
    for item in &items {
        println!("{item}");
    }
    
    // `items` is still valid.
    println!("Count: {}", items.len());
}

Closures can also capture variables by move. If you use the move keyword, the closure takes ownership of the captured variables. You can't use those variables after creating the closure.

fn main() {
    let data = String::from("shared");
    
    // `move` forces the closure to take ownership of `data`.
    let f = move || println!("{data}");
    
    // ERROR[E0382]: use of moved value.
    // `data` was moved into the closure.
    println!("{data}");
}

Remove the move keyword if you don't need ownership. The closure captures by reference instead. Or clone the data if the closure needs to outlive the original variable.

Convention: Name your functions to signal ownership. process_string might take &str. consume_string or take_string suggests it takes ownership. This helps readers predict E0382 before it happens.

Decision matrix

Use a reference (&T) when the function only needs to read the data and the caller keeps ownership. Use a mutable reference (&mut T) when the function needs to modify the data in place and the caller keeps ownership. Use clone() when you need a second independent copy of the data and the cost of copying is acceptable. Use Rc<T> when multiple parts of your program need shared ownership of the same data and you want to avoid deep cloning. Use std::mem::take or std::mem::replace when you need to move data out of a variable but keep the variable valid for future use.

Clone when you need a copy. Borrow when you need a peek.

Where to go next