2
\$\begingroup\$

I've started learning Rust a few days ago. This is the Pig Latin exercise from the Rust Book. The code works as expected. I have commented the logic of the program in the code.

My handling of Strings is very hacky, as far as I know. To be honest, I mostly fiddled with it looking at error messages until the compiler was satisfied. I'd like to know what would be a idiomatic and may be more readable way to write it and if there as sane ways to optimize, especially the string manipulation part.

Thank you

use std::io;

fn main() {
    println!("Please enter a sentence:");

    const VOWELS: &str = "aeoiu";

    let mut sentence = String::new();
    io::stdin()
        .read_line(&mut sentence)
        .expect("An error occured");

    let mut words = sentence
        .split_whitespace()
        .map(|x| String::from(x))
        .collect::<Vec<String>>();

    for word in &mut words {
        if VOWELS.contains(word.chars().nth(0).unwrap().to_ascii_lowercase()) {
            // Starts with consonant:
            // The first consonant of each word is moved to the end of the word
            // and “ay” is added, so “first” becomes “irst-fay.”
            *word = format!("{}-hay", (*word).clone());
            // *word = (*word).clone() + "-" + "hay";
        } else {
            // Starts with a vowel:
            // Add “hay” to the end (“apple” becomes “apple-hay”).
            *word = format!(
                "{}-{}ay",
                String::from(&word[1..]),
                word.chars().nth(0).unwrap().to_string()
            );
            // String::from(&word[1..]) + "-" + &(word.chars().nth(0).unwrap().to_string()) + "ay";
        }
    }

    println!("{}", words.join(" "))
```
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I wrote my comments in the new code:

fn main() {

    // I removed the input for simplicity
    let sentence = String::from("The elves are coming");

    // You don't need to collect to an intermediary vector and then mutate values inside
    // It is always nice if you can manage without mutating
    // It is also always good to split out big chunks of code into functions, here "pigifize". You can then debug or unit test that single function, for example
    let words = sentence
        .split_whitespace()
        .map(pigifize)
        .collect::<Vec<_>>();

    println!("{}", words.join(" "))
}

const VOWELS: &str = "aeoiu";

fn pigifize(word: &str) -> String {
    if word.is_empty() {
        // I don't know if this case can happen from split_whitespace, but just to get it out of the way (it makes the function more robust, should it get called in other cases)
        word.into()
    } else {
        // I removed your comments about the 2 cases because they were inverted. But they could be readded
        let first = word.chars().nth(0).unwrap();
        if VOWELS.contains(first.to_ascii_lowercase()) {
            format!("{}-hay", word) // format! can print string slices so you don't need to create String's as much
        } else {
            format!(
                "{}-{}ay",
                &word[1..],
                first
            )
        }
    }
}

Some example tests (they can help you while you develop the function too):

#[test]
fn pigifize_vowel() {
    assert_eq!(pigifize("unicorn"), "unicorn-hay")
}

#[test]
fn pigifize_consonant() {
    assert_eq!(pigifize("rhino"), "hino-ray")
}

#[test]
fn pigifize_empty() {
    assert_eq!(pigifize(""), "")
}
\$\endgroup\$
1
  • \$\begingroup\$ Thank you for the detailed answer. Learned a lot :D \$\endgroup\$
    – user1984
    Commented Dec 9, 2021 at 9:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.