Rust Error E0507: Cannot Move Out of a Value Behind a Shared Reference
E0507 means you tried to take a value (move it) out of something you only borrowed. You can look at a borrowed value, but you can’t carry it away, because then the real owner would be left with a hole. It’s a very common error, especially with Vec<String>, struct fields, and Option. The fix is usually to borrow instead, or to .clone(). This page shows the fixes first, then explains them. For the other errors in this series, see Rust compiler errors.
The error message
Section titled “The error message”error[E0507]: cannot move out of `self.name` which is behind a shared reference --> src/main.rs:7:9 |7 | self.name // ❌ tries to give away a String that `self` only borrows | ^^^^^^^^^ move occurs because `self.name` has type `String`, which does not implement the `Copy` trait |help: consider cloning the value if the performance cost is acceptable |7 | self.name.clone() // ❌ tries to give away a String that `self` only borrows | ++++++++This is the program that produces it:
struct User { name: String,}
impl User { fn name(&self) -> String { self.name // ❌ tries to give away a String that `self` only borrows }}
fn main() { let u = User { name: String::from("Ferris") }; println!("{}", u.name());}The same error code comes with several wordings. They all mean “you only borrowed this, so you can’t move it”:
cannot move out of index of `Vec<String>`forlet first = names[0];cannot move out of `*r` which is behind a shared referenceforlet owned = *r;wherer: &Stringcannot move out of `nick` as enum variant `Some` which is behind a shared referenceformatch *nick { Some(n) => n, ... }cannot move out of `report`, a captured variable in an `Fn` closurecannot move out of `self.log` which is behind a mutable reference(yes, even&mutdoesn’t let you just take it)cannot move out of an `Rc`forlet s: String = *shared;
What it means
Section titled “What it means”Look at line 7. The method returns String, an owned value. So self.name would move the string out of the struct and hand it to the caller. But the method only has &self, a shared (read-only) borrow of the user. It doesn’t own the user, so it can’t give the user’s name away.
The move occurs because ... does not implement the Copy trait part matters too. If the field were a u32, Rust would quietly copy it and there would be no error. A String owns heap memory, so it can only be moved, not copied.
Think of borrowing a friend’s bookshelf for the afternoon. You can read any book on it. But you can’t take a book home: the shelf isn’t yours, and your friend would come back to an empty slot.
Why Rust does this
Section titled “Why Rust does this”Every value has exactly one owner, and the owner cleans it up when it goes away (see Ownership). If you could move self.name out of a borrowed User, the user would still think it owns that string. Later the user is dropped, and it would free the string a second time, or someone would read a field that is no longer there. That’s a “use after free” or “double free”, two of the worst bugs in C and C++.
A Vec is the same. names[0] is still owned by the vector. If let first = names[0]; moved it out, the vector would have a hole at index 0, and it would try to free that slot again when it is dropped. So indexing only gives you access, never ownership.
The rule in one line: through a reference, you may read and (with &mut) change, but you may not take. If you want ownership, you have to own the container, or leave something in the value’s place.
How to fix it
Section titled “How to fix it”Fix 1: Borrow instead of moving
Section titled “Fix 1: Borrow instead of moving”Choose this when you only need to read the value. This is the right fix most of the time. Return &str instead of String, write &v[0] instead of v[0], and borrow the contents of an Option with as_deref() or as_ref():
struct User { name: String, nickname: Option<String>,}
impl User { fn name(&self) -> &str { &self.name }
fn display_name(&self) -> &str { match self.nickname.as_deref() { Some(nick) => nick, None => &self.name, } }}
fn describe(nick: &Option<String>) -> &str { match nick { Some(n) => n, None => "anonymous", }}
fn main() { let users = vec![ User { name: String::from("Ferris"), nickname: Some(String::from("crab")) }, User { name: String::from("Corro"), nickname: None }, ]; let first = &users[0]; println!("First user: {}", first.name()); for user in &users { println!("Shown as: {}", user.display_name()); } println!("Nickname: {}", describe(&users[1].nickname));}Output:
First user: FerrisShown as: crabShown as: CorroNickname: anonymousLook at describe: it matches on nick (the reference), not *nick. Then n is a &String, a borrow, and nothing moves. Writing match *nick { Some(n) => ... } asks for an owned String in n, which is E0507. The compiler’s hint says this too: consider removing the dereference here.
as_deref() turns an &Option<String> into an Option<&str>; as_ref() gives Option<&String>. Use them before .unwrap(), .map() or match, so you work with a borrow instead of the owned value. The same goes for *r where r: &String: you usually don’t need the * at all, just use r. See Option and Borrowing.
Fix 2: Clone it
Section titled “Fix 2: Clone it”Choose this when you really need your own copy, for example to store it somewhere else while the original stays where it is. The compiler suggests it in almost every E0507 message:
struct User { name: String,}
impl User { fn name(&self) -> String { self.name.clone() }}
fn main() { let names = vec![String::from("Ferris"), String::from("Corro")]; let first = names[0].clone(); println!("{first} is first, the list still has {} names", names.len());
let u = User { name: String::from("Ferris") }; let mut badge = u.name(); badge.push_str(" (admin)"); println!("{badge} / {}", u.name);}Output:
Ferris is first, the list still has 2 namesFerris (admin) / FerrisCloning a name or a short list is cheap, and it’s completely fine, especially while you’re learning. It only becomes a problem for big data in a hot loop. If a borrow (Fix 1) is enough, prefer that.
Fix 3: Take it out and leave something behind
Section titled “Fix 3: Take it out and leave something behind”Choose this when you have &mut access and want to move the value out for real, for example to hand off a finished log or the next task. You can’t leave a hole, but you can swap in a replacement:
std::mem::take(&mut x)takes the value and leaves its default (""for aString, empty for aVec).option.take()takes what’s inside anOptionand leavesNone.std::mem::replace(&mut x, new)putsnewin and gives you the old value back.
use std::mem;
#[derive(Debug)]enum Status { Queued, Running,}
struct Job { log: String, next_step: Option<String>, status: Status,}
impl Job { fn take_log(&mut self) -> String { mem::take(&mut self.log) }
fn start_next(&mut self) -> Option<String> { self.next_step.take() }
fn set_status(&mut self, new: Status) -> Status { mem::replace(&mut self.status, new) }}
fn main() { let mut job = Job { log: String::from("build ok"), next_step: Some(String::from("deploy")), status: Status::Queued, }; let old = job.set_status(Status::Running); println!("Status: {old:?} -> {:?}", job.status); println!("Next step: {:?}", job.start_next()); println!("Next step again: {:?}", job.start_next()); println!("Log: {:?}", job.take_log()); println!("Log now: {:?}", job.log);}Output:
Status: Queued -> RunningNext step: Some("deploy")Next step again: NoneLog: "build ok"Log now: ""The struct is never left with a hole: after take_log the log is an empty string, after start_next the step is None. This is the standard fix for “cannot move out of self.x which is behind a mutable reference”. mem::take needs the type to implement Default; for other types, use mem::replace.
Fix 4: Own the collection, then move out of it
Section titled “Fix 4: Own the collection, then move out of it”Choose this when you’re done with the collection or the struct and want its contents. If you own the Vec, it can hand items over: remove(i), swap_remove(i), pop(), or into_iter() for all of them. A method that takes self (not &self) owns the struct and may move its fields out:
struct User { name: String,}
impl User { fn into_name(self) -> String { self.name }}
fn main() { let mut queue = vec![String::from("email"), String::from("backup"), String::from("report")]; let first = queue.remove(0); println!("Doing {first}, left: {queue:?}");
let users = vec![User { name: String::from("Ferris") }, User { name: String::from("Corro") }]; let names: Vec<String> = users.into_iter().map(|u| u.into_name()).collect(); println!("{names:?}");}Output:
Doing email, left: ["backup", "report"]["Ferris", "Corro"]into_iter() consumes the vector, so you can’t use users afterwards (that would be E0382). The into_ prefix on into_name is a Rust naming habit: it tells the reader “this uses up the value”. remove(0) shifts every other item down; if order doesn’t matter, swap_remove is faster. See Removing elements.
Where you’ll usually see it
Section titled “Where you’ll usually see it”Collecting fields in a for loop over &users. for u in &users { names.push(u.name); } fails with cannot move out of `u.name` which is behind a shared reference. Fix: names.push(u.name.clone()), or loop with for u in users if you don’t need users afterwards, or collect borrows: users.iter().map(|u| u.name.as_str()).collect::<Vec<&str>>().
Calling .unwrap() on an Option field through a reference. config.title.unwrap() where config: &Config fails, because unwrap takes the Option by value. The compiler suggests the fix: config.title.as_ref().unwrap() (gives &String), or config.title.as_deref() for an Option<&str>.
Moving a captured variable inside an Fn closure. A closure that may be called many times can’t give away what it captured: cannot move out of `report`, a captured variable in an `Fn` closure. Fix: borrow inside the closure (&report), clone inside it, or, if it only runs once, change the bound to FnOnce. See Fn, FnMut and FnOnce.
Dereferencing an Rc. let s: String = *shared; gives cannot move out of an `Rc`, since other owners may still use the value. Fix: borrow it (&*shared or just &shared), clone it, or use Rc::unwrap_or_clone(shared). A Box is different: you own it, so let s = *boxed; works. See Smart pointers.
Related errors
Section titled “Related errors”- E0382: use of a moved value. You were allowed to move, but used the value again afterwards.
- E0594: cannot assign through a
&reference. The same “you only borrowed this” rule, but for writing instead of moving. - E0596: cannot borrow as mutable, for example when
mem::take(&mut self.x)is used in a method that only has&self. - E0308: mismatched types,
expected String, found &String, which often appears right after switching to a borrow. Return&str, or call.clone(). - E0515: cannot return a reference to a local variable, when a borrow-based fix points at data that is about to be dropped.
- All compiler errors: the full list of error pages.
Quick check
Section titled “Quick check”Quick check
1. let first = names[0]; with names: Vec<String> gives E0507. What is usually the best fix if you only print first?
2. A method fn next(&mut self) -> Option<Task> should hand over self.pending (an Option<Task>). What compiles?
3. Why is let n = nums[0]; fine for nums: Vec<i32>, but not for a Vec<String>?
Score: 0 / 3
Read more
Section titled “Read more”- E0507 — Rust error codes index: the official explanation, including the
mem::replacefix. - What Is Ownership? — The Rust Book: moves,
CloneandCopy, the ideas behind this error. - References and Borrowing — The Rust Book: what you can and can’t do through
&and&mut. std::mem::take— standard library docs: take a value out of a&mutand leave its default behind; its siblingmem::replacelets you choose the replacement.Option— standard library docs:as_ref,as_derefandtake, with examples.