如何在 Rust 中连接一个字符向量

3
我正在做Rustlings的练习,我尝试制作一个大写函数。 但是连接部分不起作用。 它说:
“结构体Vec存在方法join,但其trait边界未得到满足, 以下trait bound未被满足: <[char] as Join<_>>::Output=_>"。
我不知道这是什么意思。 连接字符向量的正确方法是什么?
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("")
        },
    }
}

请注意,此代码还存在逻辑错误,因为它删除了原始字符串的第二个字符- https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=5a816255eb701742bb9d2d8f5c30cdbc。 - Cerberus
还有一种不使用向量的选项,但你需要用向量来解决问题。 - Kaplan
没错。我没有注意到收集迭代器会收集剩下的部分,即尚未迭代的部分。我以为它会从一开始就全部收集起来。谢谢。 - Gabriel Machado
3个回答

7
回答你的问题,从一个字符向量获取字符串的最佳方式是通过迭代和收集:
 let my_string = my_char_vec.iter().collect();

但是你的代码中还存在其他问题:
  1. 你取第一个字符来检查字符串是否为空,然后从迭代的剩余部分构建一个字符串,并使第一个字符替换该字符串的第一个字符......丢失了这个字符,它原本是初始字符串的第二个字符。
  2. 你正在构建一个无用的向量并再次进行迭代。这些是不必要的步骤,你不需要这些步骤。
你可以通过将代码适应直接从迭代中写入字符串来解决这些问题:
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
}

请注意,您正在使用专为ASCII字符设计的功能。当您确定仅处理ASCII时,这是可以接受的,但如果您想要在国际范围内使用某些内容,则应该使用更通用的to_uppercase。 因为Unicode大写字符可能由多个字符组成,所以代码会更加复杂:
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
}

如果你确定可以使用to_ascii_uppercase,那么还有另一种解决方案。因为ASCII字符在小写和大写时只占用一个字节,所以可以直接在UTF8字符串中就地修改它们:

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

这种方法可以在不分配任何内存的情况下用于可变字符串。但是,如果第一个字符不是一个字节长,它就会发生恐慌。

我不确定原帖作者是否意识到,如果第一个字符不是字符边界或者不是ASCII字符,程序将会失败。 - Kaplan
@Kaplan 是的,我会对此进行更多评论。 - Denys Séguret
谢谢。这是一个非常完整的解决方案:D - Gabriel Machado

3
你可以直接使用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(),
    }
}

很好,我不知道Chars迭代器有一个as_str方法 - Caesar

1

join方法的返回类型中有一个限制,char类型不符合该限制,因为它没有静态生命周期:

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

你应该使用 String::from_iter
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)
      },
  }
}

2
.join 方法无法正常工作的原因是因为将元素加入到 String 中需要实现 Borrow<str>,而 char 并没有实现该方法。这并不是由于某些静态生命周期限制所导致的。 - kmdreko
@kmdreko,哦,谢谢你的澄清!我当时误读了错误信息。 - moy2010

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接