Question: Sarah is building a chat application and needs to pass messages between functions. She's confused why her code won't compile. What's wrong with Sarah's approach?
fn main() {
let message = String::from("Hello, world!");
send_message(message);
log_message(message); // Sarah expects this to work
}
fn send_message(msg: String) {
println!("Sending: {}", msg);
}
fn log_message(msg: String) {
println!("Logging: {}", msg);
}
Answer:
Sarah's code fails because of Rust's ownership rules. When message
is passed to send_message()
, ownership is transferred, and the variable is no longer valid in main()
. To fix this, Sarah could either:
- Clone the string:
send_message(message.clone());
- Use references: Change both functions to accept
&String
instead ofString
- Return ownership: Make
send_message
return theString
after use
Question: Max is writing a function to modify a customer's account balance. His code compiles but behaves strangely. What's the issue with his approach?
fn main() {
let mut account = String::from("Max: $100");
let balance_ref = &account;
update_balance(&mut account);
println!("Updated info: {}", balance_ref);
}
fn update_balance(info: &mut String) {
info.push_str(" -> $150");
}
Answer:
Max is creating a mutable borrow (&mut account
) while an immutable borrow (balance_ref
) is still in scope. Rust's borrowing rules prevent having both mutable and immutable references to the same data simultaneously. The fix is to move the println!
statement before creating the mutable borrow, or to create balance_ref
after the call to update_balance()
.
Question: Jamie is developing a text processing tool and wants to return the longer of two string slices, but the compiler is complaining. What's missing in Jamie's code?
fn main() {
let text1 = "short";
let text2 = "longer text";
let result = find_longer(text1, text2);
println!("The longer text is: {}", result);
}
fn find_longer(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
Answer: Jamie's code is missing lifetime annotations. The compiler can't determine the relationship between the lifetimes of the input parameters and the return value. The function should be defined with explicit lifetime parameters:
fn find_longer<'a>(x: &'a str, y: &'a str) -> &'a str {
// function body remains the same
}
This tells the compiler that the returned reference will have the same lifetime as both input parameters.
Question: Alex is building a multi-threaded application where one thread generates data and another processes it. The code compiles but gets stuck. What's wrong?
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let val = String::from("important data");
tx.send(val).unwrap();
});
thread::sleep(std::time::Duration::from_millis(100));
let received = rx.recv().unwrap();
println!("Got: {}", received);
}
Answer:
There's nothing wrong with this code! Alex's confusion comes from misunderstanding the behavior of recv()
. The recv()
method will block the main thread until a message is received, but in this case, the spawned thread should have already sent the message by the time recv()
is called (especially with the sleep). The program should work correctly. If Alex wants non-blocking behavior, they could use try_recv()
instead.
Question: Priya is working on a data structure that needs to be shared between different parts of her code. She's confused about why her RefCell approach isn't working as expected.
use std::cell::RefCell;
use std::rc::Rc;
fn main() {
let data = Rc::new(RefCell::new(vec![1, 2, 3]));
let data_clone = Rc::clone(&data);
{
let mut borrowed_data = data.borrow_mut();
borrowed_data.push(4);
let another_borrow = data_clone.borrow();
println!("Vector: {:?}", another_borrow);
}
}
Answer:
Priya's code will panic at runtime because she's trying to create an immutable borrow (another_borrow
) while a mutable borrow (borrowed_data
) is still active. Unlike compile-time borrowing rules, RefCell
enforces borrowing rules at runtime. To fix this, Priya should drop the mutable borrow before creating the immutable one by moving the println!
statement outside the scope where borrowed_data
is defined.
Question: Omar is implementing a generic function to find the largest element in a collection, but his code won't compile for certain types. What's missing?
fn largest<T>(list: &[T]) -> T {
let mut largest = list[0];
for &item in list.iter() {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let numbers = vec![34, 50, 25, 100, 65];
let result = largest(&numbers);
println!("The largest number is {}", result);
let chars = vec!['y', 'm', 'a', 'q'];
let result = largest(&chars);
println!("The largest char is {}", result);
}
Answer:
Omar's function is missing necessary trait bounds. To compare values with >
, the type T
must implement the PartialOrd
trait. Additionally, to copy values from the slice, T
must implement the Copy
trait. The correct function signature should be:
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
// function body remains the same
}
Question: Lin is developing a command processing system using enums and pattern matching. She's confused why her code won't compile.
enum Command {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
fn process_command(command: Command) {
match command {
Command::Quit => println!("Quitting..."),
Command::Move { x } => println!("Moving to x: {}", x),
Command::Write(text) => println!("Text message: {}", text),
Command::ChangeColor(r, g, b) => println!("Changing color to: {}, {}, {}", r, g, b),
}
}
fn main() {
let cmd = Command::Move { x: 10, y: 20 };
process_command(cmd);
}
Answer:
Lin's pattern matching is incomplete. In the Command::Move
arm, she's only matching on the x
field but ignoring the y
field. Rust requires that all fields of a struct-like enum variant be accounted for in the pattern. She should either use Command::Move { x, y }
to capture both fields or Command::Move { x, .. }
to explicitly ignore the remaining fields.
Question: Raj is experimenting with unsafe Rust to manipulate raw pointers. His code compiles but produces unexpected results. What's the issue?
fn main() {
let mut num = 5;
let r1 = &num as *const i32;
let r2 = &mut num as *mut i32;
unsafe {
*r2 += 1;
println!("r1 points to: {}", *r1);
*r2 += 1;
println!("r1 still points to: {}", *r1);
}
}
Answer:
There's no actual bug in Raj's code! The confusion arises from expectations about pointer behavior. Both r1
and r2
point to the same memory location (num
). When Raj modifies the value through r2
, he's changing the value that r1
points to as well. This is actually demonstrating one of the key dangers of unsafe code: having multiple ways to access and modify the same memory. The output will show r1
reflecting the updated value of num
after each modification through r2
.