Problem overview
This quiz shows a function that has a line after its return, and asks what the program prints:
fn greet_and_return() -> String {
return "How are you doing?".to_string();
println!("Hey bro!");
}
fn main_function() {
let question = greet_and_return();
println!("{}", question);
}
The four options differ in which lines appear and in what order. A prints Hey bro! and then How are you doing?. B prints only How are you doing?. C prints How are you doing? and then Hey bro!. D prints only Hey bro!. You answer by assigning a letter to answer.
Approach: run it line by line
Trace the program in the order the computer executes it, not the order in which things look important.
main_functioncallsgreet_and_return(). Execution jumps into that function.- The first line is
return "How are you doing?".to_string();. This creates an ownedString, ends the function on the spot, and hands the string back. - The line
println!("Hey bro!");is never reached. Execution has already left the function. - Back in
main_function,questionreceives"How are you doing?". - The last line prints
question.
The only output is therefore:
How are you doing?
That is option B. Options A and C need the println!("Hey bro!") to run, and option D needs it to run without the other line. None of that can happen.
Does it compile?
Yes. A function whose body ends after a return has nothing left to produce, so the unreachable println! doesn't upset the -> String signature. The compiler does notice the dead line and prints a warning:
warning: unreachable statement
any code following this expression is unreachable
A warning doesn't stop the build. It is the compiler pointing at code that can never run, which is usually a sign that something is in the wrong place.
The solution in Rust
let answer: String = "B".to_string();
The literal "B" has type &str, a borrowed piece of text. Calling to_string() makes an owned String from it, which is what the annotation : String asks for.
Why return ends a function immediately
return means "leave this function now and give this value to the caller". Rust does not run the remaining lines of the function afterwards. A function can have several return statements, usually guarded by an if, and the first one reached wins:
fn describe(n: i32) -> String {
if n < 0 {
return "negative".to_string();
}
println!("checked {}", n);
"not negative".to_string()
}
Calling describe(-5) returns negative and never prints checked. Calling describe(7) skips the if, prints checked 7 and returns not negative.
Compare this with leaving the value as the final expression of the function. These two functions behave the same:
fn a() -> String {
return "same".to_string();
}
fn b() -> String {
"same".to_string()
}
The second form has no return and no semicolon, so the value of the last expression is returned. Either way, code that comes after the point where the value is produced doesn't help the function print anything, so work to be done before returning must be written before it.
Getting both lines out
If the goal were to see Hey bro! first and How are you doing? second, the order inside the function has to change: print first, then return.
fn greet_and_return() -> String {
println!("Hey bro!");
return "How are you doing?".to_string();
}
Now Hey bro! is printed while the function runs, and How are you doing? is printed afterwards by main_function, because it prints the returned value. This is option A, and the contrast with the quiz's code shows why the order of the two lines decides the answer.
Common mistakes
- Reading the function as "print, then return". The lines run in the order written, and the
returncomes first. - Thinking the leftover line runs "after the function finishes". Statements after a
returnin the same block are never run at all. - Expecting the compiler to reject dead code. It only warns.
Key takeaways
- A
returnends the function at once; any statement below it in the same block is unreachable. - The compiler warns about unreachable statements but still builds the program.
- To print before returning, put the
println!above thereturn.
