Step 5 of 6
Ownership and functions
Passing a value to a function works exactly like assigning it to a variable: a String argument moves into the parameter, and an i32 argument is copied. The parameter is the new owner, and its scope is the function's body.
fn print_ticket(ticket: String) { // ticket takes ownership
println!("Ticket: {}", ticket);
} // ticket goes out of scope: the String is dropped
fn print_seat(seat: u32) { // seat gets a copy
println!("Seat: {}", seat);
}
fn main() {
let t = String::from("T-42");
let s = 17;
print_ticket(t); // t moves into the function
print_seat(s); // s is copied
println!("seat {} is still usable here", s);
// println!("{}", t); // error: t was moved
}
Ticket: T-42
Seat: 17
seat 17 is still usable here
Call print_ticket(t) twice and the second call fails:
fn print_ticket(ticket: String) {
println!("Ticket: {}", ticket);
}
fn main() {
let t = String::from("T-42");
print_ticket(t);
print_ticket(t);
}
error[E0382]: use of moved value: `t`
|
6 | let t = String::from("T-42");
| - move occurs because `t` has type `String`, which does not implement the `Copy` trait
7 | print_ticket(t);
| - value moved here
8 | print_ticket(t);
| ^ value used here after move
|
note: consider changing this parameter type in function `print_ticket` to borrow instead if owning the value isn't necessary
The note is the compiler pointing at the next module: a function that only reads a value usually shouldn't take ownership of it.
Giving ownership back
A function can also return a value, which moves it out to the caller. Creating a value inside a function and returning it is completely normal: it isn't dropped at the function's } because it has already moved out.
fn new_ticket(number: u32) -> String {
let ticket = format!("T-{}", number);
ticket // moves out to the caller
}
fn add_vip(mut ticket: String) -> String {
ticket.push_str(" (VIP)");
ticket // given back
}
fn main() {
let t = new_ticket(7);
let t = add_vip(t); // move in, get it back
println!("{}", t);
}
T-7 (VIP)
Two details:
mut ticket: String: the function owns its parameter, so it may change it, but like any variable it has to be declaredmut. Forget it and you geterror[E0596]: cannot borrow `ticket` as mutable, as it is not declared as mutable.let t = add_vip(t);moves theStringin and binds what comes back. Shadowing keeps the same name.
Returning several values
If a function needs to look at a String and return a result, it has to give the String back as well, for example in a tuple:
fn measure(text: String) -> (String, usize) {
let n = text.chars().count();
(text, n)
}
fn main() {
let msg = String::from("good game");
let (msg, n) = measure(msg);
println!("'{}' has {} characters", msg, n);
}
'good game' has 9 characters
It works, but it's clumsy: every function that just wants to look at a value has to hand it back. That's exactly the problem borrowing, the next module, solves.
drop is just a function
You can drop a value early by passing it to drop, a function from the standard library that's always available:
fn main() {
let report = String::from("imagine 50 MB of text here");
println!("report has {} bytes", report.len());
drop(report); // freed now, not at the end of main
println!("memory released");
}
report has 26 bytes
memory released
There's no magic in it. Its whole definition is pub fn drop<T>(_x: T) {}, with an empty body. The value moves into the parameter _x, the function ends, _x goes out of scope, and the value is dropped. Ownership does the rest. (The <T> means "a value of any type"; you'll learn that syntax in the generics module.)
Common mistakes
- Using an argument after passing it (E0382
use of moved value). Either give it back, or (next module) let the function borrow it. - Forgetting to return the value: a function that takes a
Stringand doesn't return it drops it at its}. - Forgetting
muton a parameter you change (E0596).
Your task
Write mark_paid. It takes ownership of an order description, appends [PAID] (with a leading space) and gives the String back. For example, mark_paid(String::from("#17 2x tacos")) returns "#17 2x tacos [PAID]".
Challenge 2
char_count takes ownership of the message and never gives it back, so main can't print it afterwards. Without .clone(), change char_count to return the message and the count as a tuple (String, usize), and destructure it in main with let (message, n) = char_count(message);. The program should print:
gg wp has 5 characters
Challenge 3
Write longer, which takes ownership of two Strings and returns the longer one (by .len()); if they're the same length, return a. The other one is dropped when the function ends: nothing else to do.
Previous: Copy types and clone Next: Ownership and collections