Feedback on Chapter 8 exercise 2

Hello everyone!

I'm learning Rust from the official book. I'm working on the exercise of the 8th chapter if anyone would be keen to provide some feedbacks on my solution that would be great.

Thank you. :pray:

fn is_in_array(haystack: &[char], needle: Option<char>) -> bool {
    match needle {
        None => false,
        Some(ndl) => {
            for c in haystack {
                if *c == ndl {
                    return true;
                }
            }
            false
        }
    }
}

fn convert_to_pig_latin(string: &String) -> String {
    let vowels = ['a', 'e', 'i', 'o', 'u', 'y'];
    let first_char = string.chars().nth(0);
    let start_with_vowel = is_in_array(&vowels, first_char);

    if start_with_vowel {
        format!("{}-hay", string)
    } else {
        match first_char {
            None => String::from(""),
            Some(c) => format!("{}-{}ay", &string[1..], &c),
        }
    }
}

fn main() {
    let test = String::from("car");

    println!("initial string = {}", &test);
    println!("translated string = {}", &convert_to_pig_latin(&test));
}

&String is a mostly useless type, the idiomatic way is to use &str in function parameters.

You shouldn't slice by 1 to dismiss the first char, see char::len_utf8.

String::new() or String::default() for an empty string is more idiomatic as well.

Slices have a slice::contains method. Though here I'm using str::contains which is slightly more complex.

fn convert_to_pig_latin(string: &str) -> String {
    if string.is_empty() {
        return String::new();
    }
    let first_char = string.chars().nth(0).unwrap();
    if "aeiouy".contains(first_char) {
        format!("{}-hay", string)
    } else {
        format!("{}-{}ay", &string[first_char.len_utf8()..], first_char)
    }
}

There's probably things I missed, but all in all, your code is good in my opinion.

3 Likes

Make sure to use clippy when learning Rust. It gives you a lot of usefull small hints for improvements. E.g. in the playground under Tools.

    Checking playground v0.0.1 (/playground)
warning: writing `&String` instead of `&str` involves a new object where a slice will do
  --> src/main.rs:15:33
   |
15 | fn convert_to_pig_latin(string: &String) -> String {
   |                                 ^^^^^^^ help: change this to: `&str`
   |
   = note: `#[warn(clippy::ptr_arg)]` on by default
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg

warning: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent
  --> src/main.rs:17:22
   |
17 |     let first_char = string.chars().nth(0);
   |                      ^^^^^^^^^^^^^^^^^^^^^ help: try calling `.next()` instead of `.nth(0)`: `string.chars().next()`
   |
   = note: `#[warn(clippy::iter_nth_zero)]` on by default
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero

warning: 2 warnings emitted

    Finished dev [unoptimized + debuginfo] target(s) in 2.11s
2 Likes

Thank you for your feedbacks. :pray:

Alright, thank you so it means if you wanted to pass a String to the function you'd have to do String.as_str() or use a slice shorthand isn't it?

I knew there was something special since they mentioned in the exercise: "Keep in mind the details about UTF-8 encoding!". Thanks.

I hesitated on this one to be honest. I wasn't the sure String::from("") was the same or not with String::new().

I looked for a such a function on the array type but I couldn't find it. I didn't know it existed on the Slice type. Thank you.

Thank you for the suggestion. I'm using vim as my editor and I use vim-ale for Rust linting through rust-analyzer. I'll check it out.

Technically, yes. Or rather, that’s one possibility. However, there’s also an implicit coercion from &String to &str, so most often you don’t need to change anything at all at the calling site. So, if you have some variable s: String then passing &s to a function expecting &str works just as well as passing s.as_str() or &s[..].

The same applies to &Vec<T> and &[T], and clippy will readily complain about &Vec<T> function arguments just like it complained about the &String.

Alright, thank you @steffahn for the explanation.