Home > Mobile >  Efficient ways to build new Strings in Rust
Efficient ways to build new Strings in Rust

Time:11-01

I have just recently started learning Rust and have been messing around with some code. I wanted to create a simple function that removes vowels from a String and returns a new String. The code below functions, but I was concerned if this truly was a valid, typical approach in this language or if I'm missing something...

// remove vowels by building a String using .contains() on a vowel array
fn remove_vowels(s: String) -> String {
    let mut no_vowels: String = String::new();
    for c in s.chars() {
        if !['a', 'e', 'i', 'o', 'u'].contains(&c) {
            no_vowels  = &c.to_string();
        }
    }
    return no_vowels;
}

First, using to_string() to construct a new String and then using & to borrow just seemed off. Is there a simpler way to append characters to a String, or is this the only way to go? Or should I rewrite this entirely and iterate through the inputted String using a loop by its length, not by a character array?

Also, I have been informed that it's quite popular in Rust to not use the return statement but to instead let the last expression return the value from the function. Is my return statement required here, or is there a cleaner way to return that value that follows convention?

CodePudding user response:

You can use collect on an iterator of characters to create a String. You can filter out the characters you don't want using filter.

// remove vowels by building a String using .contains() on a vowel array
fn remove_vowels(s: String) -> String {
    s.chars()
        .filter(|c| !['a', 'e', 'i', 'o', 'u'].contains(c))
        .collect()
}

playground

If this is in a performance critical region, then since you know the characters you're removing are single bytes in utf8, they are OK to remove directly from the bytes instead. Which means you can write something like

fn remove_vowels(s: String) -> String {
    String::from_utf8(
        s.bytes()
            .filter(|c| ![b'a', b'e', b'i', b'o', b'u'].contains(c))
            .collect()
    ).unwrap()
}

which may be more efficient. playground

CodePudding user response:

If you consume the original String as your example does, you can remove the vowels in-place using retain(), which will avoid allocating a new string:

fn remove_vowels(mut s: String) -> String {
    s.retain(|c| !['a', 'e', 'i', 'o', 'u'].contains(&c));
    s
}

See it working on the playground. Side note: you may want to consider uppercase vowels as well.

  • Related