【问题标题】:How do I collect from multiple iterator types?如何从多个迭代器类型中收集?
【发布时间】:2016-11-15 12:25:13
【问题描述】:

我正在尝试为 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 的返回值不能收集在一起,因为地图现在有多种返回类型。

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

【问题讨论】:

    标签: rust


    【解决方案1】:

    强制转换很少是解决 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());
    }
    

    如果你有某种类型来表示“ToUppercaseToLowercase”,这或多或少就是collect 会做的事情无论如何。在大量大多数情况下,这也只会执行一次分配。

    【讨论】:

    • stdlib 中的某处可能有一个 Iterator 类型,但我怀疑我们中的任何人都能找到它。老实说,我觉得有太多,但我离题了。这看起来是最好的解决方案,因为它只是放弃了类似 for 循环的 collect 调用,我认为这将是最聪明的方法。感谢您的意见。
    【解决方案2】:

    我会这样做:

    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());
    }
    

    这将比理想的解决方案慢一点,因为它将第一个字符解码两次,但它在 IMO 上非常易读。

    输出:

    Foobar
    

    【讨论】:

    • @Jared 它不会泄漏内存(一旦 collect 使用完 Boxes 就会被释放),但效率会非常低。请查看编辑后的答案。
    【解决方案3】:

    在查看了 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
    }
    
    猜你喜欢
    • 2015-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-21
    • 1970-01-01
    • 2019-08-23
    • 2021-05-25
    相关资源
    最近更新 更多