The "Hole" in the Data
You're writing a function to extract a configuration value. You have a Config struct with an api_key field. You pass a reference to the config into a helper function and try to return the key. The compiler rejects you with E0507: cannot move out of borrowed content. You're confused. You're not trying to delete the key. You just want to use it. Why does Rust think you're destroying the data?
The error message is precise, even if it feels harsh. "Move out" means transferring ownership. "Borrowed content" means you only have a temporary loan. Rust is telling you that you're trying to take ownership of something you don't own. If it let you do that, it would leave a hole in the original data. Other parts of your program might still be looking at that data, expecting it to be there. Rust refuses to let you create dangling references or double-free memory.
Borrowing vs Moving
Rust treats every value as a physical object with a single owner. The owner is responsible for cleaning up the value when it's no longer needed. A reference (&T) is a loan. It gives you permission to look at the value, but it doesn't transfer ownership. You can read the value. You can use it for calculations. You cannot take it away.
Moving a value means transferring ownership to a new location. When you move a String, you copy the pointer, length, and capacity to a new variable. The old variable is invalidated. If you move a value out of a reference, you're copying the pointer out of a loan. Now two pointers point to the same heap allocation. When both go out of scope, Rust tries to free the memory twice. That's a double free, a critical memory safety bug. E0507 exists to prevent this at compile time.
Minimal Example
Here's the pattern that triggers E0507. You have a struct with a field that owns data. You try to return that field from a function that takes a reference.
struct Book {
title: String,
}
/// Attempting to move a field out of a borrowed struct.
fn take_title(b: &Book) -> String {
// E0507: cannot move out of `b.title` which is behind a shared reference
b.title
}
The function signature says b: &Book. You have a shared reference. The return type is String. The compiler sees b.title and tries to coerce it to String. That coercion requires moving the String out of b. Since b is a reference, it doesn't own the String. The move is forbidden. The compiler emits E0507.
Why Primitives Work
You might notice that this error never appears for integers or booleans. If you change title to isbn: u64, the code compiles.
struct Book {
isbn: u64,
}
fn get_isbn(b: &Book) -> u64 {
// This works. u64 implements Copy.
b.isbn
}
The difference is the Copy trait. Types that implement Copy are duplicated bitwise when you use them. There's no ownership transfer. There's no invalidation. Copying a u64 out of a reference is just reading a number. The original value remains intact. E0507 only fires for types that do not implement Copy, like String, Vec, or custom structs with owned fields. The compiler knows that moving these types would invalidate the source, so it blocks the move through a reference.
Realistic Scenario: Config Extraction
In real code, you often need to extract owned data from a borrowed struct. The standard fix is cloning. Cloning creates a new, independent copy of the data. You get ownership of the copy, and the original struct remains untouched.
struct Config {
api_key: String,
timeout: u32,
}
/// Builds an authorization header from a borrowed config.
fn build_auth_header(config: &Config) -> String {
// Clone the key to get an owned String.
// The original config remains valid.
let key = config.api_key.clone();
format!("Bearer {}", key)
}
fn main() {
let config = Config {
api_key: String::from("secret-123"),
timeout: 30,
};
let header = build_auth_header(&config);
// config is still usable here.
println!("Timeout: {}", config.timeout);
}
Cloning is the escape hatch. It satisfies the borrow checker by creating a new owner. The cost is a heap allocation and a memory copy. For small strings or occasional calls, this is negligible. If you're cloning large data in a tight loop, the performance hit matters. In that case, redesign the function to avoid the clone.
Convention aside: When cloning a String from a struct field, write config.api_key.clone() explicitly. Avoid config.api_key.to_owned() unless you're converting from a trait object or a type where Clone isn't implemented. The explicit clone() call signals to readers that you're making a deep copy, not just changing the type.
The Mutable Reference Exception
There's a twist. You can move a value out of a mutable reference. The compiler allows partial moves through &mut T.
struct Book {
title: String,
author: String,
}
/// Extracts the title, leaving the Book in a partially moved state.
fn extract_title(b: &mut Book) -> String {
// This compiles. &mut allows moving out of fields.
b.title
}
fn main() {
let mut book = Book {
title: String::from("Rust in Action"),
author: String::from("John Doe"),
};
let title = extract_title(&mut book);
println!("Title: {}", title);
// ERROR: cannot borrow `book` as mutable because it is also borrowed as immutable
// Actually, the error is more specific: use of partially moved value.
// println!("Author: {}", book.author); // This would fail.
}
When you move b.title out of &mut Book, the compiler marks the Book as partially moved. The title field is gone. You can no longer access title, and you can't drop the Book normally because the destructor would try to drop a moved field. The compiler tracks this state and prevents you from using the struct again.
This feature exists for advanced use cases, like implementing custom collections or swapping elements. For most code, partial moves are a trap. They leave data in an invalid state and make the code harder to reason about. Stick to cloning when you have a shared reference. Use partial moves only when you're building low-level abstractions and you've proven the safety invariants.
Pitfalls and Related Errors
E0507 often appears in iterators. If you iterate over a slice or a reference to a vector, the items are borrowed. Trying to move a field out of the item triggers the error.
struct Item {
data: String,
}
fn process_items(items: &[Item]) {
for item in items {
// E0507: cannot move out of `item.data` which is behind a shared reference
let owned_data = item.data;
}
}
The fix is cloning inside the loop, or changing the loop to take ownership of the collection if the caller allows it.
fn process_items_owned(items: Vec<Item>) {
for item in items {
// item is owned here. Moving is allowed.
let owned_data = item.data;
}
}
If you try to use a struct after a failed move attempt, you might also see E0382 (use of moved value). The compiler sometimes reports E0507 first, but if you work around it incorrectly, E0382 can appear. E0507 is the gatekeeper. It stops the move before it happens. E0382 stops you from using the value after the move. They work together to enforce ownership.
Decision Matrix
Use clone() when the value is small or cheap to copy, and the caller needs an independent owned value. Use clone() when the function signature must accept a reference for API consistency, but the implementation requires ownership. Use &str or &T as the return type when you only need to read the data and don't need to transfer ownership. Change the function signature to take ownership when the caller is done with the value and you are the new owner. Use Cow<T> when you want to avoid cloning in the common case but still return an owned value when necessary. Use &mut T and partial moves only when you're implementing a low-level abstraction and you can prove that the partially moved value is never accessed again.
Don't fight the compiler here. Clone the value, or change the signature to match the ownership flow. The borrow checker is protecting you from memory bugs that would be invisible at runtime. Trust the error. It's pointing you to a design decision you need to make.