Start learning free

LESSON 191 OF 414 · NESTED LOOPS

Nested Loops in Rust: for Loops Inside for Loops

Lesson Read the full explanation

A nested loop has one loop inside of another. These are typically used for working with two dimensions such as printing stars in rows and columns as shown below. When a loop is nested inside another loop, the inner loop runs many times inside the outer loop. In each iteration of the outer loop, the inner loop will be re-started. The inner loop must finish all of its iterations before the outer loop can continue to its next iteration.

Example:

for row in 0..3 {
	for col in 0..5 {
		print!("*");
	}
	println!();
}

// This code prints a rectangle with 3 rows and 5 columns, filled with '*'

Iterating through multidimensional arrays

Nested loops are also helpful when we want to iterate through a multi dimensional array, for example:

let arr = vec![
	vec![1, 2],
	vec![3, 4],
	vec![5, 6],
];

for i in 0..3 {
	for j in 0..2 {
		println!("{}", arr[i][j]);
	}
}

// This outputs each sub-element in arr one at a time

Breaking nested loops

In a nested loop, a break statement only stops the loop it is placed in. Therefore, if a break is placed in the inner loop, the outer loop still continues. For example:

let arr = vec![
	vec![1, 2],
	vec![3, 4],
	vec![5, 6],
];

for i in 0..3 {
	for j in 0..2 {
		if arr[i][j] == 3 {
			break;
		}
		println!("{}", arr[i][j]);
	}
}

// prints the numbers 1, 2, 5 and 6 on different lines

When i is 1 and j is 0, we execute the break. This means we stop the inner loop and go back to the outer loop to continue from the next iteration, which is i = 2. And as you can see, we print all the elements of row 2.

However, if the break is placed in the outer loop, all of the looping stops. For example:

let arr = vec![
	vec![1, 2],
	vec![3, 4],
	vec![5, 6],
];

for i in 0..3 {
	if arr[i][0] == 3 {
		break;
	}
	for j in 0..2 {
		println!("{}", arr[i][j]);
	}
}

// prints the numbers 1 and 2 on different lines

Assignment
Follow the Coding Tutorial and let's write some nested loops.


Hint
Look at the examples above if you get stuck.


STEP 1 OF 4
    Ctrl + Enter to check

    RUST · NESTED LOOPS

    Nested Loops in Rust, explained

    What a nested loop is

    A nested loop is a loop inside another loop. For every pass of the outer loop, the inner loop starts from the beginning and runs all of its passes before the outer loop moves on. If the outer loop runs 5 times and the inner loop runs 10 times per pass, the inner body runs 5 × 10 = 50 times.

    Nested loops are the natural tool for two-dimensional data: rows and columns of a grid, a vector of vectors, or all pairs of values from a range. They are also the basis of brute-force solutions, which is where this chapter is heading.

    The lesson's exercise: a rectangle of stars

    The task is to print 5 rows of 10 stars. The structure mirrors the picture: the outer loop is over rows, the inner loop over the columns of one row.

    1. Write the outer loop for row in 0..5, one pass per row. The range 0..5 includes 0 and excludes 5, so it gives exactly five values.
    2. Write the inner loop inside it, for col in 0..10, one pass per column.
    3. Print one star in the inner loop with print!("*"). The print! macro writes its text without a newline, so the stars of a row stay on the same line.
    4. End the row with println!() after the inner loop but still inside the outer one. A bare println!() prints just a newline, so the next row starts on a fresh line.

    The solution in Rust

    for row in 0..5 {
        for col in 0..10 {
            print!("*");
        }
        println!();
    }
    

    The output is:

    **********
    **********
    **********
    **********
    **********
    

    Compiled as written, the loop variables row and col are never used, so rustc prints an unused variable warning for each. A warning does not stop the program. Naming them _row and _col, or just _, silences it.

    Placement of println!() matters. Inside the inner loop it would put every star on its own line. After the outer loop it would print a single long line of 50 stars and one newline at the end.

    Going through a vector of vectors

    With arr as a Vec of three Vecs of two numbers each, two loops visit every element:

    let arr = vec![
        vec![1, 2],
        vec![3, 4],
        vec![5, 6],
    ];
    
    for i in 0..3 {
        for j in 0..2 {
            println!("{}", arr[i][j]);
        }
    }
    

    This prints 1 to 6 on separate lines. The indices i and j are of type usize, which Rust requires for indexing. Going past the end panics instead of reading garbage: if the outer range were 0..4, the program would stop at the first access to arr[3] with index out of bounds: the len is 3 but the index is 3.

    When you don't need the indices, iterate over the values directly, which avoids out-of-range mistakes:

    for row in &arr {
        for x in row {
            print!("{} ", x);
        }
    }
    

    This prints 1 2 3 4 5 6 .

    break and continue in nested loops

    A break leaves only the innermost loop that contains it. With break in the inner loop, the outer loop carries on:

    for i in 0..3 {
        for j in 0..2 {
            if arr[i][j] == 3 {
                break;
            }
            print!("{} ", arr[i][j]);
        }
    }
    

    This prints 1 2 5 6 . When i is 1 and j is 0, the value 3 stops the inner loop for that row, so the 4 is skipped, but the outer loop continues with row 2.

    A break placed directly in the outer loop ends everything. To stop both loops from inside the inner one, Rust lets you label the outer loop and name the label:

    'outer: for i in 0..3 {
        for j in 0..2 {
            if arr[i][j] == 3 {
                break 'outer;
            }
            print!("{} ", arr[i][j]);
        }
    }
    

    This prints 1 2 and stops. A labelled continue 'outer works the same way for skipping to the outer loop's next pass. A plain continue skips the rest of the current pass of the innermost loop only.

    Ranges for the inner loop

    The inner range can depend on the outer variable. This visits each pair once, with the second value never smaller than the first:

    for i in 1..=3 {
        for j in i..=3 {
            print!("({},{}) ", i, j);
        }
    }
    

    It prints (1,1) (1,2) (1,3) (2,2) (2,3) (3,3) . The syntax a..=b includes b, while a..b does not.

    Common mistakes

    • Off-by-one ranges. 0..5 has five values, 0 to 4. Use 0..=5 only when you want six.
    • Using break to leave both loops. It leaves one. Use a label, or move the loops into a function and return.
    • Reusing the same variable name in both loops. The inner i would shadow the outer one, which is legal but confusing.
    • Counting the cost. Two nested loops over n values run n * n times. Ten thousand values means a hundred million passes.

    Key takeaways

    • The inner loop restarts and finishes completely on every pass of the outer loop.
    • print! keeps the output on the line, and println!() ends it.
    • break affects the innermost loop; break 'label reaches an outer one.
    • Indexing uses usize and panics when out of bounds.
    Keep going with RustAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free