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 implements Copy, the value is copied into the function parameter and the caller retains its original value.
  • If arg's type does not implement Copy, ownership moves into the function parameter, and the caller can no longer use arg after 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!