Build the mental model
In Rust, a function's body is a block mixing statements (which have effects but return no value) with expressions (which produce a value) — if a block's final line is an expression without a trailing semicolon, that expression's value becomes the block's implicit return value, no explicit `return` keyword needed. This differs sharply from languages like C or Java, and it's a consequence of Rust's "most things are expressions" philosophy — even `if` is an expression, not a statement (`let x = if condition { 5 } else { 6 };`), with the constraint that both branches must produce the same type. There are three loop constructs: `loop` (an infinite loop that only stops on an explicit `break`, and `break value;` can return that value as the loop's own result), `while` (runs while a condition stays true), and `for` (traverses an iterator, sidestepping off-by-one index errors entirely). Explicit parameter and return types in a function signature also double as documentation, letting a caller immediately see, as a compiler error, exactly what type is expected if they get it wrong.
Connect it to a real scenario
Write the content analyzer's `count_words` function as `fn count_words(text: &str) -> u32 { text.split_whitespace().count() as u32 }` — the expression inside the braces has no semicolon, so it becomes the implicit return value. To classify a difficulty level from tag string, use an `if`/`else if`/`else` chain as an expression: `let level = if word_count < 500 { "basic" } else if word_count < 1500 { "intermediate" } else { "advanced" };`, keeping every branch's type as `&str`. Iterate every file in the lesson directory with `for entry in files { ... }`, accumulating word counts without any manual index management.
Try the working example
fn count_words(text: &str) -> u32 {
text.split_whitespace().count() as u32
}
fn classify(word_count: u32) -> &'static str {
if word_count < 500 {
"basic"
} else if word_count < 1500 {
"intermediate"
} else {
"advanced"
}
}
fn main() {
let count = count_words("Rust ownership prevents data races");
println!("{count} words -> {}", classify(count));
let mut n = 0;
let result = loop {
n += 1;
if n == 5 { break n * 10; }
};
println!("loop result: {result}");
}You print "5 words -> basic" and "loop result: 50".5-minute try-it
Write `fn classify_length(n: u32) -> &'static str` that returns "short" if n < 3, "medium" if n < 8, otherwise "long" — using a single `if` expression with no `return` keyword at all.
One important caution
Adding a trailing semicolon to a function's final expression, turning what should be an implicit return value into `()` (the unit type) — resulting in a return-type mismatch compile error.
Writing `for` loops over a collection in an index-based style (`for i in 0..vec.len() { vec[i] }`) instead of iterator-based (`for item in &vec`) — raising the risk of off-by-one bugs and needlessly complicating the code.