Search code examples
stringrustiteratorchar

How do I join a char vector in Rust


I'm doing the rustlings exercises and I tried this to make a capitalize function. But the join part does not work. It says:

"the method join exists for struct Vec<char>, but its trait bounds were not satisfied the following trait bounds were not satisfied: <[char] as Join<_>>::Output = _"

which I don't know what means. What would be the right way to join a char vector?

pub fn capitalize_first(input: &str) -> String {
    let mut c = input.chars();
    match c.next() {
        None => String::new(),
        Some(first) => {
            let upper = first.to_ascii_uppercase();
            let mut v = c.collect::<Vec<char>>();
            v[0] = upper;
            v.join("")
        },
    }
}

Solution

  • To answer your question, the best way to get a string from a vec of chars is to iter and collect:

     let my_string = my_char_vec.iter().collect();
    

    But there are other problems in your code:

    1. you're taking the first char to check the string isn't empty, then you build a string from the rest of the iteration, and you make the first char replace the first char of that string... losing this char which was the second one of the initial str
    2. you're building a useless vec and iterating again. Those are expensive steps that you don't need

    You can fix those problems by adapting the code to directly write from the iteration into the string:

    pub fn capitalize_first(input: &str) -> String {
        let mut chars = input.chars();
        let mut string = String::new();
        if let Some(first) = chars.next() {
            string.push(first.to_ascii_uppercase());
            for c in chars {
                string.push(c);
            }
        }
        string
    }
    

    Note that you're using a function dedicated to ASCII characters. This is fine when you're sure you're only dealing with ASCII but if you want something which works in an international context, you want to use the more general to_uppercase. As an unicode uppercased character may be several characters, the code is a little more complex:

    pub fn capitalize_first(input: &str) -> String {
        let mut chars = input.chars();
        let mut string = String::new();
        if let Some(first) = chars.next() {
            let first = first.to_uppercase();
            for c in first {
                string.push(c);
            }
            for c in chars {
                string.push(c);
            }
        }
        string
    }
    

    If you're sure you can use to_ascii_upercase, then there's another solution. Because ASCII chars are just one byte in lowercase and uppercase, you can change them in place in the UTF8 string:

    pub fn capitalize_first(input: &str) -> String {
        let mut string = input.to_string();
        if !string.is_empty() {
            string[..1].make_ascii_uppercase();
        }
        string
    }
    

    This second approach could be used on a mutable string with zero allocation. But it would panic if the first char weren't one byte long.