使用修改后的`Chars`迭代器

时间:2016-10-15 10:29:48

标签: iterator rust traits lifetime

我想为包含IntoIterator的结构实现String特征。迭代器基于chars()迭代器,应该计算'1'个字符并累积结果。这是我到目前为止的简化版本:

use std::iter::Map;
use std::str::Chars;

fn main() {
    let str_struct = StringStruct { system_string: String::from("1101") };
    for a in str_struct {
        println!("{}", a);
    }
}

struct StringStruct {
    system_string: String
}

impl IntoIterator for StringStruct {
    type Item = u32;
    type IntoIter = Map<Chars, Fn(char) -> u32>;

    fn into_iter(self) -> Self::IntoIter {
        let count = 0;
        return self.system_string.chars().map(|c| match c {
            Some('1') => {
                count += 1;
                return Some(count);
            },
            Some(chr) => return Some(count),
            None => return None
        });
    }
}

预期输出:1, 2, 2, 3

这失败了:

error[E0107]: wrong number of lifetime parameters: expected 1, found 0
  --> src/main.rs:17:25
   |
17 |     type IntoIter = Map<Chars, Fn(char) -> u32>;
   |                         ^^^^^ expected 1 lifetime parameter

字符迭代器应该与StringStruct::system_string具有相同的生命周期,但我不知道如何表达这一点,或者这种方法是否可行。

1 个答案:

答案 0 :(得分:5)

要回答您提出的问题,我建议impl IntoIterator for &StringStruct引用StringStruct而不是直接结构)。代码如下所示:

impl<'a> IntoIterator for &'a StringStruct {
    type Item = u32;
    type IntoIter = Map<Chars<'a>, Fn(char) -> u32>;
    // ...
}

但是,您会发现许多更多错误之后会有不同的来源。弹出的下一个错误是Fn(char) -> u32在编译时没有常量大小。

问题是您尝试通过编写Fn(char) -> u32来命名闭包的类型。但这不是不是闭包的类型,而只是闭包实现的特征。闭包的类型不能命名为(有时称为“伏地级类型”)。

这意味着,您现在无法指定Map<_, _>对象的类型。这是一个已知的问题;最近接受的impl Trait - RFC可能会为此类案例提供解决方法。但是现在,这是不可能的,抱歉。

那么如何解决呢?您需要创建自己的类型来实现Iterator并使用它而不是Map<_, _>。请注意,您仍然可以使用Chars迭代器。这是完整的解决方案:

struct StringStructIter<'a> {
    chars: Chars<'a>,
    count: u32,
}

impl<'a> Iterator for StringStructIter<'a> {
    type Item = u32;

    fn next(&mut self) -> Option<Self::Item> {
         self.chars.next().map(|c| {
            if c == '1' {
                self.count += 1;
            }
            self.count
        })
    }
}

impl<'a> IntoIterator for &'a StringStruct {
    type Item = u32;
    type IntoIter = StringStructIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
         StringStructIter {
             chars: self.system_string.chars(),
             count: 0,
         }
    }
}

fn main() {
    let str_struct = StringStruct { system_string: String::from("1101") };
    for a in &str_struct {
        println!("{}", a);
    }
}

只是一个小注释:一个明确的return在没有必要时被认为是Rust中的坏风格。通过尽可能删除return来更好地坚持规则并编写惯用代码; - )