How to fix Rust E0382 use of moved value

Fix Rust E0382 by cloning the value or passing a reference to avoid moving ownership.

The variable didn't vanish. You gave it away.

You write a function to process a user's input. You pass the input string to the function. The function does its work. You try to print the string again to confirm it worked. The compiler screams E0382: use of moved value. Your variable didn't just go out of scope. It vanished. The compiler thinks you handed the data to the function and then tried to use it again.

This error stops you from writing code that would crash at runtime. Rust tracks ownership of every value. When you pass a value to a function, you often transfer ownership. The function becomes the new owner. The original variable is no longer valid. If you try to use it, the compiler rejects the code with E0382. This rule prevents two parts of your program from trying to free the same memory, which causes double-free bugs and security vulnerabilities.

Ownership and the move semantics

Rust enforces a single-owner rule. Every value has exactly one owner at any time. When the owner goes out of scope, the value is dropped and its memory is reclaimed. This keeps memory safe without a garbage collector.

The move semantics are how ownership transfers. When you assign a value to a new variable or pass it to a function, the ownership moves. The old variable becomes invalid. This happens automatically for types that manage heap memory, like String, Vec, and Box. These types allocate memory on the heap. The variable on the stack holds a pointer, a length, and a capacity. Moving the variable copies the pointer, length, and capacity, but not the heap data. If Rust copied the heap data automatically, every assignment would be expensive. Moving is cheap. It just copies the stack metadata.

Think of a String like a deed to a house. The deed is a small piece of paper with the address. The house is the data on the heap. When you hand the deed to someone else, they own the house. You no longer have the right to sell it or demolish it. If you tried to hand the deed to a second person, you'd be selling the same house twice. Rust prevents this by invalidating your copy of the deed after the transfer.

Stack-only types like i32, bool, and f64 work differently. These values fit entirely on the stack. There is no heap data to manage. Rust copies these values automatically. This is called the Copy trait. You don't get E0382 for Copy types because the compiler duplicates the data instead of moving it.

Minimal example

This example shows the classic E0382 trigger. A String is moved into a function. The code tries to use the string after the move.

fn process_input(input: String) {
    // Function takes ownership of `input`.
    // `input` is now owned by this function.
    println!("Processing: {}", input);
}

fn main() {
    let data = String::from("user_input");
    
    // Move `data` into `process_input`.
    // `data` is no longer valid here.
    process_input(data);
    
    // E0382: use of moved value `data`.
    // The compiler rejects this because `data` was moved.
    println!("Data is: {}", data);
}

The compiler output points to the second println!. It tells you that data was moved. The error message includes the location of the move. This helps you find where the ownership transferred.

Trust the compiler. If it says the value moved, it moved. The variable is invalid.

What the compiler sees

The borrow checker tracks the state of every variable. When you declare let data = String::from("user_input");, the compiler marks data as initialized and owned. When you call process_input(data), the compiler sees that process_input expects a String by value. It records that data has been moved into the function. The variable data in main is now in a moved state.

Any subsequent use of data checks this state. The compiler sees the moved state and emits E0382. The check happens at compile time. No runtime overhead is involved. The compiler guarantees that you never use a value after it has been moved.

This guarantee extends to the Drop trait. When a value goes out of scope, Rust calls its Drop implementation to clean up resources. If data were still valid after the move, both main and process_input would try to drop the same String. The heap memory would be freed twice. The second free would corrupt memory. Rust eliminates this class of bugs entirely.

Realistic scenario: processing a user

In real code, E0382 often appears when you want to reuse a struct or a large data structure. You might want to log a user, save the user, and then send a welcome email. If your functions take ownership, you can't chain them easily.

struct User {
    name: String,
    email: String,
}

fn log_user(user: User) {
    // Function takes ownership of `user`.
    println!("Logged user: {}", user.name);
}

fn save_user(user: User) {
    // Function takes ownership of `user`.
    println!("Saved user: {}", user.email);
}

fn main() {
    let user = User {
        name: String::from("Alice"),
        email: String::from("alice@example.com"),
    };

    // Move `user` into `log_user`.
    log_user(user);

    // E0382: use of moved value `user`.
    // `user` was moved to `log_user`.
    save_user(user);
}

The error occurs at save_user(user). The user variable was moved into log_user. It is no longer available. You have two main ways to fix this. You can pass a reference instead of the value. Or you can clone the value if you need independent copies.

Passing a reference is usually the right choice. References let functions read the data without taking ownership. The original owner keeps the data alive.

fn log_user(user: &User) {
    // Function borrows `user`.
    // Ownership stays with the caller.
    println!("Logged user: {}", user.name);
}

fn save_user(user: &User) {
    // Function borrows `user`.
    println!("Saved user: {}", user.email);
}

fn main() {
    let user = User {
        name: String::from("Alice"),
        email: String::from("alice@example.com"),
    };

    // Pass references. `user` is not moved.
    log_user(&user);
    save_user(&user);

    // `user` is still valid.
    println!("User name: {}", user.name);
}

