Debug School

rakesh kumar
rakesh kumar

Posted on

Control Flow & Loops in Rust

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
Enter fullscreen mode Exit fullscreen mode

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");
    }
}

Enter fullscreen mode Exit fullscreen mode

Key Rules

Condition must be bool

No parentheses required

Curly braces {} are mandatory
Enter fullscreen mode Exit fullscreen mode

Invalid in Rust:

if number { }  // error: expected bool
Enter fullscreen mode Exit fullscreen mode

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);
}

Enter fullscreen mode Exit fullscreen mode

Rules

Both branches must return the same type

No semicolon at the end of returned expressions
Enter fullscreen mode Exit fullscreen mode

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;
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

When to Use

Unknown number of iterations

Event listeners

Retry logic
Enter fullscreen mode Exit fullscreen mode

loop with Return Value

fn main() {
    let mut x = 0;

    let result = loop {
        x += 1;
        if x == 5 {
            break x * 2;
        }
    };

    println!("Result: {}", result);
}
Enter fullscreen mode Exit fullscreen mode

✔ 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!");
}
Enter fullscreen mode Exit fullscreen mode

Best Use Case

When condition is known

Simple countdowns

Conditional repetition
Enter fullscreen mode Exit fullscreen mode

5️⃣

for Loop (Most Common & Safest)

Used to iterate over ranges or collections.

Example: Range

fn main() {
    for i in 1..5 {
        println!("{}", i);
    }
}
Enter fullscreen mode Exit fullscreen mode

➡️ Prints 1 2 3 4

Inclusive Range

for i in 1..=5 {
    println!("{}", i);
}
Enter fullscreen mode Exit fullscreen mode

➡️ Prints 1 2 3 4 5

Iterating Over an Array

fn main() {
    let numbers = [10, 20, 30];

    for num in numbers {
        println!("{}", num);
    }
}
Enter fullscreen mode Exit fullscreen mode
✔ Prevents out-of-bounds errors
✔ Preferred over while for collections
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

7️⃣

Nested Loops

fn main() {
    for i in 1..=3 {
        for j in 1..=2 {
            println!("i={}, j={}", i, j);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

✔ 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.

  1. Which control flow statements are available in Rust?

Answer:
Rust provides:

if / else

loop

while

for
Enter fullscreen mode Exit fullscreen mode
  1. Does Rust allow conditions without parentheses?

Answer:
Yes. Rust does not require parentheses around conditions.

if x > 5 {
    println!("Greater");
}
Enter fullscreen mode Exit fullscreen mode
  1. 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" };
Enter fullscreen mode Exit fullscreen mode
  1. What restriction applies when using if as an expression?

Answer:
Both branches must return the same data type.

Intermediate Level

  1. What is the difference between loop and while?

Answer:

loop runs infinitely until break

while runs while a condition is true

  1. Can loop return a value?

Answer:
Yes, using break value;.

let result = loop {
    break 10;
};
Enter fullscreen mode Exit fullscreen mode
  1. Which loop is safest for iterating collections?

Answer:
The for loop, because it prevents out-of-bounds access.

  1. What does the continue keyword do?

Answer:
It skips the current iteration and moves to the next one.

  1. What does the break keyword do?

Answer:
It immediately exits the loop.

Advanced Level

  1. What are loop labels in Rust?

Answer:
Loop labels allow control of nested loops, enabling breaking or continuing outer loops.

'outer: loop {
    break 'outer;
}
Enter fullscreen mode Exit fullscreen mode
  1. How does Rust ensure safety in loops?

Answer:
Through:

Mandatory boolean conditions

Bounds checking

Compile-time ownership rules

  1. Can while be used to iterate over arrays?

Answer:
Yes, but it is not recommended due to possible index errors.

  1. Why is for preferred over while?

Answer:

Because for loops:

Are safer

More readable

Automatically handle bounds
Enter fullscreen mode Exit fullscreen mode
  1. Can control flow constructs return values?

Answer:
Yes. Rust treats if and loop as expressions.

Tricky Questions

  1. Is this code valid?
let x = if true { 5 } else { 6 };
Enter fullscreen mode Exit fullscreen mode

Answer:
Yes. Both branches return the same type.

  1. Is this valid?
let x = if true { 5 } else { "six" };
Enter fullscreen mode Exit fullscreen mode

Answer:
No. Return types must match.

  1. Can break be used outside loops?

Answer:
No. It causes a compile-time error.

  1. What happens if a loop never breaks?

Answer:
The program runs indefinitely.

  1. 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

  1. W*hich statement is used for conditional execution?*
A. for
B. if
C. loop
D. while
Enter fullscreen mode Exit fullscreen mode

✅ Answer: B

  1. What type must an if condition return?
A. i32
B. bool
C. String
D. usize
Enter fullscreen mode Exit fullscreen mode

✅ Answer: B

  1. Which loop runs infinitely by default?

A. for
B. while
C. loop
D. if
Enter fullscreen mode Exit fullscreen mode

✅ Answer: C

  1. Which loop is best for iterating arrays?
A. while
B. for
C. loop
D. if
Enter fullscreen mode Exit fullscreen mode

✅ Answer: B

MCQs – Control Flow Expressions

  1. Which control flow construct can return a value?
A. for
B. if
C. while
D. continue
Enter fullscreen mode Exit fullscreen mode

✅ Answer: B

  1. Which keyword exits a loop?
A. continue
B. break
C. return
D. stop
Enter fullscreen mode Exit fullscreen mode

✅ Answer: B

  1. How does a loop return a value?
A. return
B. continue
C. break value;
D. yield
Enter fullscreen mode Exit fullscreen mode

✅ Answer: C

MCQs – Advanced

  1. What happens if array index goes out of bounds?
A. Undefined behavior
B. Runtime panic
C. Compile error
D. Memory corruption
Enter fullscreen mode Exit fullscreen mode

✅ Answer: B

  1. What feature helps exit nested loops?
A. continue
B. return
C. Loop labels
D. match

Enter fullscreen mode Exit fullscreen mode

✅ Answer: C

  1. Which is NOT a Rust loop?
A. for
B. while
C. do-while
D. loop
Enter fullscreen mode Exit fullscreen mode

✅ Answer: C

  1. 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
Enter fullscreen mode Exit fullscreen mode

✅ Answer: B

  1. Which keyword skips an iteration?
A. break
B. continue
C. pass
D. skip
Enter fullscreen mode Exit fullscreen mode

✅ Answer: B

  1. Rust treats control flow constructs as:
A. Statements only
B. Expressions
C. Macros
D. Functions
Enter fullscreen mode Exit fullscreen mode

✅ Answer: B

  1. Which loop is used when iteration count is unknown?
A. for
B. while
C. loop
D. if
Enter fullscreen mode Exit fullscreen mode

✅ Answer: C

  1. Which prevents unsafe memory access?
A. No semicolon
B. Bounds checking
C. Loop labels
D. continue
Enter fullscreen mode Exit fullscreen mode

✅ 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
}
Enter fullscreen mode Exit fullscreen mode

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))
}
Enter fullscreen mode Exit fullscreen mode

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]
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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]
}
Enter fullscreen mode Exit fullscreen mode

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]
}
Enter fullscreen mode Exit fullscreen mode

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)
}
Enter fullscreen mode Exit fullscreen mode

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)
}
Enter fullscreen mode Exit fullscreen mode

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]
}
Enter fullscreen mode Exit fullscreen mode

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);
}

Enter fullscreen mode Exit fullscreen mode

Output:


*
**
***
****
Enter fullscreen mode Exit fullscreen mode

Top comments (0)