Question
Understanding “Cannot Move Out of Borrowed Content” in Rust
Question
I want to understand the Rust error “cannot move out of borrowed content” (or in newer versions, “cannot move out of ... which is behind a shared reference”).
I have encountered this error many times and usually fixed it, but I never fully understood why it happens.
For example, this code:
for line in self.xslg_file.iter() {
self.buffer.clear();
for current_char in line.into_bytes().iter() {
self.buffer.push(*current_char as char);
}
println!("{}", line);
}
produces an error like:
error[E0507]: cannot move out of borrowed content
--> src/main.rs:31:33
|
31 | for current_char in line.into_bytes().iter() {
| ^^^^ cannot move out of borrowed content
In newer Rust versions, the message is:
error[E0507]: cannot move out of `*line` which is behind a shared reference
--> src/main.rs:31:33
|
31 | for current_char in line.into_bytes().iter() {
| ^^^^ move occurs because `*line` has type `std::string::String`, which does not implement the `Copy` trait
A workaround is to clone line first:
for current_char in line.clone().into_bytes().iter() {
What causes this kind of error, and what is Rust protecting me from when it reports it?
Short Answer
By the end of this page, you will understand why Rust prevents moving a value out of borrowed data, how shared references work, why methods like into_bytes() trigger a move, and which fixes are most appropriate: borrowing, cloning, or changing ownership.
Concept
Rust’s ownership system guarantees memory safety by making sure each value has a clear owner and that references do not outlive or invalidate the data they point to.
The key idea behind this error is:
- A move transfers ownership of a value.
- A borrow only gives temporary access to a value.
- You cannot move a value out of something you only borrowed.
In your example, self.xslg_file.iter() produces references to items, not owned items. If self.xslg_file is something like Vec<String>, then:
for line in self.xslg_file.iter() {
means line is a &String, not a String.
Then this line:
line.into_bytes()
tries to call a method that consumes the String. The method into_bytes(self) takes ownership of the string. But line is only a shared reference &String, so Rust refuses.
Mental Model
Think of ownership like owning a book.
- If you own the book, you can give it away, tear out pages, or keep it.
- If someone only lent you the book, you can read it, but you cannot give it to someone else.
A shared reference like &String is like borrowing a book from a library reading room. You are allowed to look at it, but not take it away.
Calling into_bytes() is like saying: “I want to take this whole book apart into pages, and I want to own those pages.” Rust says no, because you never owned the book in the first place.
Calling as_bytes() is different. It is like saying: “I just want to look at the pages.” That is allowed.
Syntax and Examples
Core idea
In Rust, methods often signal ownership through their names and signatures:
fn into_bytes(self) -> Vec<u8>
selfmeans the method takes ownership- this usually causes a move
fn as_bytes(&self) -> &[u8]
&selfmeans the method borrows- this does not move the value
Example: code that fails
let names = vec![String::from("Ada")];
for name in names.iter() {
let bytes = name.into_bytes();
println!(, bytes);
}
Step by Step Execution
Consider this example:
let lines = vec![String::from("Hi")];
for line in lines.iter() {
for b in line.bytes() {
println!("{}", b);
}
println!("{}", line);
}
Step-by-step
1. Create the vector
let lines = vec![String::from("Hi")];
linesowns aVec<String>- the vector owns one
String
2. Iterate with .iter()
for lines.() {
Real World Use Cases
This rule appears constantly in Rust programs.
Reading from collections
When iterating through a Vec<String>, HashMap<K, V>, or similar collection, you often get references:
for item in items.iter() {
// item is borrowed
}
You can inspect items, validate them, log them, or transform borrowed views of them.
Parsing and text processing
When processing strings, you often only need borrowed views:
as_str()as_bytes()chars()bytes()- slices like
&text[..]
These avoid allocations and preserve the original owner.
Web and API handlers
Server code often borrows request data for validation instead of consuming it immediately.
Example uses:
- checking headers
- inspecting JSON fields
- validating input
- logging request data
Configuration and settings
Applications commonly borrow configuration values from a loaded config struct instead of moving them out one by one.
Real Codebase Usage
In real Rust projects, developers usually handle this situation with one of a few common patterns.
1. Borrow instead of move
Most common when you only need to read data.
for line in self.xslg_file.iter() {
for b in line.as_bytes() {
// use b
}
}
This is efficient and avoids cloning.
2. Use iterator methods that match ownership
Choose the right iterator:
.iter()for&T.iter_mut()for&mut T.into_iter()forT
Example:
let lines = vec![String::from("a"), String::from()];
lines.() {
= line.();
(, bytes);
}
Common Mistakes
1. Calling consuming methods on borrowed values
Broken code:
let v = vec![String::from("abc")];
for s in v.iter() {
let bytes = s.into_bytes();
println!("{:?}", bytes);
}
Why it fails:
sis&Stringinto_bytes()consumes aString
Fix:
for s in v.iter() {
let bytes = s.as_bytes();
println!("{:?}", bytes);
}
2. Using .iter() when .into_iter() is needed
Comparisons
| Situation | What you have | What you want | Works? | Typical fix |
|---|---|---|---|---|
| Read from an item in a collection | &String | inspect text | Yes | as_str(), as_bytes(), chars(), bytes() |
| Convert borrowed string into owned bytes | &String | Vec<u8> | No, not directly | clone, or change iteration to own the String |
| Consume items from a collection | String | move into function/method |
Cheat Sheet
Quick rules
- You can move only from something you own.
- You cannot move out of
&T. - Shared references (
&T) allow reading, not taking ownership. - Mutable references (
&mut T) allow mutation, but still do not automatically allow moving fields out. - If a method takes
self, it usually consumes the value. - If a method takes
&self, it borrows.
Common fixes
If you only need to read
s.as_bytes()
s.as_str()
s.bytes()
s.chars()
If you need ownership
- iterate with
into_iter() - restructure code so you own the value
- clone as a last resort or deliberate tradeoff
Recognize consuming methods
fn method(self) -> ...
This usually means move/consume.
Recognize borrowing methods
FAQ
Why does into_bytes() fail but as_bytes() work?
into_bytes() consumes the String and returns an owned Vec<u8>. as_bytes() only borrows the string’s bytes as &[u8].
Why is line borrowed in a for loop?
Because self.xslg_file.iter() returns references to each item. If the collection stores String, then each line is &String.
Why does cloning fix the error?
Cloning creates a new owned String. You are allowed to consume that clone because you own it.
Is cloning the best solution?
Usually not. If you only need to read data, borrowing is cheaper and more idiomatic.
How can I tell whether a method consumes a value?
Look at its signature. If it takes self, it consumes ownership. If it takes &self, it borrows.
Mini Project
Description
Build a small Rust program that processes a list of strings and prints both the original text and its byte values. The project demonstrates the difference between borrowing data for inspection and consuming data for ownership-based conversion.
Goal
Practice choosing between borrowing and moving by processing strings without triggering “cannot move out of borrowed content”.
Requirements
- Create a vector of at least three
Stringvalues. - Iterate over the strings by reference and print each string.
- For each string, print its byte values without cloning it.
- Add a second example that consumes a separate vector and converts each string into owned bytes.
- Keep both examples valid and compileable.
Keep learning
Related questions
Accessing Cargo Package Metadata in Rust
Learn how to read Cargo package metadata like version, name, and authors in Rust using compile-time environment macros.
Associated Types vs Generic Type Parameters in Rust: When to Use Each
Learn when to use associated types vs generic parameters in Rust traits, with clear rules, examples, and practical API design advice.
Convert an Integer to a String in Rust
Learn the current Rust way to convert integers to strings, why `to_str()` no longer works, and when to use `to_string()` or `format!`.