This looks pretty good! I'm still fairly new to Rust myself, so I'm not sure how familiar I am with the idioms beyond those outlined in the book. That said, there's a few improvements I can suggest.
Unsigned integer for day
Since day
is never negative, you can use an unsigned integer instead of an i32
here:
fn gen_lyrics(day: usize) -> String
If you use usize
specifically, you also avoid the cast to usize
here:
start.push_str(&LYRICS[11 - (i as usize)]);
That would then just be:
start.push_str(&LYRICS[11 - i]);
Ordinal suffix match
expression
You can avoid some .to_string()
calls and repetition of numbers by making the match expression return just the suffix of the ordinal. You can then append that suffix to the number itself in the format!()
call:
let ordinal_suffix = match day {
1 => "st",
2 => "nd",
3 => "rd",
_ => "th",
};
let mut start = format!(
"On the {}{} day of Christmas, my true love gave to me:\n",
day,
ordinal_suffix
);
Inclusive range
You can use ..=
instead of ..
to create an inclusive range:
fn main() {
for i in 1..=12 {
println!("{}\n", gen_lyrics(i))
}
}
Iterators and the skip method
You can iterate directly over the LYRICS
array, skipping over the number of lines you don't want to include:
for line in (&LYRICS).iter().skip(12 - day) {
start.push_str(line);
start.push_str("\n");
}
The problem with this is that there's no longer an i
variable to check whether an iteration is the last line or not, so it can't conditionally insert a newline. The code above will always insert a newline.
But we can cheat a little 😉
If you remove the newline from the end of the start
variable:
"On the {} day of Christmas, my true love gave to me:"
You can push the newline first, that way you no longer need a conditional in the loop:
for line in (&LYRICS).iter().skip(12 - day) {
start.push_str("\n");
start.push_str(line);
}
Bringing it all together
This is the code with all the changes applied:
const LYRICS: [&str; 12] = [
"Twelve drummers drumming",
"Eleven pipers piping",
"Ten lords a-leaping",
"Nine ladies dancing",
"Eight maids a-milking",
"Seven swans a-swimming",
"Six geese a-laying",
"Five golden rings",
"Four calling birds",
"Three french hens",
"Two turtle doves, and",
"A partridge in a pear tree",
];
fn gen_lyrics(day: usize) -> String {
let ordinal_suffix = match day {
1 => "st",
2 => "nd",
3 => "rd",
_ => "th",
};
let mut start = format!(
"On the {}{} day of Christmas, my true love gave to me:",
day,
ordinal_suffix
);
for line in (&LYRICS).iter().skip(12 - day) {
start.push_str("\n");
start.push_str(line);
}
start
}
fn main() {
for i in 1..=12 {
println!("{}\n", gen_lyrics(i))
}
}