"Use of moved value" in Rust: How to Fix It and Keep Your Sanity

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

"Use of moved value" in Rust: Why It Happens and How to Live with It

\n\n

If you've just started learning Rust, you've probably already encountered the mysterious message: error[E0382]: use of moved value. The compiler scolds you, you scratch your head, and code that is logically perfectly correct refuses to compile. Sound familiar? Welcome to the world of ownership and borrowing — key concepts of Rust that make it safe but sometimes baffle even experienced developers.

\n\n

This article is not dry documentation, but a lively breakdown of the problem. We'll look at why the use of moved value error occurs, using real code examples, and, most importantly, what to do about it. You'll learn how to think like the borrow checker and stop fighting it. Let's go!

\n\n

What is a "move" in Rust and why is the compiler so strict?

\n\n

In Rust, every value has exactly one owner. When you assign a value to another variable or pass it to a function, ownership is moved. After the move, the original variable becomes invalid — the compiler forbids accessing it. This is the root of the use of moved value error.

\n\n

Look at this simplest example:

\n\n
let s1 = String::from("hello");\nlet s2 = s1; // Ownership moves from s1 to s2\nprintln!("{}", s1); // ERROR: use of moved value: `s1`
\n\n

Why is that? String is not a simple stack copy. It stores data on the heap. If Rust simply copied the pointer, when freeing memory both variables would try to delete it — a double free. Instead, Rust "moves" ownership: now s2 is responsible for the memory, and s1 no longer exists from the compiler's perspective. This is the core idea of move semantics in Rust.

\n\n

Typical scenarios leading to the error

\n\n

The use of moved value error most often occurs in three situations:

\n\n
    \n
  • Passing a value to a function
  • \n
  • Assigning a value to another variable
  • \n
  • Using a value in a loop or iterator
  • \n
\n\n

Let's examine each case in detail.

\n\n

1. Passing to a function without returning

\n\n
fn print_string(s: String) {\n    println!("{}", s);\n}\n\nfn main() {\n    let my_string = String::from("Rust");\n    print_string(my_string); // ownership moved\n    println!("{}", my_string); // error! use of moved value\n}
\n\n

After calling print_string, ownership of my_string goes into the function. When the function ends, s is dropped, and my_string is no longer valid. The compiler protects you from using a dead object.

\n\n

2. Assignment without copying

\n\n

We've already seen this example with String. But importantly: not all types behave this way. Simple types like i32, bool, f64 implement the Copy trait. They are not moved, but automatically copied.

\n\n
let x = 5;\nlet y = x; // copy, not move\nprintln!("{}", x); // works!
\n\n

Types that store data on the heap (String, Vec, Box) do not implement Copy — only Clone. Therefore, they are moved.

\n\n

3. Iterating over a collection

\n\n

Another common source of errors is for loops:

\n\n
let v = vec![1, 2, 3];\nfor x in v { // v is moved into the iterator\n    println!("{}", x);\n}\nprintln!("{:?}", v); // error! use of moved value
\n\n

When you write for x in v, you are effectively calling into_iter(), which consumes the vector. After the loop, v no longer exists.

\n\n

How to fix it: 4 proven methods

\n\n

Now

Blogs

Book Recommendations