---
title: What is partial move in Rust
summary: A partial move in Rust invalidates a struct by moving a non-Copy field out of it, preventing further use of the struct.
lang: en
slug: what-is-partial-move-in-rust
published: 2026-04-17T12:05:42+00:00
updated: 2026-04-25T22:00:13+00:00
canonical: https://www.rustfaq.org/en/what-is-partial-move-in-rust/
---

# What is partial move in Rust



## The partial move trap

You are building a background worker. Your application struct holds a database connection, a cache, and a configuration string. You want to hand the database connection to the worker thread so it can query data, but you need the configuration to stay in the main loop for logging and shutdown handling. You write `let db = app.db;` to extract the connection. The compiler rejects the code. It says `app` is partially moved. You can no longer access `app.config`. The entire struct is tainted because one field left the building.

This is a partial move. Rust treats structs as atomic units for ownership. You cannot move one field out of a struct and continue using the struct. If a field moves, the struct becomes invalid. The compiler enforces this to prevent memory safety violations during cleanup.

## Ownership is atomic

Rust's ownership system relies on a simple rule: every value has exactly one owner. When the owner goes out of scope, the value is dropped. Structs bundle multiple values together. The struct itself is the owner of its fields. When you move a struct, you move all its fields at once.

Imagine a backpack containing a laptop, a notebook, and a water bottle. The backpack is the container. If you hand the backpack to someone else, they get everything inside. You can't hand over just the laptop and keep the backpack valid for the notebook. The backpack's integrity is compromised. Rust applies this logic to memory. If a field moves, the struct can no longer guarantee that all its parts are valid. The compiler marks the struct as unusable.

This rule has one exception: the `Copy` trait. If a field implements `Copy`, moving it actually copies the data. The original field remains valid. Primitives like `i32`, `bool`, and `f64` implement `Copy`. Complex types like `String`, `Vec`, and custom structs do not.

## The Copy exception

When you extract a field that implements `Copy`, the struct survives. The compiler generates a bitwise copy of the field. The original struct retains its data.

```rust
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let p1 = Point { x: 10, y: 20 };
    
    // i32 implements Copy. This copies the value 10.
    // p1.x remains 10. p1 is still fully valid.
    let x = p1.x;
    
    println!("p1 is still usable: ({}, {})", p1.x, p1.y);
    println!("x is independent: {}", x);
}
```

The `Copy` trait is automatic for primitive types. You rarely need to derive it manually for simple data. When you see code that extracts fields without moving the struct, check the field types. They are almost always `Copy`.

**Convention aside:** The community often derives `Copy` and `Clone` together for data-only structs. If a struct contains only primitives or references, adding `#[derive(Copy, Clone)]` is standard practice. It signals that the struct is cheap to duplicate and avoids partial move issues entirely.

## When the struct breaks

Fields that do not implement `Copy` move ownership. Extracting such a field moves the data out of the struct. The struct loses ownership of that field. The compiler invalidates the struct to prevent you from accessing the missing data.

```rust
struct Data {
    text: String,
    num: i32,
}

fn main() {
    let d1 = Data {
        text: String::from("hello"),
        num: 5,
    };
    
    // String does not implement Copy.
    // This moves the String out of d1.text.
    // d1 is now partially moved and invalid.
    let text = d1.text;
    
    // ERROR: E0382 (use of moved value)
    // The compiler rejects this because d1 is partially moved.
    // println!("num is {}", d1.num);
    
    println!("text is {}", text);
}
```

The error is `E0382` (use of moved value). The compiler tracks the validity of `d1`. Once `d1.text` moves, `d1` is marked as moved. Any attempt to use `d1` triggers the error. The compiler does not care that you only want to read `d1.num`. The struct is broken.

## Why Rust forbids this

The restriction exists because of drop glue. When a struct goes out of scope, Rust generates code to drop each field. This drop code assumes all fields are valid. If a field has been moved, the drop code would try to free memory that no longer belongs to the struct. This causes double-free errors or use-after-free bugs.

Rust could theoretically allow partial moves and track which fields are valid, but the complexity is high. Drop implementations might access multiple fields. Methods might assume the struct is complete. The atomic ownership model keeps the compiler simple and the guarantees strong. If a struct is valid, all its fields are valid. If a field moves, the struct is invalid.

