» Quick Introduction to Rust » 1. Basics » 1.8 Control Flow

Control Flow

The ability to run some code depending on whether a condition is true and to run some code repeatedly while a condition is true are basic building blocks in most programming languages.

If Else

fn main() {
    let a = 58;
    if a < 0 {
        print!("{} is negative", a); 
    } else if a > 0 {
        print!("{} is positive", a);
    } else {
        print!("{} is zero", a);
    }
}

Loop

Rust provides a loop keyword to indicate an infinite loop. Similar to other C-like languages, the break statement can be used to exit a loop at anytime, whereas the continue statement can be used to skip the rest of the iteration and start a new one.

fn main() {
    let mut count = 0;
    loop {
        count += 1;
        if count == 7 {
            println!("seven");
            continue;
        }
        println!("{}", count);
        if count == 10 {
            println!("Let's end here!");
            break;
        }
    }
}

Loop Labels

You may annotate the loops with some 'label. In that case, you can break or continue outer loops when the label is passed to the statement.

fn main() {
    'outer: loop {
        println!("The outer loop begins");
        'inner: loop {
            println!("The inner loop begins");
            break 'outer;
        }
        println!("Unreachable line here");
    }
    println!("The outer loop ends");
}

Return from the loop

You can put the return value after the break, and it will be returned by the loop expression.

fn main() {
    let mut counter = 0;
    let result = loop {
        counter += 1;
        if counter == 10 {
            break counter * 2;
        }
    };
    println!("counter: {} result: {}", counter, result);
    // counter: 10 result: 20
}

While

fn main() {
    let mut n = 1;
    while n < 10 {
        if n % 2 == 0 {
            println!("{}", n);
        }
        n += 1;
    }
}

For

The a..b notation can create a range in Rust. The for ... in construct can be used to iterate through a range, or more generally, an Iterator.

fn main() {
    for n in 1..5 {
        print!("{} ", n); // 1 2 3 4 
    }
}

If you want to include the end element of the range, use a..=b notation.

fn main() {
    for n in 1..=5 {
        print!("{} ", n); // 1 2 3 4 5
    }
}

iter, iter_mut and into_iter all convert a collection into an iterator in different ways.

fn main() {
    let names = vec!["Alice", "Bob", "Cindy"];
    for name in names.iter() {
        print!("{} ", name)
    }
    println!("\nnames: {:?}", names);
    // Alice Bob Cindy 
    // names: ["Alice", "Bob", "Cindy"]
}

Match

The pattern matching can be used like a C switch, but it can do even more.

fn main() {
    let number = 7;
    match number {
        1 => println!("One"),
        2 | 3 | 5 | 7 | 11 => println!("A prime"),
        13..=19 => println!("A teen"),
        _ => println!("Others"),
    }
}

Destructuring

A match block can destructure items(tuples, arrays, enums, structs and etc) in a variety of ways.

fn main() {
    let triple = (5, -8, 2);
    match triple {
        (0, y, z) => println!("First is `0`, `y` is {:?}, and `z` is {:?}", y, z),
        (1, ..)  => println!("First is `1` and the rest doesn't matter"),
        (.., 2)  => println!("last is `2` and the rest doesn't matter"),
        _      => println!("It doesn't matter what they are"),
    }
    // last is `2` and the rest doesn't matter
}
enum Color {
    Red,
    RGB(u16, u16, u16),
    CMYK(u32, u32, u32, u32),
}

fn main() {
    let color = Color::RGB(58, 23, 78);
    match color {
        Color::Red   => println!("The color is Red!"),
        Color::RGB(r, g, b) =>
            println!(
                "Red: {}, green: {}, and blue: {}", r, g, b),
        Color::CMYK(c, m, y, k) =>
            println!(
                "Cyan: {}, magenta: {}, yellow: {}, key (black): {}!",
                c, m, y, k),
    }
    // Red: 58, green: 23, and blue: 78
}

Guard

A guard can be added to filter the match arm.

fn main() {
    let a: u8 = 58;

    match a {
        i if i == 0 => println!("Zero"),
        i if i > 0 => println!("Greater than zero"),
        _ => unreachable!("Should never happen."),
    }
    // Greater than zero
}

Binding

match provides the @ sigil to bind values to names, so you can use the matched item as a variable inside the arm.

fn main() {
    let number = 11;
    match number {
        1 => println!("One"),
        n @ (2 | 3 | 5 | 7 | 11) => println!("A prime: {}", n),
        13..=19 => println!("A teen"),
        _ => println!("Others"),
    }
    // A prime: 11
}

If Let

For some use cases, when matching enums, match is kind of cumbersome.

let a = Some(58);
match a {
    Some(b) => {
        println!("My precious value is {}", b);
    },
    _ => {},
}

if let comes to the save for this use case.

if let Some(b) = a {
    println!("My value is {}", b);
}

While Let

while let can handle similar use cases.

fn main() {
    let mut optional = Some(0);
    while let Some(i) = optional {
        if i > 9 {
            println!("Hit 9, quit!");
            optional = None;
        } else {
            println!("It's {}, try again.", i);
            optional = Some(i + 1);
        }
    }
}
// It's 0, try again.
// It's 1, try again.
// It's 2, try again.
// It's 3, try again.
// It's 4, try again.
// It's 5, try again.
// It's 6, try again.
// It's 7, try again.
// It's 8, try again.
// It's 9, try again.
// Hit 9, quit!

Code Challenge

Try to modify the code provided in the editor to make it print Greeting from John: John.

Loading...
> code result goes here
Prev
Next