What Is Control Flow?
if / else Control Flow
if as an Expression (Very Important)
loop – Infinite Loop
while Loop
for Loop (Most Common & Safest)
Iterating Over an Array
break and continue
Nested Loops
Loop Labels (Advanced but Important)
Interview-ready questions and answers
What Is Control Flow?
Control flow decides which code runs and how many times it runs based on conditions or repetition.
Rust provides:
if / else
if as an expression
loop
while
for
1️⃣
if / else Control Flow
Used to execute code based on conditions.
Example
fn main() {
let number = 10;
if number > 0 {
println!("Positive number");
} else {
println!("Not positive");
}
}
Key Rules
Condition must be bool
No parentheses required
Curly braces {} are mandatory
❌ Invalid in Rust:
if number { } // error: expected bool
2️⃣
if as an Expression (Very Important)
In Rust, if can return a value.
Example
fn main() {
let number = 5;
let result = if number % 2 == 0 {
"Even"
} else {
"Odd"
};
println!("{}", result);
}
Rules
Both branches must return the same type
No semicolon at the end of returned expressions
3️⃣
loop – Infinite Loop
loop runs forever unless explicitly stopped.
Example
fn main() {
let mut count = 0;
loop {
count += 1;
println!("Count: {}", count);
if count == 3 {
break;
}
}
}
When to Use
Unknown number of iterations
Event listeners
Retry logic
loop with Return Value
fn main() {
let mut x = 0;
let result = loop {
x += 1;
if x == 5 {
break x * 2;
}
};
println!("Result: {}", result);
}
✔ loop can return a value using break value;
4️⃣
while Loop
Runs while a condition is true.
Example
fn main() {
let mut number = 3;
while number > 0 {
println!("{}", number);
number -= 1;
}
println!("Done!");
}
Best Use Case
When condition is known
Simple countdowns
Conditional repetition
5️⃣
for Loop (Most Common & Safest)
Used to iterate over ranges or collections.
Example: Range
fn main() {
for i in 1..5 {
println!("{}", i);
}
}
➡️ Prints 1 2 3 4
Inclusive Range
for i in 1..=5 {
println!("{}", i);
}
➡️ Prints 1 2 3 4 5
Iterating Over an Array
fn main() {
let numbers = [10, 20, 30];
for num in numbers {
println!("{}", num);
}
}
✔ Prevents out-of-bounds errors
✔ Preferred over while for collections
6️⃣
break and continue
break – Exit loop
for i in 1..10 {
if i == 5 {
break;
}
println!("{}", i);
}
continue – Skip current iteration
for i in 1..6 {
if i == 3 {
continue;
}
println!("{}", i);
}
7️⃣
Nested Loops
fn main() {
for i in 1..=3 {
for j in 1..=2 {
println!("i={}, j={}", i, j);
}
}
}
8️⃣
Loop Labels (Advanced but Important)
Used to control outer loops.
Example
fn main() {
'outer: for i in 1..=3 {
for j in 1..=3 {
if i == 2 && j == 2 {
break 'outer;
}
println!("i={}, j={}", i, j);
}
}
}
✔ Breaks the outer loop
Interview-ready questions and answers
Interview Questions & Answers on Rust Control Flow
Basic Level1. What is control flow in Rust?
Answer:
Control flow determines which code executes and how many times based on conditions or repetition using constructs like if, loop, while, and for.
- Which control flow statements are available in Rust?
Answer:
Rust provides:
if / else
loop
while
for
- Does Rust allow conditions without parentheses?
Answer:
Yes. Rust does not require parentheses around conditions.
if x > 5 {
println!("Greater");
}
- Can if be used as an expression in Rust?
Answer:
Yes. if is an expression and can return a value.
let result = if x > 0 { "Positive" } else { "Negative" };
- What restriction applies when using if as an expression?
Answer:
Both branches must return the same data type.
Intermediate Level
- What is the difference between loop and while?
Answer:
loop runs infinitely until break
while runs while a condition is true
- Can loop return a value?
Answer:
Yes, using break value;.
let result = loop {
break 10;
};
- Which loop is safest for iterating collections?
Answer:
The for loop, because it prevents out-of-bounds access.
- What does the continue keyword do?
Answer:
It skips the current iteration and moves to the next one.
- What does the break keyword do?
Answer:
It immediately exits the loop.
Advanced Level
- What are loop labels in Rust?
Answer:
Loop labels allow control of nested loops, enabling breaking or continuing outer loops.
'outer: loop {
break 'outer;
}
- How does Rust ensure safety in loops?
Answer:
Through:
Mandatory boolean conditions
Bounds checking
Compile-time ownership rules
- Can while be used to iterate over arrays?
Answer:
Yes, but it is not recommended due to possible index errors.
- Why is for preferred over while?
Answer:
Because for loops:
Are safer
More readable
Automatically handle bounds
- Can control flow constructs return values?
Answer:
Yes. Rust treats if and loop as expressions.
Tricky Questions
- Is this code valid?
let x = if true { 5 } else { 6 };
Answer:
Yes. Both branches return the same type.
- Is this valid?
let x = if true { 5 } else { "six" };
Answer:
No. Return types must match.
- Can break be used outside loops?
Answer:
No. It causes a compile-time error.
- What happens if a loop never breaks?
Answer:
The program runs indefinitely.
- Can continue be used in if blocks?
Answer:
No. continue is only valid inside loops.
Part 2: MCQs on Rust Control Flow (With Answers)
MCQs – Basics
- W*hich statement is used for conditional execution?*
A. for
B. if
C. loop
D. while
✅ Answer: B
- What type must an if condition return?
A. i32
B. bool
C. String
D. usize
✅ Answer: B
- Which loop runs infinitely by default?
A. for
B. while
C. loop
D. if
✅ Answer: C
- Which loop is best for iterating arrays?
A. while
B. for
C. loop
D. if
✅ Answer: B
MCQs – Control Flow Expressions
- Which control flow construct can return a value?
A. for
B. if
C. while
D. continue
✅ Answer: B
- Which keyword exits a loop?
A. continue
B. break
C. return
D. stop
✅ Answer: B
- How does a loop return a value?
A. return
B. continue
C. break value;
D. yield
✅ Answer: C
MCQs – Advanced
- What happens if array index goes out of bounds?
A. Undefined behavior
B. Runtime panic
C. Compile error
D. Memory corruption
✅ Answer: B
- What feature helps exit nested loops?
A. continue
B. return
C. Loop labels
D. match
✅ Answer: C
- Which is NOT a Rust loop?
A. for
B. while
C. do-while
D. loop
✅ Answer: C
- Which is true about for loops?
A. They can cause buffer overflow
B. They are bounds-checked
C. They are infinite
D. They require manual indexing
✅ Answer: B
- Which keyword skips an iteration?
A. break
B. continue
C. pass
D. skip
✅ Answer: B
- Rust treats control flow constructs as:
A. Statements only
B. Expressions
C. Macros
D. Functions
✅ Answer: B
- Which loop is used when iteration count is unknown?
A. for
B. while
C. loop
D. if
✅ Answer: C
- Which prevents unsafe memory access?
A. No semicolon
B. Bounds checking
C. Loop labels
D. continue
✅ Answer: B
Sum numbers until a sentinel (break with value)
Problem: Read numbers from a list until you hit -1. Return the sum (excluding -1).
Tricky: Using loop + break value.
fn sum_until_sentinel(nums: &[i32]) -> i32 {
let mut i = 0;
let mut sum = 0;
loop {
if i >= nums.len() {
break sum; // reached end
}
let x = nums[i];
if x == -1 {
break sum; // sentinel found
}
sum += x;
i += 1;
}
}
fn main() {
let nums = [5, 10, 20, -1, 999];
println!("{}", sum_until_sentinel(&nums)); // 35
}
2) Find first pair that sums to target (nested loops + labeled break)
Problem: Return the first pair (a, b) such that a + b == target.
Tricky: Exiting both loops cleanly.
fn first_pair_sum(nums: &[i32], target: i32) -> Option<(i32, i32)> {
let mut result = None;
'outer: for i in 0..nums.len() {
for j in i + 1..nums.len() {
if nums[i] + nums[j] == target {
result = Some((nums[i], nums[j]));
break 'outer;
}
}
}
result
}
fn main() {
let nums = [2, 7, 11, 15];
println!("{:?}", first_pair_sum(&nums, 9)); // Some((2, 7))
}
3) Reverse array in-place (two pointers)
Problem: Reverse a mutable slice in-place.
Tricky: Correct indices + termination.
fn reverse_in_place(a: &mut [i32]) {
if a.is_empty() { return; }
let mut left = 0usize;
let mut right = a.len() - 1;
while left < right {
a.swap(left, right);
left += 1;
// prevent underflow
if right == 0 { break; }
right -= 1;
}
}
fn main() {
let mut a = [1, 2, 3, 4, 5];
reverse_in_place(&mut a);
println!("{:?}", a); // [5,4,3,2,1]
}
4) Run-length encoding (careful loop end handling)
Problem: Compress "aaabbc" → "a3b2c1".
Tricky: Final run must be flushed after the loop.
fn rle(s: &str) -> String {
let mut it = s.chars();
let Some(mut prev) = it.next() else { return String::new(); };
let mut count = 1usize;
let mut out = String::new();
for ch in it {
if ch == prev {
count += 1;
} else {
out.push(prev);
out.push_str(&count.to_string());
prev = ch;
count = 1;
}
}
// flush last run
out.push(prev);
out.push_str(&count.to_string());
out
}
fn main() {
println!("{}", rle("aaabbc")); // a3b2c1
}
5) Rotate slice right by k (k can be > n)
Problem: [1,2,3,4,5] rotated right by 2 → [4,5,1,2,3].
Tricky: Normalize k and do it efficiently.
fn rotate_right(a: &mut [i32], k: usize) {
let n = a.len();
if n == 0 { return; }
let k = k % n;
if k == 0 { return; }
// reverse helper
fn rev(x: &mut [i32]) {
let mut i = 0usize;
let mut j = x.len().saturating_sub(1);
while i < j {
x.swap(i, j);
i += 1;
j = j.saturating_sub(1);
}
}
rev(a);
rev(&mut a[..k]);
rev(&mut a[k..]);
}
fn main() {
let mut a = [1,2,3,4,5];
rotate_right(&mut a, 2);
println!("{:?}", a); // [4, 5, 1, 2, 3]
}
6) Compute prefix sums (off-by-one)
Problem: [2, 4, 1, 3] → [2, 6, 7, 10]
Tricky: Carry running sum correctly.
fn prefix_sums(nums: &[i32]) -> Vec<i32> {
let mut out = Vec::with_capacity(nums.len());
let mut sum = 0;
for &x in nums {
sum += x;
out.push(sum);
}
out
}
fn main() {
println!("{:?}", prefix_sums(&[2,4,1,3])); // [2,6,7,10]
}
7) Find majority element (Boyer–Moore loop trick)
Problem: Element appearing more than n/2 times.
Tricky: Candidate selection loop.
fn majority_element(nums: &[i32]) -> Option<i32> {
if nums.is_empty() { return None; }
let mut candidate = 0;
let mut count = 0;
for &x in nums {
if count == 0 {
candidate = x;
count = 1;
} else if x == candidate {
count += 1;
} else {
count -= 1;
}
}
// verify
let freq = nums.iter().filter(|&&x| x == candidate).count();
if freq > nums.len() / 2 { Some(candidate) } else { None }
}
fn main() {
println!("{:?}", majority_element(&[2,2,1,2,3,2,2])); // Some(2)
}
8) Detect cycle in a sequence (Floyd’s tortoise-hare)
Problem: Given f(x), determine if sequence repeats (cycle).
Tricky: Two-speed loop logic.
fn has_cycle(mut x: u64, f: fn(u64) -> u64, max_steps: usize) -> bool {
let mut tortoise = x;
let mut hare = x;
for _ in 0..max_steps {
tortoise = f(tortoise);
hare = f(f(hare));
if tortoise == hare {
return true;
}
}
false
}
fn f(x: u64) -> u64 { (x * 2 + 1) % 7 }
fn main() {
println!("{}", has_cycle(1, f, 100)); // true (mod creates cycle)
}
9) Remove duplicates from a sorted vector in-place (two pointers)
Problem: [1,1,2,2,2,3] → 1,2,3.
Tricky: Write index and truncate.
fn dedup_sorted(v: &mut Vec<i32>) {
if v.is_empty() { return; }
let mut write = 1usize;
for read in 1..v.len() {
if v[read] != v[write - 1] {
v[write] = v[read];
write += 1;
}
}
v.truncate(write);
}
fn main() {
let mut v = vec![1,1,2,2,2,3];
dedup_sorted(&mut v);
println!("{:?}", v); // [1,2,3]
}
10) Print a pattern without nested loops? (single loop trick)
Problem: Print a right triangle of * of height n using only one loop.
Tricky: Track row/col manually.
fn triangle(n: usize) {
let mut row = 1usize;
let mut col = 0usize;
while row <= n {
if col < row {
print!("*");
col += 1;
} else {
println!();
row += 1;
col = 0;
}
}
}
fn main() {
triangle(4);
}
Output:
*
**
***
****
Top comments (0)