Rust Interview Prep: Tricky Questions and Real-World Scenarios Quiz Quiz

Sharpen your Rust skills with this quiz focused on real-world programming scenarios, tricky concepts, ownership, borrowing, lifetimes, and error handling in Rust. Assess your readiness for Rust interviews by tackling common pitfalls and language nuances.

  1. Understanding Ownership Transfer

    In Rust, what happens when you assign a value of a variable of type String to another variable, as in let s2 = s1?

    1. Ownership of the String moves to s2, making s1 invalid
    2. A deep copy of the String is made automatically
    3. A shallow copy of the String is made
    4. Both s1 and s2 share ownership of the String

    Explanation: In Rust, assigning a variable of type String to another variable transfers ownership, so s1 becomes invalid and only s2 can be used. This ensures memory safety without a garbage collector. Both sharing and automatic deep copying do not occur with this assignment. A shallow copy does not happen because Rust's move semantics prevent double freeing of resources.

  2. Borrowing and Mutability

    Which statement about borrowing is true when you need to modify data in a function?

    1. You pass ownership of x to the function
    2. You use an immutable reference, like u0026x
    3. You clone x inside the function
    4. You must pass a mutable reference, like u0026mut x

    Explanation: Passing a mutable reference with u0026mut x allows the function to modify the original data without transferring ownership. Passing ownership disables the original variable after the call, and an immutable reference would not allow mutation. Cloning copies data instead of modifying the original, which is less efficient and not necessary for simple mutation.

  3. Lifetimes Simplified

    Why does Rust require lifetime annotations on references in some function signatures?

    1. To ensure that references remain valid for as long as needed
    2. To enforce variable naming conventions
    3. To increase compilation speed
    4. To improve code readability

    Explanation: Lifetime annotations help Rust ensure that references are not used after the data they refer to has gone out of scope, preventing dangling references. Compilation speed and variable naming are unrelated to lifetimes, and while annotations can make code more explicit, their main purpose is enforcing reference validity, not readability.

  4. Pattern Matching in Enums

    Given enum MyEnum { A(i32), B(String) }, how can you safely extract the integer from a variable of type MyEnum?

    1. Access it directly with my_var.0
    2. Use if let MyEnum::A(val) = my_var
    3. Call my_var.to_integer()
    4. Downcast my_var to i32

    Explanation: Pattern matching with if let lets you safely access the value inside enum variants by destructuring. Direct field access like my_var.0 is invalid since it's not a tuple struct, downcasting is not how Rust handles enums, and there is no built-in to_integer method for enums.

  5. Option and Result Handling

    Why is it recommended to use the match statement or the '?' operator when dealing with Option or Result in Rust?

    1. They always unwrap the value without checks
    2. They ignore all errors automatically
    3. They ensure you handle the None or Err cases safely
    4. They convert the types to integers

    Explanation: Using match or the '?' operator forces you to deal with possible error cases, making Rust programs less prone to runtime errors. Ignoring errors or unwrapping without checks is unsafe and discouraged, and neither approach converts these types to integers automatically.

  6. Mutability by Default

    What is the default mutability of variables in Rust if you declare let x = 10?

    1. x is immutable and cannot be changed
    2. x is mutable only inside functions
    3. x's mutability depends on its type
    4. x is mutable and can always be changed

    Explanation: Variables declared with let are immutable by default, so their values cannot be modified. To change a variable after declaration, you must use let mut. The variable's type does not affect mutability, and function scope does not change this behavior.

  7. Shadowing vs. Mutation

    Given let x = 5; let x = x + 1;, what is the result of this code in Rust?

    1. x becomes mutable automatically
    2. x is shadowed, and its value is now 6
    3. This code fails because x cannot be redefined
    4. The old value of x is lost and causes a memory leak

    Explanation: In Rust, you can shadow variables by declaring a new variable with the same name, so x takes the new value of 6. Redefining a variable is allowed via shadowing, not mutation, and the variable does not become mutable unless declared as such. The old value is released properly; no memory leak occurs.

  8. Trait Implementation

    Which syntax correctly implements a trait Display for a struct Foo?

    1. fn Foo::Display() { ... }
    2. provide Display for Foo { ... }
    3. impl std::fmt::Display for Foo { ... }
    4. implement Display for Foo { ... }

    Explanation: Trait implementations use the impl keyword followed by Trait for Type and the relevant method definitions. The other options are invalid syntax: Rust does not use fn Type::Trait() or implement/provide keywords for trait implementation.

  9. Data Race Prevention

    What feature of Rust’s type system prevents data races at compile time?

    1. Ownership and borrowing rules
    2. Dynamic type checking
    3. Automatic garbage collection
    4. Global locking by default

    Explanation: Rust's ownership and borrowing system, enforced at compile time, guarantees that only one mutable reference or many immutable references to data can exist simultaneously, preventing data races. Garbage collection is absent in Rust, and type checking or global locks do not prevent data races by themselves.

  10. Vectors vs. Arrays

    What is a key difference between a vector and an array in Rust?

    1. Arrays can store multiple types, while vectors cannot
    2. Vectors require explicit memory allocation but arrays do not
    3. Vectors are stored on the stack, arrays on the heap
    4. A vector can change size at runtime, while an array's size is fixed

    Explanation: Vectors in Rust are growable collections allocated on the heap and can change size at runtime, while arrays have a fixed size determined at compile time. Both store elements of a single type, and the memory allocation point is opposite — arrays are usually stack-allocated for small sizes, vectors heap-allocated. Neither can easily store mixed types.