📖 Scenario: You are building a simple Rust program that manages a book title. You will learn how ownership works when passing data to functions.
🎯 Goal: Create a Rust program that stores a book title, passes it to a function that takes ownership, and then prints the title inside the function.
📋 What You'll Learn
Create a variable called book_title with the exact value "The Rust Book"
Create a function called print_title that takes ownership of a String parameter named title
Call the function print_title passing book_title
Print the book title inside the function using println!
💡 Why This Matters
🌍 Real World
Understanding ownership is key to managing memory safely in Rust programs, especially when passing data between functions.
💼 Career
Rust developers must master ownership to write efficient and safe code, which is highly valued in systems programming and performance-critical applications.
Progress0 / 4 steps
1
Create the book title variable
Create a variable called book_title and set it to the String value "The Rust Book" using String::from.
Rust
Hint
Use let book_title = String::from("The Rust Book"); to create the variable.
2
Create the function to take ownership
Create a function called print_title that takes a String parameter named title. Inside the function, print the title using println!("{}", title);.
Rust
Hint
Define fn print_title(title: String) and use println!("{}", title); inside.
3
Call the function with ownership transfer
Inside main, call the function print_title passing the variable book_title to transfer ownership.
Rust
Hint
Call print_title(book_title); inside main.
4
Run the program and see the output
Run the program and print the output. The program should display the book title The Rust Book.
Rust
Hint
Run the program. You should see The Rust Book printed.
Practice
(1/5)
1. What happens when a function in Rust takes ownership of a variable passed to it?
easy
A. The variable is shared between the function and the caller simultaneously.
B. The variable is copied and both original and function have ownership.
C. The function borrows the variable temporarily without taking ownership.
D. The original variable can no longer be used after the function call.
Solution
Step 1: Understand ownership transfer in Rust functions
When a function takes ownership, it means the variable's ownership moves to the function.
Step 2: Effect on original variable after function call
Since ownership moved, the original variable cannot be used anymore after the call.
Final Answer:
The original variable can no longer be used after the function call. -> Option D
Quick Check:
Ownership transfer = original variable unusable [OK]
Hint: Ownership moves to function, original variable becomes invalid [OK]
Common Mistakes:
Thinking ownership is shared, not moved
Assuming variable is copied automatically
Confusing borrowing with ownership transfer
2. Which of the following function signatures correctly borrows a string slice without taking ownership?
easy
A. fn print_name(name: String) { println!("{}", name); }
B. fn print_name(name: &String) { println!("{}", name); }
C. fn print_name(name: &str) { println!("{}", name); }
D. fn print_name(name: String) -> String { name }
Solution
Step 1: Identify borrowing syntax for string slices
Borrowing a string slice uses &str type, which is a reference to a string slice.
Step 2: Check each signature for borrowing without ownership
fn print_name(name: &str) { println!("{}", name); } uses &str, which borrows without taking ownership. fn print_name(name: &String) { println!("{}", name); } borrows a String reference but &str is more idiomatic for slices.
Final Answer:
fn print_name(name: &str) { println!("{}", name); } -> Option C
Quick Check:
Borrow string slice = &str parameter [OK]
Hint: Use &str to borrow string slices without ownership [OK]
A. Inside function: hello
Compile error: use of moved value 's'
B. Inside function: hello
Outside function: hello
C. Inside function: hello
Outside function: <empty>
D. Runtime error: variable s not found
Solution
Step 1: Analyze ownership transfer in function call
The function take_ownership takes ownership of s, so s is moved into the function.
Step 2: Check usage of s after function call
After the call, s is no longer valid in main, so using it causes a compile error.
Final Answer:
Inside function: hello
Compile error: use of moved value 's' -> Option A
Quick Check:
Ownership moved, use after move = compile error [OK]
Hint: Using moved variable after function call causes compile error [OK]
Common Mistakes:
Assuming variable is copied automatically
Expecting output after ownership moved
Ignoring Rust's ownership rules
4. Identify the error in this Rust code and how to fix it:
fn calculate_length(s: String) -> usize {
s.len()
}
fn main() {
let s1 = String::from("hello");
let len = calculate_length(s1);
println!("The length of '{}' is {}.", s1, len);
}
medium
A. Error: s1 is moved into calculate_length; fix by passing &s1 instead.
B. Error: calculate_length returns wrong type; fix by returning &usize.
C. Error: println! macro syntax incorrect; fix by removing quotes.
D. No error; code runs and prints length correctly.
Solution
Step 1: Identify ownership issue in function call
calculate_length takes ownership of s1, so s1 is moved and invalid after call.
Step 2: Fix by borrowing s1 instead of moving
Change function parameter to &String and pass &s1 to avoid moving ownership.
Final Answer:
Error: s1 is moved into calculate_length; fix by passing &s1 instead. -> Option A
Quick Check:
Pass reference to avoid moving ownership [OK]
Hint: Pass references to keep ownership when calling functions [OK]
Common Mistakes:
Trying to return reference to local variable
Ignoring ownership move on function parameters
Misunderstanding borrowing syntax
5. You want to write a function that takes ownership of a vector, doubles each element, and returns ownership back. Which function signature and body correctly implement this in Rust?
hard
A. fn double_vec(v: &Vec<i32>) -> Vec<i32> { v.iter().map(|x| x * 2).collect() }
B. fn double_vec(v: Vec<i32>) -> Vec<i32> { v.iter().map(|x| x * 2).collect() }
C. fn double_vec(v: Vec<i32>) { for x in &v { println!("{}", x * 2); } }
D. fn double_vec(v: &mut Vec<i32>) { for x in v { *x *= 2; } }
Solution
Step 1: Understand ownership and return requirements
The function must take ownership of the vector and return ownership back after doubling elements.
Step 2: Analyze each option for ownership and functionality
fn double_vec(v: Vec<i32>) -> Vec<i32> { v.iter().map(|x| x * 2).collect() } takes ownership (Vec<i32>), creates a new doubled vector, and returns it, satisfying requirements.
Final Answer:
fn double_vec(v: Vec<i32>) -> Vec<i32> { v.iter().map(|x| x * 2).collect() } -> Option B
Quick Check:
Take ownership, return new doubled vector [OK]
Hint: Take Vec ownership and return new Vec after processing [OK]
Common Mistakes:
Borrowing vector but not returning ownership
Not returning anything from function
Modifying borrowed vector without ownership
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.