Ownership
Ownership Transfer into Functions
Passing a variable by value to a function (func(arg)) follows the exact same ownership rules as assigning a variable (let param = arg;):
- If
arg's type implementsCopy, the value is copied into the function parameter and the caller retains its original value. - If
arg's type does not implementCopy, ownership moves into the function parameter, and the caller can no longer useargafter the call.
How Function Parameters Take Ownership and Drop Values
Caller (`main`) Callee (`consume_report`)
+-------------------------------+ +----------------------------------+
| let report = String::from(..);| | fn consume_report(doc: String) { |
| consume_report(report); ──────+──(Moves)─> | println!("{doc}"); |
| // `report` is now moved! | | } <-- `doc` is DROPPED here! |
+-------------------------------+ +----------------------------------+
fn consume_report(doc: String) {
println!("Processing and dropping doc: '{doc}'");
} // `doc` goes out of scope here; its heap buffer is freed!
fn log_code(code: u32) {
println!("Status code (copied): {code}");
}
fn main() {
let report = String::from("Quarterly Summary");
let status: u32 = 200;
// 1. `String` is moved into `consume_report`:
consume_report(report);
// 2. `u32` is `Copy`, so `status` is copied into `log_code` and remains valid:
log_code(status);
println!("status is still valid in main: {status}");
}
Processing and dropping doc: 'Quarterly Summary'
Status code (copied): 200
status is still valid in main: 200
Mutable Parameters (mut param: T) on Owned Arguments
When a function takes ownership of a parameter by value, the callee can mark the parameter mut (fn process(mut text: String)) to modify its owned value locally—even if the caller's variable was declared immutable! Why? Because the callee now owns the value exclusively:
fn print_shouted(mut text: String) {
text.push_str("!!!");
println!("{}", text.to_uppercase());
}
fn main() {
let message = String::from("hello"); // Declared immutable in `main`
print_shouted(message); // Moved into `mut text` parameter!
}
HELLO!!!
When Should a Function Take Ownership (T) vs Borrow (&T)?
| Function Goal | Idiomatic Parameter Type | Reason |
|---|---|---|
| Only needs to read or inspect the data | &T (or &str / &[U]) |
Avoids forcing the caller to give up ownership or .clone() |
| Stores the value inside a struct, collection, or thread | T (by value) |
The function genuinely needs to keep or transfer ownership |
Consumes and transforms a buffer in place (Builder pattern) |
mut self or T -> T |
Reuses the existing heap allocation without reallocating |
Intentionally closes or destroys a resource (drop, File::sync_all) |
self / T (by value) |
Statically prevents the caller from using the closed resource again! |