---
title: How to fix Rust E0382 value used after being moved
summary: The E0382 error occurs because Rust's ownership system prevents using a value after it has been moved to another variable, ensuring memory safety without garbage collection.
lang: en
slug: how-to-fix-rust-e0382-value-used-after-being-moved
published: 2026-04-17T16:32:01+00:00
updated: 2026-04-25T22:00:13+00:00
canonical: https://www.rustfaq.org/en/how-to-fix-rust-e0382-value-used-after-being-moved/
---

# How to fix Rust E0382 value used after being moved



## The variable is dead. Long live the variable.

You write a function that takes a `String`. You call it. Then you try to print the `String` again. The compiler screams E0382. You didn't copy the data; you moved it. Rust thinks the variable is dead. This happens to everyone. It's the first wall you hit when learning ownership.

In Python or JavaScript, variables are labels on boxes. You can slap multiple labels on the same box. Rust is different. Variables are the boxes themselves. If you hand the box to someone else, you no longer have it. The label doesn't stay. The box moves.

## Ownership is physical

Rust treats variables like physical objects. When you assign `let b = a`, you are handing the object from `a` to `b`. `a` no longer holds the object. It's gone. This prevents double-free bugs. If two variables owned the same memory, both would try to clean it up when they go out of scope. That crashes programs. Rust stops that at compile time.

Think of a signed deed to a house. You can't sign the deed to two people at once. If you sign it to Alice, you no longer own the house. Alice does. Rust enforces this rule for every value that owns resources.

## Minimal example

```rust
fn main() {
    // Create a String on the heap. s1 owns the pointer, length, and capacity.
    let s1 = String::from("hello");
    
    // Move ownership from s1 to s2. s1 is now invalid.
    let s2 = s1;
    
    // Compiler rejects this: s1 was moved, so it holds no data.
    // Error E0382: use of moved value: `s1`
    println!("{}", s1);
}
```

The compiler rejects this with E0382 (use of moved value). It points to the move and the use. The message is clear: `s1` was moved into `s2`. You can't use `s1` anymore.

## What happens in memory

Look at the memory. `String` stores three numbers: a pointer to the heap, the length, and the capacity. `s1` holds these three numbers. The assignment `let s2 = s1` copies those three numbers into `s2`. Now both variables point to the same heap allocation.

Rust marks `s1` as invalid. You can't read `s1` anymore. If you could, and `s1` went out of scope, it would free the heap. Then `s2` would free the same heap. That's a double-free crash. Rust kills the program at compile time to save you from that runtime crash.

When a variable goes out of scope, Rust calls `drop`. For `String`, `drop` frees the heap. If two variables owned the same `String`, `drop` would run twice. The second `drop` would access freed memory. That's undefined behavior. Rust prevents this by invalidating the first variable. The move is the mechanism. Once moved, the first variable can't call `drop`. Only the new owner calls `drop`. This guarantees single ownership at all times.

## Copy types don't move

Not all types move. Primitive types like `i32`, `bool`, and `f64` implement the `Copy` trait. When you assign `let b = a` for an `i32`, Rust copies the bits. `a` remains valid. The compiler knows these are small and cheap to copy. You don't get E0382 for `i32`.

```rust
fn main() {
    // i32 implements Copy. Assignment copies the value.
    let x = 5;
    let y = x;
    
    // x is still valid. No move happened.
    println!("x is {}", x);
}
```

`String` does not implement `Copy`. It owns heap memory. Copying the pointer would create two owners. Rust forbids that. If you define your own struct, you can derive `Copy` if all fields are `Copy`. Otherwise, it moves.

Primitives copy. Complex types move. The compiler enforces this distinction. Don't assume a type copies just because it's small.

## Realistic patterns

E0382 often appears when passing values to functions. Functions that take ownership are powerful but restrictive.

```rust
/// Takes ownership of a String to process it.
fn process_data(data: String) {
    // data owns the String. It will be dropped when this function returns.
    println!("Processing: {}", data);
}

fn main() {
    // input owns a heap-allocated String.
    let input = String::from("user_input");
    
    // Move input into process_data. Ownership transfers to the parameter.
    process_data(input);
    
    // input is invalid here. The function took it.
    // Error E0382: use of moved value: `input`
    println!("Done with {}", input);
}
```

