12 Days of Christmas in Rust I've recently been trying to learn more rust and I have just finished chapter 3 of the Rust book, which has a list of projects to do to practice.

I have tried to write a program that prints the 12 Days of Christmas and I would like to know if it is idiomatic or if any major improvementsc could be made.

Thanks in advance.

const LYRICS: [&amp;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: i32) -&gt; String {
    let ordinal = match day {
        1 =&gt; "1st".to_string(),
        2 =&gt; "2nd".to_string(),
        3 =&gt; "3rd".to_string(),
        _ =&gt; day.to_string() + "th",
    };

    let mut start = format!(
        "On the {} day of Christmas, my true love gave to me:\n",
        ordinal
    );

    for i in (0..day).rev() {
        start.push_str(&amp;LYRICS[11 - (i as usize)]);

        if i != 0 {
            start.push_str("\n");
        }
    }

    start
}

fn main() {
    for i in 1..13 {
        println!("{}\n", gen_lyrics(i))
    }
}
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) -&gt; String


If you use usize specifically, you also avoid the cast to usize here:

start.push_str(&amp;LYRICS[11 - (i as usize)]);


That would then just be:

start.push_str(&amp;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 =&gt; "st",
    2 =&gt; "nd",
    3 =&gt; "rd",
    _ =&gt; "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 (&amp;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 (&amp;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: [&amp;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) -&gt; String {
    let ordinal_suffix = match day {
        1 =&gt; "st",
        2 =&gt; "nd",
        3 =&gt; "rd",
        _ =&gt; "th",
    };

    let mut start = format!(
        "On the {}{} day of Christmas, my true love gave to me:",
        day,
        ordinal_suffix
    );

    for line in (&amp;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))
    }
}