C.W.K.
Stream
Lesson 04 of 05 · published

Ownership Across Function Calls

~11 min · ownership, functions, move, return

Level 0Rust Curious
0 XP0/80 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete

The ownership rules don't stop at let. Passing a value to a function moves it just like an assignment does — and that has consequences worth seeing clearly before we fix them with borrowing.

Passing moves (or copies)

When you call takes(s) with a String, ownership moves into the function. After the call, the caller no longer owns s and can't use it. If the argument is a Copy type like i32, it copies instead and the caller keeps its value — the same rule as assignment.

Returning gives ownership back

A function can hand ownership back through its return value. So the classic (clunky) pattern is: take the value, use it, return it so the caller gets it back. It works, but threading ownership in and out of every function by hand is exhausting — imagine returning a tuple just to keep a value you only wanted to read.

This lesson is the setup for borrowing. Moving values into functions and returning them just to keep them is so tedious that Rust gives you a better tool: references. The next track is the payoff — but you need to feel the pain first to appreciate the fix.

The drop connection

If a function takes ownership and doesn't return the value, the value is dropped when the function ends — its heap memory freed right there. Ownership and lifetime are the same story told from two angles: who owns it decides who frees it, and when.

Code

Ownership moves into and out of functions·rust
fn takes_ownership(s: String) {
    println!("got {s}");
} // s is dropped here — its buffer is freed

fn gives_back(s: String) -> String {
    s // hand ownership back to the caller
}

fn takes_copy(n: i32) {
    println!("copy of {n}");
}

fn main() {
    let a = String::from("hi");
    takes_ownership(a);
    // println!("{a}");        // error[E0382]: a was moved into the function

    let b = String::from("yo");
    let b = gives_back(b);     // got it back, rebound to b
    println!("still have {b}");

    let n = 5;
    takes_copy(n);
    println!("still have {n}"); // i32 is Copy — n is untouched
}

External links

Exercise

Write fn loudest(words: Vec<String>) -> Vec<String> that prints each word uppercased, then returns the vec so the caller keeps it. Notice how awkward it is to return the value just to avoid losing it. Keep that feeling — the borrowing track makes it disappear.
Hint
You're returning words only because passing it in moved it away. That awkwardness is exactly the motivation for references.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue
💛 by Ttoriwarm

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.