Skip to Content
Course content

88: Common Rust Interview Questions on Ownership

Click on the "Edit" button in the top corner of the screen to edit your slide content.

When I'm interviewing candidates for Rust roles, I rarely ask them to recite the definition of ownership from the book. Anyone can memorize a paragraph. Instead, I look for whether they actually feel how ownership moves through a program. Most people stumble when I ask them to explain why a piece of code fails to compile, specifically when they're juggling String types in functions.

The Move That Breaks Your Logic

The most common "naive" mistake I see—and the one that usually triggers a long conversation in an interview—is treating a String like it's a cheap pointer, similar to how you'd handle a string in Java or Python. Look at this snippet. I'm trying to build a simple greeting system where I log the user's name and then use that name again to update a database record.

fn log_name(name: String) {
    println!("Logging access for: {}", name);
}

fn main() {
    let user_name = String::from("Ferris");
    log_name(user_name); 
    println!("Updating record for: {}", user_name); // ❌ Compiler Error!
}

The candidate usually says, "Oh, I just need to pass it in." But the compiler sees something else. By passing user_name by value to log_name, ownership is moved. The main function no longer owns that memory; log_name does. Once log_name hits its closing brace, the string is dropped. Trying to use user_name on the last line is a classic "use of moved value" error. It's a fundamental mistake because it shows the developer is fighting the borrow checker rather than working with it.

Borrowing for Read-Only Access

The "senior" way to handle this isn't just to add an ampersand and call it a day. It's about understanding the difference between &String and &str. If I'm just printing a name, I don't need to own the string, and I certainly don't need a reference to the String object itself—I just need a reference to the slice of characters.

fn log_name(name: &str) {
    println!("Logging access for: {}", name);
}

fn main() {
    let user_name = String::from("Ferris");
    log_name(&user_name); // We lend the value, we don't give it away
    println!("Updating record for: {}", user_name); // ✅ Works perfectly
}

Notice I changed the parameter to &str. This is a key interview point: Deref Coercion. By taking a string slice (&str) instead of a reference to a String (&String), the function becomes much more flexible. It can now accept a String, a literal "hello", or a slice of another string. I always tell my juniors: if your function only needs to read the data, use &str. It's cleaner and more idiomatic.

When You Actually Need to Take Ownership

Now, an interviewer might push back and ask, "So should we just always borrow?" That's the trap. If you borrow everything, you'll eventually hit a wall where you need to move data into a new thread or store it in a struct that must own its data to remain valid.

If log_name actually needed to push that name into a Vec that lives as long as the whole program, borrowing wouldn't work because the user_name in main will eventually be destroyed. In that specific case, moving ownership is the correct architectural choice. The trade-off is simple: borrowing is for temporary access; moving is for transferring responsibility. If you can't decide, ask yourself: "Who is responsible for cleaning up this memory?" If it's the function receiving the data, move it. If it's the caller, borrow it.




📋 Practical Task

Fixing the User Session Registry

You are reviewing a colleague's code for a session manager. They've written a function register_session that is intended to add a username to a global list, but the current implementation prevents the main function from using the username after registration, causing a compilation error. Additionally, they are using an inefficient type for the logging part of the function.

Your goal: Modify the provided code so that register_session still takes ownership of the name (to store it in the sessions vector), but a secondary function print_session_status is implemented to borrow the name and print it without taking ownership.

fn register_session(sessions: &mut Vec<String>, name: String) {
    sessions.push(name);
}

fn print_session_status(name: String) { 
    // This is the buggy part! It takes ownership just to print.
    println!("Session active for: {}", name);
}

fn main() {
    let mut sessions = Vec::new();
    let current_user = String::from("Alice");

    print_session_status(current_user.clone()); // The colleague used .clone() to hack around the move
    register_session(&mut sessions, current_user);
    
    // This line should work without needing to clone current_user earlier
    println!("User {} has been registered.", current_user); 
}

Requirements:

  • Rewrite print_session_status to use a string slice (&str) instead of taking ownership.
  • Remove the .clone() call in main.
  • Ensure that register_session still moves the String into the vector, but only after the status has been printed, so that the final println! in main still has access to the variable (Hint: you may need to reorder the calls or change how register_session is called relative to the final print).
Rating
0 0

There are no comments for now.

to be the first to leave a comment.