The function `process_data` takes `String` by value. It owns the data. When `main` calls it, `input` moves into the function. `input` is dead in `main`.

You can move a value into a function and get it back. The function takes ownership, does work, and returns the value. This is efficient. No cloning needed. The ownership transfers in and out.

```rust
/// Appends text and returns the owned String.
fn append_to(mut data: String) -> String {
    data.push_str(" world");
    data
}

fn main() {
    let mut s = String::from("hello");
    
    // Move s in, get it back. Ownership chain stays clean.
    s = append_to(s);
    
    println!("{}", s);
}
```

Moving in and out is efficient. It avoids cloning. Use this pattern when a function transforms data and returns the result. The ownership chain stays clean.

E0382 also happens with struct fields. If you move a field out of a struct, the struct becomes partially moved. You can't use the struct anymore. Rust prevents this to keep structs consistent.

```rust
struct Config {
    name: String,
    value: i32,
}

fn main() {
    let config = Config {
        name: String::from("debug"),
        value: 42,
    };
    
    // Move the name field out.
    let name = config.name;
    
    // config is partially moved. Can't use it.
    // Error E0382: use of moved value: `config.name`
    println!("Config name: {}", config.name);
}
```

The struct `config` is invalid after moving `name`. You can't access `value` either. Rust treats the struct as a single unit. If you move a part, the whole is compromised.

## Pitfalls and conventions

Cloning is not free. `String::clone` allocates new memory. It copies every byte. If you clone a 1MB string, you allocate 1MB and copy 1MB. Doing this in a loop kills performance. Profile your code. If cloning is the bottleneck, switch to references. Or use `Rc` if you need shared ownership.

When you use `Rc`, the convention is `Rc::clone(&rc)`. Writing `rc.clone()` compiles, but it looks like a deep copy. The explicit form signals to readers that you are bumping a reference count, not duplicating data. Follow the convention. It makes code readable.

```rust
use std::rc::Rc;

fn main() {
    let s1 = Rc::new(String::from("hello"));
    
    // Convention: use Rc::clone to show you're cloning the reference.
    let s2 = Rc::clone(&s1);
    
    println!("s1: {}", s1);
    println!("s2: {}", s2);
}
```

`Rc` uses a simple counter. It's fast. But it's not thread-safe. If you send an `Rc` to another thread, the compiler rejects it. `Arc` uses atomic operations. Atomics are slower. They ensure the counter updates correctly across cores. Use `Arc` only when threads share the data. `Rc` is for single-threaded graphs and caches.

Sometimes the fix is to change the design. If a function takes ownership, it implies the function is responsible for the data. If the caller needs the data later, the function shouldn't take ownership. Change the signature to take a reference. Or have the function return a new value. Or use a builder pattern. E0382 often signals a mismatch between the API and the usage. Fix the API, not just the call site.

## Decision matrix

Use `clone()` when the data is small or you genuinely need an independent copy. Cloning a `String` allocates new memory and copies bytes. It costs CPU and memory. Only clone when the caller needs to keep the original and the callee needs ownership.

Use a reference (`&T`) when the callee only needs to read or modify the data temporarily. References are cheap. They are just pointers without ownership. The original variable stays alive. This is the most common fix for E0382.

Use `Rc<T>` when multiple parts of your program need to own the same data on a single thread. `Rc` tracks how many owners exist. The data stays alive until the last owner drops. It adds a small overhead for the counter.

Use `Arc<T>` when you need shared ownership across threads. `Arc` uses atomic operations for the counter, which is thread-safe. It's heavier than `Rc`. Use it only when threads are involved.

Restructure your code when you find yourself cloning large data just to satisfy the compiler. Often this means a function is taking ownership when it shouldn't. Change the function signature to take a reference. Or return the value back to the caller.

Start with references. Borrowing is the default path. Move only when you have a reason to take ownership. The compiler will guide you.

## Where to go next

- [How to Read Rust Compiler Error Messages](/en/how-to-read-rust-compiler-error-messages/)
- [How to fix Rust E0463 can't find crate](/en/how-to-fix-rust-e0463-cant-find-crate/)
- [Error: "linker cc not found" — How to Fix on Linux](/en/error-linker-cc-not-found-how-to-fix-on-linux/)