Gabriel Machado
Gabriel Machado

Reputation: 493

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("")
        },
    }
}

Upvotes: 6

Views: 7936

Answers (3)

devgony
devgony

Reputation: 49

You can just use as_str()

pub fn capitalize_first(input: &str) -> String {
    let mut c = input.chars();
    match c.next() {
        None => String::new(),
        Some(first) => first.to_uppercase().to_string() + c.as_str(),
    }
}

Upvotes: 4

Denys S&#233;guret
Denys S&#233;guret

Reputation: 382092

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.

Upvotes: 12

moy2010
moy2010

Reputation: 902

There's a constraint in the return type of the join method that the char type does not meet, since it doesn't have a static lifetime:

pub fn join<Separator>(&self, sep: Separator) -> <Self as Join<Separator>>::Output
    where
        Self: Join<Separator>,
    {
        Join::join(self, sep)
    }

You should use String::from_iter instead:

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;
          String::from_iter(v)
      },
  }
}

Upvotes: 2

Related Questions