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.
In Rust, what happens when you assign a value of a variable of type String to another variable, as in let s2 = s1?
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.
Which statement about borrowing is true when you need to modify data in a function?
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.
Why does Rust require lifetime annotations on references in some function signatures?
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.
Given enum MyEnum { A(i32), B(String) }, how can you safely extract the integer from a variable of type MyEnum?
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.
Why is it recommended to use the match statement or the '?' operator when dealing with Option or Result in Rust?
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.
What is the default mutability of variables in Rust if you declare let x = 10?
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.
Given let x = 5; let x = x + 1;, what is the result of this code in Rust?
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.
Which syntax correctly implements a trait Display for a struct 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.
What feature of Rust’s type system prevents data races at compile time?
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.
What is a key difference between a vector and an array in Rust?
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.