The code compiles. The references &user borrow the data. The borrow checker ensures the borrows don't conflict. Since both functions only read the data, multiple immutable borrows are allowed.

Clone the value only when you need a second independent copy. Cloning allocates new memory and copies the data. It is more expensive than borrowing.

fn main() {
    let user = User {
        name: String::from("Alice"),
        email: String::from("alice@example.com"),
    };

    // Clone `user` to create an independent copy.
    let user_copy = user.clone();
    
    // Move the copy into `log_user`.
    log_user(user_copy);
    
    // `user` is still valid.
    save_user(user);
}

Cloning works, but it costs memory and CPU time. Use references when possible. Clone only when the function needs to modify the data or store it beyond the current scope.

Partial moves and struct fields

Rust allows you to move individual fields of a struct. This is called a partial move. When you move a field, the struct becomes partially moved. You can no longer use the struct as a whole. You can still access fields that were not moved, provided they implement Copy or you haven't moved them.

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

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

    // Move `host` out of `config`.
    let host = config.host;
    
    // E0382: use of partially moved value `config`.
    // `config` is invalid because `host` was moved.
    println!("Config host: {}", config.host);
    
    // This works because `port` is `Copy` and wasn't moved.
    println!("Config port: {}", config.port);
}

The error occurs at config.host. The field host was moved. You can't move it again. The struct config is partially moved. You can't pass config to a function that expects a Config. You can access port because u16 implements Copy. The compiler copies port automatically.

Partial moves are useful for extracting data from a struct without dropping the whole struct. Be careful. Once a field is moved, the struct is broken. You can't reconstruct it easily. Use std::mem::take to reset a field to a default value while extracting the old value. This keeps the struct valid.

Partial moves are real. Watch your struct fields. Moving one field can invalidate the whole struct.

The Copy trait: when moves don't happen

Types that implement the Copy trait don't move. They duplicate automatically. The compiler inserts a copy operation whenever you assign or pass a Copy value. This includes primitive types like i32, u8, f64, bool, and char. It also includes tuples and arrays if all their elements are Copy.

You can derive Copy for your own types. A type can be Copy only if all its fields are Copy. You cannot derive Copy for a type that contains a String or Vec. These types manage heap memory and must be moved or cloned.

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

fn main() {
    let p1 = Point { x: 1.0, y: 2.0 };
    
    // `p1` is copied. No move happens.
    let p2 = p1;
    
    // Both `p1` and `p2` are valid.
    println!("p1: ({}, {})", p1.x, p1.y);
    println!("p2: ({}, {})", p2.x, p2.y);
}

The Copy trait requires the Clone trait. Clone provides the clone() method. Copy tells the compiler to call clone() automatically during assignments. You rarely call clone() on Copy types. The compiler does it for you.

If you see E0382 on a small struct, check if it should be Copy. If the struct contains only primitives and other Copy types, derive Copy and Clone. This removes the move overhead and makes the API more ergonomic.

Pitfalls and performance

Cloning is a common fix for E0382, but it has costs. Cloning a String allocates new heap memory and copies the bytes. Cloning a Vec allocates a new buffer and copies the elements. If you clone large data structures in a hot loop, your program will slow down and consume more memory.

Profile your code before cloning. If you clone just to silence the compiler, you might be hiding a design issue. Ask yourself if the function really needs ownership. Most functions only need to read the data. They can take a reference. References are zero-cost. They don't allocate or copy.

Another pitfall is ignoring the Copy trait. If you have a struct of numbers, you might be moving it unnecessarily. Derive Copy to let the compiler duplicate the data efficiently.

Community convention prefers &str over &String for function parameters. &str is a string slice that can point to a String or a string literal. It's more flexible and avoids unnecessary allocations. If you see fn foo(s: &String), refactor it to fn foo(s: &str). This convention applies to slices too. Prefer &[T] over &Vec<T>.

Clone only when you need a copy. Otherwise, reach for a reference.

Decision matrix

Use a reference (&T) when the function only needs to read the data and doesn't need to own it. References are cheap and keep the original value alive.

Use a mutable reference (&mut T) when the function needs to modify the data in place. Mutable references enforce exclusive access. Only one mutable reference can exist at a time.

Use .clone() when you need a second independent copy of the data, and the cost of copying is acceptable. Cloning is safe but allocates memory for heap types like String or Vec.

Use Copy types when the data is small and fits on the stack, like i32 or bool. These types duplicate automatically without moving ownership. Derive Copy for structs that contain only Copy fields.

Use std::mem::take or std::mem::replace when you need to extract data from a struct or container without dropping the container itself. This is useful for resetting a buffer while keeping the allocation.

Use Rc<T> or Arc<T> when multiple parts of your program need shared ownership of the same data. Reference counting allows multiple owners, but adds overhead and complexity.

Use RefCell<T> or Mutex<T> when you need interior mutability behind a shared reference. This lets you mutate data through a reference, but requires runtime checks or locking.

Pick the tool that matches your intent. Don't clone if a reference works. Don't use Rc if a single owner suffices. The borrow checker guides you toward the right choice.

Where to go next