如何从多个迭代器类型中收集?

时间:2016-07-13 04:43:58

标签: rust

我正在尝试为String实现一个新特性,该字符串具有一个函数,该函数将每个String的第一个字母大写并且不使其余字符大写。我基于Rust标准库中to_uppercase()to_lowercase()的函数接口。

use std::io;

trait ToCapitalized {
    fn to_capitalized(&self) -> String;
}

impl ToCapitalized for String {
    fn to_capitalized(&self) -> String {
        self.chars().enumerate().map(|(i, c)| {
            match i {
                0 => c.to_uppercase(),
                _ => c.to_lowercase(),
            }
        }).collect()
    }
}

fn main() {
    let mut buffer = String::new();
    io::stdin().read_line(&mut buffer).ok().expect("Unable to read from stdin.");

    println!("{}", buffer.to_capitalized());
}

此代码基于here给出的建议,但代码已过时并导致多个编译错误。我现在执行的唯一问题是以下错误:

src/main.rs:10:13: 13:14 error: match arms have incompatible types [E0308]
src/main.rs:10             match i {
                           ^
src/main.rs:10:13: 13:14 help: run `rustc --explain E0308` to see a detailed explanation
src/main.rs:10:13: 13:14 note: expected type `std::char::ToUppercase`
src/main.rs:10:13: 13:14 note:    found type `std::char::ToLowercase`
src/main.rs:12:22: 12:38 note: match arm with an incompatible type
src/main.rs:12                 _ => c.to_lowercase(),

简而言之,fn to_uppercase(&self) -> ToUppercasefn to_lowercase(&self) -> ToLowercase的返回值无法收集在一起,因为地图现在有多种返回类型。

我试图将它们转换为另一种常见的Iterator类型,例如BytesChars,但是这些迭代器类型无法收集以形成String。有什么建议吗?

3 个答案:

答案 0 :(得分:2)

很少转换是解决Rust中类型问题的好方法。这里正确的解决方案是编写(或找到一个定义的包)一种统一不同迭代器类型的类型。但这需要努力,所以将collect抛出窗口会更简单:

trait ToCapitalized {
    fn to_capitalized(&self) -> String;
}

impl ToCapitalized for String {
    fn to_capitalized(&self) -> String {
        let mut r = String::with_capacity(self.len());
        for (i, c) in self.chars().enumerate() {
            match i {
                0 => r.extend(c.to_uppercase()),
                _ => r.extend(c.to_lowercase()),
            }
        }
        r
    }
}

fn main() {
    let buffer = String::from("canberra");
    println!("{}", buffer.to_capitalized());
}

如果您有某种类型来表示“collectToUppercase”,这或多或少都会ToLowercase 浩大的大多数情况下,这也只会执行一次分配。

答案 1 :(得分:2)

在查看了pub fn to_uppercase(&self) -> String here的实现之后,我设计了一个解决方案,该解决方案在Dogbert和DK。的解决方案以及标准库中给出的实现之间有点混合。它甚至适用于Unicode!

fn to_capitalized(&self) -> String {
    match self.len() {
        0 => String::new(),
        _ => {
            let mut s = String::with_capacity(self.len());
            s.extend(self.chars().next().unwrap().to_uppercase());
            s.extend(self.chars().skip(1).flat_map(|c| c.to_lowercase()));
            return s;
        }
    }
}

Working Rust Playground Example

编辑:为了提高可见度,Shepmaster的简化和优化解决方案:

fn to_capitalized(&self) -> String {
    let mut s = String::with_capacity(self.len());
    let mut chars = self.chars(); 

    s.extend(chars.by_ref().take(1).flat_map(|c| c.to_uppercase()));
    s.extend(chars.flat_map(|c| c.to_lowercase()));

    s
}

答案 2 :(得分:1)

这是我将如何做到的:

trait ToCapitalized {
    fn to_capitalized(&self) -> String;
}

impl ToCapitalized for String {
    fn to_capitalized(&self) -> String {
        match self.chars().next() {
            Some(c) => {
                c.to_uppercase()
                    .chain(self.chars().skip(1).flat_map(|c| c.to_lowercase()))
                    .collect()
            }
            None => String::new(),
        }
    }
}

fn main() {
    println!("{}", "fOoBaR".to_string().to_capitalized());
}

这会比理想的解决方案慢一些,因为它会对第一个char进行两次解码,但它的可读性非常高。

输出:

Foobar