How to understand ownership if you come from Python or JavaScript
Short answer: in Python and JavaScript, a variable is a name for an object, and many names can
point at the same one. In Rust, a binding is the owner of its value, and let b = a; hands that
ownership over instead of binding a second name. Most early ownership errors come from using that
second name.
The habit you’re bringing
Section titled “The habit you’re bringing”In Python, b = a doesn’t copy the list. Both names reach the same object, a change through either
is visible through the other, and it lives until a garbage collector finds nothing reaching it.
JavaScript is the same for objects and arrays. Integers and strings never reveal the sharing, but
the model underneath is identical, and it has no place for a name retired by an assignment.
The same line in Rust
Section titled “The same line in Rust”let b = a; moves. For a String, Vec, or Box, ownership transfers to b, and a isn’t
emptied or freed — it’s retired, and the next line that mentions it is a compile error.
What happens to the original variable after a move?
covers that state. The reason is the double free: two names owning one heap buffer would each free
it, so the compiler won’t leave two owners in play
(Why does Rust move values?).
The types that behave like Python’s integers are the Copy ones — i32, f64, bool, char,
references — and let b = a; leaves both usable;
how to tell whether a type will move or copy
is the test for the rest.
Where the difference shows up
Section titled “Where the difference shows up”Calling a function. f(x) never takes anything from the caller. A by-value Rust parameter
takes ownership, and the caller’s binding dies at the call. Borrowing matches your intuition:
f(&x) hands over a reference, and you keep the value. That & is the shape of most Rust
signatures — passing a Vec to a function without cloning
it is the worked example.
Looping. for item in vec consumes the Vec: a for loop calls into_iter, which takes its
receiver by value. for item in &vec is the loop you were expecting
(looping over a Vec without moving it).
Storing. Pushing your value into a collection moves it in; the clone at the push is the
price of a second copy, and
When should you clone? sorts out when to pay it.
Rc (and Arc across threads) is the sharing answer, in the
smart pointers book.
Mutating. Any name for an object can change it in JavaScript. Rust allows mutation through
&mut only, exclusively: while the borrow exists, nothing else may touch the value.
When the value dies
Section titled “When the value dies”A collector frees the object when nothing can reach it; Rust drops the value at the closing brace of the owning scope, before the next statement, every time. How does Rust manage memory without a garbage collector? works through what that buys.
The mapping
Section titled “The mapping”| Python / JavaScript habit | What Rust does instead |
|---|---|
b = a binds a second name |
let b = a; moves, unless the type is Copy |
f(x) shares the argument |
by-value parameter moves; f(&x) borrows |
for x in items leaves it alone |
by-value loop consumes; &items borrows |
| any name can mutate | &mut only, and exclusively |
| freed when unreachable | dropped at the owner’s closing brace |
Related
Section titled “Related”- What is ownership in Rust? — the rule this page maps onto
- How to fix error E0382: borrow of moved value — the error the habit produces
- Borrowing — the next chapter, where
&gets its own rules