Pig Latin translator with too many `push_str`s
Write tests

Unfortunately, your piglatify doesn't follow your rules. apple gets transformed into applyaay, but it should get transformed into apple-hay. Also, first should get transformed into irst-fay, but you will transform it into irstfay.

Rust makes it really easy to add tests to your code:

#[cfg(test)]
mod test {
    use transform_word;

    #[test]
    fn empty_string() {
        assert_eq!(transform_word(""), "");
    }

    #[test]
    fn apple() {
        assert_eq!(transform_word("apple"), "apple-hay");
    }

    #[test]
    fn first() {
        assert_eq!(transform_word("first"), "irst-fay");
    }
}


While this only uses the given strings from the exercise, it already tells us that there is something amiss if we run the tests with cargo test:


running 3 tests
test test::empty_string ... FAILED
test test::first ... FAILED
test test::apple ... FAILED

failures:

---- test::empty_string stdout ----
    thread 'test::empty_string' panicked at 'byte index 1 is out of bounds of ``', /checkout/src/libcore/str/mod.rs:2217:9
note: Run with `RUST_BACKTRACE=1` for a backtrace.

---- test::first stdout ----
    thread 'test::first' panicked at 'assertion failed: `(left == right)`
  left: `"irstfay"`,
 right: `"irst-fay"`', src/main.rs:57:9

---- test::apple stdout ----
    thread 'test::apple' panicked at 'assertion failed: `(left == right)`
  left: `"ppleahay"`,
 right: `"apple-hay"`', src/main.rs:52:9


Use with_capacity if possible

You can use String::with_capacity in transform_word. The new word has the same length as the old one plus four:

fn transform_word(word: &amp;str) -&gt; String {
    if word.is_empty() {
        return String::new();
    }
    let mut new_word = String::with_capacity(word.len() + 4);

    // still buggy, see above
    new_word.push_str(&amp;word[1..]);
    new_word.push_str(&amp;word[..1]);
    if starts_with_a_vowel(word) {
        new_word.push_str("h");
    }
    new_word.push_str("ay");
    new_word
}


However, we still need to fix the bug mentioned before:

fn transform_word(word: &amp;str) -&gt; String {
    if word.is_empty() {
        return String::new();
    }
    let mut new_word = String::with_capacity(word.len() + 4);

    if starts_with_a_vowel(word) {
        new_word.push_str(&amp;word);
        new_word.push_str("-hay");
        new_word
    } else {
        new_word.push_str(&amp;word[1..]);
        new_word.push('-');
        new_word.push_str(&amp;word[..1]);
        new_word.push_str("ay");
        new_word
    }
}


Alternative variants

Truth be told, that looks somewhat silly. We can just use += instead of push_str, or even +:

fn transform_word(word: &amp;str) -&gt; String {
    if word.is_empty() {
        return word.to_owned();
    }
    if starts_with_a_vowel(word) {
        word.to_owned() + "-hay"
    } else {
        word[1..].to_owned() + "-" + &amp;word[..1] + "ay"
    }
}


Speaking about alternatives, if you use any then starts_with_a_vowel can be written in a single line:

fn starts_with_a_vowel(word: &amp;str) -&gt; bool {
    "aeiou".chars().any(|vowel| word.starts_with(vowel))
}


Note that we use a Char as pattern, not a String. starts_with works with both, but this variant shows our intend to match only a single character.

Ownership

It's not necessary to take ownership of the given String in pig_latinify.