How to pass ownership of a value to a function
Short answer: a parameter whose type is the value’s own type — fn describe(text: String), not
fn describe(text: &str) — takes ownership of the argument. Passing it in is a move, so the call is the
last place your variable works: the next line that mentions it fails to compile with E0382. If the
argument’s type is Copy, like i32, no move happens and the variable carries on as before.
The parameter type decides
Section titled “The parameter type decides”One variable, two calls, no error — because the parameter’s type is Copy:
fn describe(number: i32) { println!("the function got {number}");}
fn main() { let score = 42;
describe(score); describe(score);
println!("the caller still has {score}");}the function got 42the function got 42the caller still has 42i32 is Copy, so the argument is duplicated into the parameter and score is untouched. Calling
describe twice is fine. How to tell whether a type will move or copy
is the test for any type, including your own.
Change the parameter’s type — String instead of i32 — and the second call stops compiling:
fn describe(text: String) { println!("the function got {text}");}
fn main() { let message = String::from("hello");
describe(message); describe(message);}error[E0382]: use of moved value: `message` --> src/main.rs:9:14 |6 | let message = String::from("hello"); | ------- move occurs because `message` has type `String`, which does not implement the `Copy` trait7 |8 | describe(message); | ------- value moved here9 | describe(message); | ^^^^^^^ value used here after move |note: consider changing this parameter type in function `describe` to borrow instead if owning the value isn't necessary --> src/main.rs:1:19 |1 | fn describe(text: String) { | -------- ^^^^^^ this parameter takes ownership of the value | | | in this functionhelp: consider cloning the value if the performance cost is acceptable |8 | describe(message.clone()); | ++++++++
For more information about this error, try `rustc --explain E0382`.The complaint is about message, not about describe. The first call took ownership on line 8 —
value moved here, printed under the argument, because the call is where the move happens — so line 9
reads a binding that owns nothing.
The note: is the part worth reading twice. rustc describes the parameter as this parameter takes ownership of the value, and its first suggestion is to make the parameter a borrow, which is the last
section of this page. The second suggestion, message.clone(), compiles: every call gets a buffer of
its own and message keeps the original.
The destructor goes with it
Section titled “The destructor goes with it”The error tells you a move happened. A destructor tells you when, which is the other way to check that the callee really did take the value:
struct Payload(String);
impl Drop for Payload { fn drop(&mut self) { println!("dropping {:?}", self.0); }}
fn consume(item: Payload) { println!("consume buffer: {:p}", item.0.as_ptr()); println!("consume text: {:?}", item.0);}
fn main() { let item = Payload(String::from("report")); println!("caller buffer: {:p}", item.0.as_ptr());
consume(item); println!("after the call");}caller buffer: 0x102f1d0d0consume buffer: 0x102f1d0d0consume text: "report"dropping "report"after the callBoth addresses are the same, so no second buffer was allocated on the way in: the text stayed where it was, and the parameter is that buffer under a new name — the same thing that happens when you move between two bindings in one function (How move semantics work in Rust puts a byte count on it). The hex digits differ every run; that they match is the part that holds.
Then dropping "report" lands before after the call. item is a binding in consume, so it goes
out of scope when consume returns and the free happens during the call rather than at the end of
main. That’s the callee’s side of the handover, and it’s the thing to look at when you can’t tell
whether a function took the value or borrowed it.
If the function only needs to read
Section titled “If the function only needs to read”A & on the parameter is what the note: was suggesting. The call gets a reference, and ownership
stays where it is:
fn describe(text: &str) { println!("the function got {text}");}
fn main() { let message = String::from("hello");
describe(&message); describe(&message);
println!("the caller still has {message}");}the function got hellothe function got hellothe caller still has helloBoth calls work and message is still usable after them. & is a borrow: no ownership changes hands,
and nothing is freed when describe returns. That’s the subject of the Borrowing book —
this page stops at the rule that the parameter’s type is what decides.
Related
Section titled “Related”- Why does my String not implement Copy? — why the parameter can’t just copy the value
- Why can’t you use a value after moving it? — the double free this rule prevents
- How to tell whether a type will move or copy — the test to run on your own types
- Moves and ownership — the rest of this chapter
- Next in this chapter: how to return ownership from a function