04–05. Control Flow · Functions, Expressions, Return Values, Doc Comments
Rust's `if` is an expression — you can write `let x = if cond { 1 } else { 2 }`. This lesson covers `loop` / `while` / `for`, Rust-specific touches like attaching a value to `break`, labeled break / continue across nested loops, and `for` over ranges and iterators. Defining functions with `fn name(arg: T) -> R { ... }`, the distinction between expressions and statements, returning implicitly via the last expression vs. explicit `return`, and `///` doc comments that turn into HTML via `cargo doc`. This lesson sets you up to write idiomatic functional Rust.
What you'll learn
- 1Understand `if` is an expression and use it as a value
- 2Pick the right one between `loop` / `while` / `for`
- 3Return a value from `loop` via `break value`
- 4Break out of nested loops cleanly with labels
- 5Iterate with ranges (`0..10`, `0..=10`) and iterators
- 6Define functions with explicit parameter and return types
- 7Tell expressions from statements
- 8Return implicitly from the last expression — no explicit `return`
- 9Write `///` doc comments and build HTML with `cargo doc`
- 10Return multiple values via tuples
04. Control Flow (if · loop · while · for · break-with-value)
Rust's `if` is an expression — you can write `let x = if cond { 1 } else { 2 }`. This lesson covers `loop` / `while` / `for`, Rust-specific touches like attaching a value to `break`, labeled break / continue across nested loops, and `for` over ranges and iterators.
Overview
Rust has no `cond ? a : b` ternary. It doesn't need one — `if` itself produces a value, so the same expressiveness comes naturally. This consistency keeps code short and readable.
Core Concepts
1) if is an expression
let n = 10;
let kind = if n % 2 == 0 { "even" } else { "odd" };
println!("{}", kind); // evenEach branch must produce the same type. One arm returning `"even"` and the other `42` is a compile error.
2) loop — infinite, exits via break
- Unconditional infinite loop
- `break value` makes that value the result of the `loop` expression
- Labels (`'outer:`) target which loop break/continue applies to
3) while — runs while condition is true
Skipped entirely if the condition starts false. For index-based traversal `for` is usually nicer.
4) for — iterator traversal
- `for x in 0..10` — 0,1,...,9 (exclusive upper)
- `for x in 0..=10` — 0,1,...,10 (inclusive)
- `for x in &v` — iterate Vec / slice
Hands-on Examples
Returning a value from `loop` — handy for search:
fn main() {
let arr = [3, 7, 11, 13, 17];
let target = 11;
let idx = loop {
let mut i = 0;
while i < arr.len() {
if arr[i] == target { break i as i32; }
i += 1;
}
break -1;
};
println!("index = {}", idx); // 2
}Labels exit nested loops:
fn main() {
'outer: for i in 0..5 {
for j in 0..5 {
if i + j == 6 {
println!("found ({}, {})", i, j);
break 'outer;
}
}
}
}Ranges and iterators with `for`:
fn main() {
for i in 0..3 { println!("i={}", i); } // 0,1,2
for i in 1..=3 { println!("j={}", i); } // 1,2,3
let v = vec![10, 20, 30];
for x in &v { println!("x={}", x); }
for (idx, x) in v.iter().enumerate() {
println!("{}-{}", idx, x);
}
}Common Mistakes
Q. Where's C-style `for (i = 0; i < n; i++)`?
A. Rust doesn't have it. `for i in 0..n` covers the common case, and `.iter().enumerate()` covers the rest. Off-by-one bugs go down too.
Q. What if `if` arms have different types?
A. Compile error. `let x = if c { 1 } else { "two" }` won't fly. Make types match, or wrap them in an enum.
Q. What's `while let`?
A. A `while` with pattern matching. `while let Some(v) = iter.next() { ... }` loops while the iterator yields Some. Covered in the pattern-matching lesson.
Recap
- `if` is an expression — use it on the right-hand side of `let`
- `loop` is infinite, `break value` returns from it
- `for in range` / `for in iterator` are the most common loop forms
- Use labels to break out of nested loops precisely
Try It Yourself
- Sum every even number from 1 to 100 using `for` and `if`
- Use `loop + break value` to compute sqrt(2) by Newton's method in 5 iterations
- Find the first `(i, j)` in a 2D grid where `i + j == 7` using a labeled break
05. Functions, Expressions, Return Values, Doc Comments
Defining functions with `fn name(arg: T) -> R { ... }`, the distinction between expressions and statements, returning implicitly via the last expression vs. explicit `return`, and `///` doc comments that turn into HTML via `cargo doc`. This lesson sets you up to write idiomatic functional Rust.
Overview
Rust functions are straightforward, but expression-orientation trips up people from imperative languages — **whether you put a semicolon on the last line decides whether anything is returned**. Once that clicks, code gets dramatically shorter.
Core Concepts
1) Function syntax
fn add(a: i32, b: i32) -> i32 {
a + b // no semicolon → this is the return value
}2) Expression vs. statement
- **Expression** — produces a value. `5`, `a + b`, `if cond { 1 } else { 2 }`, blocks `{ let x = 1; x + 1 }`
- **Statement** — performs an action but produces no value. `let x = 5;`, `fn f() {}`
- Adding a semicolon to an expression turns it into a statement — value is discarded
3) Explicit return
Use `return value;` when you want an early exit. For the normal happy path the trailing-expression form is more idiomatic.
4) Doc comments
- `///` declares documentation for the next item
- Markdown is supported — code blocks become automatic doctests
- `cargo doc --open` generates and opens HTML docs
Hands-on Examples
Implicit return alongside explicit `return`:
fn divide(a: i32, b: i32) -> i32 {
if b == 0 {
return -1; // early exit
}
a / b // last expression → returned
}
fn main() {
println!("{}", divide(10, 3)); // 3
println!("{}", divide(10, 0)); // -1
}Return multiple values via a tuple:
fn minmax(v: &[i32]) -> (i32, i32) {
let mut mn = v[0];
let mut mx = v[0];
for &x in v {
if x < mn { mn = x; }
if x > mx { mx = x; }
}
(mn, mx)
}
fn main() {
let (a, b) = minmax(&[3, 7, 1, 9, 4]);
println!("min={}, max={}", a, b); // 1, 9
}Doc comments — Markdown + doctest:
/// Add two numbers and return the sum.
///
/// # Examples
///
/// ```
/// assert_eq!(my_crate::add(2, 3), 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}cargo doc --open # generate and open HTML docs
cargo test --doc # run doctestsCommon Mistakes
Q. My return value disappeared — I wrote `a + b` and got nothing back
A. `a + b;` (with semicolon) is a statement, the value is dropped. The trailing expression must have **no semicolon**.
Q. What's the unit type `()`?
A. Roughly equivalent to `void` elsewhere. Omitting the return type means `-> ()` — the function returns no meaningful value.
Q. Can main return a value?
A. Yes — `fn main() -> Result<(), Box<dyn Error>>` enables `?` inside main. Covered in the error-handling lessons.
Recap
- Function signatures spell out parameter and return types
- Last expression is the return value — semicolon turns it into a statement
- Use `return` for early exits
- /// doc comments become HTML via `cargo doc`, doctests run via `cargo test --doc`
Try It Yourself
- Write Celsius ↔ Fahrenheit converter functions and call them from main
- Write a `stats` function returning `(i32, i32, f64)` for min / max / mean
- Add a /// example block and run `cargo test --doc` to verify the doctest passes
All lecture materials and example code are openly available on GitHub.
View on GitHub ↗Comments 0
No comments yet. Be the first.
Only members can comment.
Comments with ads, abuse, personal information or illegal content are hidden or removed. Comments are shown with your nickname. Privacy Policy