**Convention aside:** Keep `unsafe` blocks small. The community calls this the "minimum unsafe surface" rule. Partial moves are a safe-language concern, but understanding them helps you write safe abstractions. When you build a custom collection, you need to know that moving an element invalidates the container unless you handle the state carefully.

## The Option escape hatch

You can extract a non-`Copy` field and keep the struct alive by replacing the field with a placeholder. The standard pattern uses `Option`. Wrap the field in `Option<T>`. Use `Option::take` to move the value out, leaving `None` behind. The struct remains valid because the field still exists; it just holds `None`.

```rust
struct App {
    db: Option<Database>,
    config: String,
}

fn main() {
    let mut app = App {
        db: Some(Database::new()),
        config: String::from("production"),
    };
    
    // take() moves the Database out of app.db.
    // app.db becomes None.
    // app remains valid because the field is still present.
    let db = app.db.take();
    
    // app is usable. app.db is None, app.config is intact.
    println!("Config: {}", app.config);
    
    // db owns the Database now.
    db.query("SELECT 1");
}
```

This pattern is common for resources that need to be extracted. You wrap the resource in `Option`. You initialize it with `Some`. You extract it with `take`. The struct survives.

For types that implement `Default`, you can use `std::mem::take`. It works like `Option::take` but replaces the value with `Default::default()` instead of `None`.

```rust
use std::mem;

struct Cache {
    entries: Vec<String>,
    size: usize,
}

impl Default for Cache {
    fn default() -> Self {
        Cache {
            entries: Vec::new(),
            size: 0,
        }
    }
}

struct Service {
    cache: Cache,
    name: String,
}

fn main() {
    let mut service = Service {
        cache: Cache {
            entries: vec!["key".to_string()],
            size: 1,
        },
        name: String::from("worker"),
    };
    
    // mem::take moves the Cache out.
    // service.cache is replaced with Cache::default().
    // service remains valid.
    let cache = mem::take(&mut service.cache);
    
    println!("Service name: {}", service.name);
    println!("Cache entries: {}", cache.entries.len());
}
```

`std::mem::take` is the idiomatic way to extract resources without `Option`. It requires `Default`. It keeps the struct usable.

## Methods that consume self

A common pitfall involves methods that take `self` by value. When you call a method that consumes `self`, the entire struct moves. This happens even if the method only uses one field.

```rust
struct Data {
    text: String,
    num: i32,
}

impl Data {
    // This method takes ownership of the whole struct.
    fn process(self) -> String {
        // Only uses self.text, but self.num is also moved.
        self.text.to_uppercase()
    }
}

fn main() {
    let d = Data {
        text: String::from("hello"),
        num: 5,
    };
    
    // d is moved into process().
    // d is no longer usable.
    let result = d.process();
    
    // ERROR: E0382 (use of moved value)
    // println!("num is {}", d.num);
}
```

The method signature `fn process(self)` moves the struct. If you need to keep the struct alive, change the signature to `fn process(&self)` or `fn process(&mut self)`. Borrowing avoids the move.

## Decision matrix

Use references when you need to read or modify a field but the struct must remain usable. Borrowing keeps ownership with the struct.

Use `Clone` when the data is cheap to copy and you want an independent snapshot. Cloning creates a new value without moving the original.

Move the entire struct when the destination needs full ownership and the source is done. Moving the struct transfers all fields at once.

Use `Option::take` or `std::mem::take` when you must extract a non-`Copy` field and keep the struct alive for other fields. This replaces the field with a placeholder and avoids partial move errors.

Treat the struct as a single unit. If you need a piece, borrow it. If you need the piece forever, move the whole thing or use `Option::take`. The compiler will guide you. Trust the error messages. They point to the exact field causing the issue.

## Where to go next

- [How to Use Weak<T> to Prevent Reference Cycles](/en/how-to-use-weakt-to-prevent-reference-cycles/)
- [How to Handle Resource Cleanup in Rust (RAII)](/en/how-to-handle-resource-cleanup-in-rust-raii/)
- [What Is the Difference Between Rc<T> and Arc<T>?](/en/what-is-the-difference-between-rct-and-arct/)