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
- Declare
starswithlet mut, starting at 1: the first line has a single star. - Declare
spaceswithlet mut, starting atn / 2. Dividing two integers already drops the remainder, so forn = 5it is 2. - Loop over the lines with
for row in 0..n. The range excludesn, so the body runs exactlyntimes. - 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 withprintln!. - Update the counters for the next line. If
row < n / 2the rhombus is still widening: add 2 tostarsand subtract 1 fromspaces. Otherwise it narrows: subtract 2 fromstarsand add 1 tospaces. - Write
build_stringaboveprint_rhombus, takingn: i32andcharacter: charand returning aString. - Fill it: create an empty mutable
Stringnamedresult, pushcharacteronto itntimes 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 theStringon its left by value, which moves it into the operation and reuses its memory. The right side must be a&str. A&Stringis accepted there because Rust converts it to&strautomatically. Writinga + bwith twoStrings is a compile error: it expected&strand foundString. - The left string is consumed. After
let c = a + &b;, the variableahas been moved and can't be used again, whilebis still usable. Here the left operands are temporary strings returned bybuild_string, so nothing is lost. - Inferred types.
starsandspaceshave no annotation, but they are passed to a parameter of typei32, so Rust infersi32for them. charliterals.' 'and'*'are single characters in single quotes.pushtakes achar, which is why the helper's parameter has that type.- Explicit
return.return result;is valid anywhere in a function. Writing justresultas the last line would do the same. - Calling a sibling function.
build_stringtakes noself, so it is called asSelf::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.starsis 1 andspacesis 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 oddn. 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 / 2widens 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 = 1prints*.n = 3prints*,***,*.
