Start learning free

LESSON 193 OF 414 · NESTED LOOPS

Print Rhombus in Rust: Widening and Narrowing Rows

Practice O(n2) Read the full explanation

Given an odd positive integer n print a rhombus of stars as in the example below

Example:

Input: n = 5
Output:
  *  
 *** 
*****
 *** 
  *  

Explanation:
There are n = 5 lines. Each line contains exactly n = 5 characters.
1st line contains two spaces, one '*' and two spaces.
2nd line contains one space, three '*' and one space.
3rd line contains five stars.
4th line contains one space, three '*' and one space.
5th line contains two spaces, one '*' and two spaces.
Note: Be careful to add the necessary spaces at the end of every line!


STEP 1 OF 13
    Ctrl + Enter to check

    RUST · NESTED LOOPS

    Print Rhombus in Rust, explained

    Problem overview

    The function print_rhombus receives an odd positive integer n as an i32 and prints a rhombus of stars on n lines. Every line is exactly n characters wide, so the spaces at the end of a line count as much as the ones at the start.

    For n = 5 the output is the following, where each line has five characters, spaces included:

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

    The first line has two spaces, one star and two spaces. The middle line is five stars. After the middle, the shape narrows again in the same steps.

    Approach: track stars and spaces

    Each line has the same shape: some spaces, some stars, the same number of spaces. Two numbers describe a line completely: how many stars it has, and how many spaces sit on each side. They always add up to n, because stars + 2 * spaces = n.

    Going from one line to the next changes both numbers by a fixed amount. In the top half, each line gains one star on each side, so stars grows by 2 and spaces shrinks by 1. In the bottom half it is the reverse. The first line has one star and n / 2 spaces on each side, since for odd n the middle column has (n - 1) / 2 columns on its left, and integer division gives that value.

    The program therefore needs only a loop over the lines, a way to build a string of a repeated character, and an update of the two counters after each line.

    Step by step

    1. Declare stars with let mut, starting at 1: the first line has a single star.
    2. Declare spaces with let mut, starting at n / 2. Dividing two integers already drops the remainder, so for n = 5 it is 2.
    3. Loop over the lines with for row in 0..n. The range excludes n, so the body runs exactly n times.
    4. Build the line from the left spaces, the stars and the right spaces, using a helper build_string(n, character) that the lesson writes later. The three pieces are joined with +, and the line is printed with println!.
    5. Update the counters for the next line. If row < n / 2 the rhombus is still widening: add 2 to stars and subtract 1 from spaces. Otherwise it narrows: subtract 2 from stars and add 1 to spaces.
    6. Write build_string above print_rhombus, taking n: i32 and character: char and returning a String.
    7. Fill it: create an empty mutable String named result, push character onto it n times in a loop, and return it. The loop variable is named _ because it only counts.

    The condition row < n / 2 is true for rows 0 and 1 when n = 5. The update after row 1 prepares row 2, the widest one. From row 2 on, the condition is false, so the updates shrink the rhombus for rows 3 and 4.

    The solution in Rust

    impl Solution {
        fn build_string(n: i32, character: char) -> String {
            let mut result = String::new();
            for _ in 0..n {
                result.push(character);
            }
            return result;
        }
    
        fn print_rhombus(n: i32) {
            let mut stars = 1;
            let mut spaces = n / 2;
            for row in 0..n {
                let line = Self::build_string(spaces, ' ')
                    + &Self::build_string(stars, '*')
                    + &Self::build_string(spaces, ' ');
                println!("{}", line);
                if row < n / 2 {
                    stars += 2;
                    spaces -= 1;
                } else {
                    stars -= 2;
                    spaces += 1;
                }
            }
        }
    }
    

    The Rust details worth a closer look:

    • Why the & after the first operand. The + operator on strings takes the String on its left by value, which moves it into the operation and reuses its memory. The right side must be a &str. A &String is accepted there because Rust converts it to &str automatically. Writing a + b with two Strings is a compile error: it expected &str and found String.
    • The left string is consumed. After let c = a + &b;, the variable a has been moved and can't be used again, while b is still usable. Here the left operands are temporary strings returned by build_string, so nothing is lost.
    • Inferred types. stars and spaces have no annotation, but they are passed to a parameter of type i32, so Rust infers i32 for them.
    • char literals. ' ' and '*' are single characters in single quotes. push takes a char, which is why the helper's parameter has that type.
    • Explicit return. return result; is valid anywhere in a function. Writing just result as the last line would do the same.
    • Calling a sibling function. build_string takes no self, so it is called as Self::build_string.

    Complexity

    Time: O(n²). The program prints n lines of n characters each, so the output alone has n² characters, and building each line costs time proportional to its width.

    Space: O(n). At any moment the program holds a few strings of at most n characters. The lines are printed as they are built and not stored.

    Edge cases and common mistakes

    • Trailing spaces. A line like * must keep its two spaces on the right. A solution that prints only the left spaces and the stars produces lines of different widths.
    • n = 1. stars is 1 and spaces is 0, so the program prints a single *. The counters are also updated after that last line, to values that are never used.
    • n = 3. The output is * , *** and * .
    • Even n. The problem guarantees an odd n. With an even value such as 4 the program still prints 4 lines, but they are not a rhombus.
    • Wrong switch point. Using row <= n / 2 widens one line too long, so the bottom half is off by one row.
    • Updating before printing. The first line would already have 3 stars.
    • Forgetting &. Self::build_string(...) + Self::build_string(...) doesn't compile, as explained above.

    Testing

    For n = 7 the output is the following, with a trailing space after the stars on the four shorter lines:

       *   
      ***  
     ***** 
    *******
     ***** 
      ***  
       *   
    
    • n = 1 prints *.
    • n = 3 prints * , ***, * .
    Keep going with RustAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free