繁体   English   中英

拥有另一个迭代器并创建具有通用生命周期的项目的迭代器

[英]Iterator that owns another iterator and creates items with generic lifetime

我想创建一个拥有另一个迭代器并基于该另一个迭代器提供项目的迭代器。 最初,内部迭代器是由数据库查询的结果形成的,它提供从数据库到达的原始数据行。 外部迭代器获取内部迭代器的项目并将它们放入在我的程序中有意义的结构中。 因为不同的软件版本将相同的数据存储在不同的数据库表结构中,所以我有一个解析器特征,它需要一行并创建一个结构。 我的外部迭代器需要两个参数来创建:用于数据库行的迭代器和实现如何解析数据的 object。

但是我遇到了一个我并没有真正看到原因的终身错误,并且遵循编译器的提示只会让我陷入困境。 我从字面上遵循编译器的建议并回到同样的问题。 我试图缩小代码并将其简化为最小形式,以重现我遇到的相同编译器错误。 我不完全确定它是否可以进一步最小化,但我也希望它类似于我的真实代码。

这是示例:

struct Storeroom<'a> {
    storeroom_id: i64,
    version: &'a str
}

trait StoreroomParser {
    fn parse(&self, row: Row) -> Result<Storeroom, Error>;
}

struct StoreroomParserX;
impl StoreroomParser for StoreroomParserX {
    fn parse(&self, row: Row) -> Result<Storeroom, Error> {
        Ok(Storeroom { storeroom_id: row.dummy, version: "0.0.0"})
    }
}

struct StoreroomIterator {
    rows: Box<dyn Iterator<Item = Row>>,
    parser: Box<dyn StoreroomParser>
}

impl StoreroomIterator {
    fn new() -> Result<Self, Error> {
        let mut rows: Vec<Row> = vec![];
        rows.push(Row { dummy: 4});
        rows.push(Row { dummy: 6});
        rows.push(Row { dummy: 8});
        let rows = Box::new(rows.into_iter());
        let parser = Box::new(StoreroomParserX {});
        Ok(Self {rows, parser})
    }
}

impl Iterator for StoreroomIterator {
    type Item<'a> = Result<Storeroom<'a>, Error>;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(nextrow) = self.rows.next() {
            Some(self.parser.parse(nextrow))
        }
        else {
            None
        }
    }
}

在我第一次尝试时,编译器建议在 Item 类型声明中添加生命周期注解,因为它使用需要生命周期的结构。 但这导致了以下错误:

error[E0658]: generic associated types are unstable
  --> src/main.rs:59:5
   |
59 |     type Item<'a> = Result<Storeroom<'a>, Error>;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information

error[E0195]: lifetime parameters or bounds on type `Item` do not match the trait declaration
  --> src/main.rs:59:14
   |
59 |     type Item<'a> = Result<Storeroom<'a>, Error>;
   |              ^^^^ lifetimes do not match type in trait

这是 Playground 上的示例代码。

当我试图通过将生命周期注释移动到 impl 块来缓解这种情况时,我引发了以下错误,我无法继续:

error: lifetime may not live long enough
  --> src/main.rs:61:13
   |
56 | impl<'a> Iterator for StoreroomIterator<'a> {
   |      -- lifetime `'a` defined here
...
59 |     fn next(&mut self) -> Option<Self::Item> {
   |             - let's call the lifetime of this reference `'1`
60 |         if let Some(nextrow) = self.rows.next() {
61 |             Some(self.parser.parse(nextrow))
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`

操场。

我已经被这个问题困扰了大约一个星期了。 您对如何解决这些错误有任何想法吗? 我也在想,我可能应该只在行上使用map()以及正确转换数据所需的任何闭包,但在这一点上,它肯定会感觉像是一种妥协。

所以你说你用Box::leak()强制version'static的”。 如果是这样,您可以完全删除生命周期参数:

struct Storeroom {
    storeroom_id: i64,
    version: &'static str
}

操场

您还提到编译器“强制”您使用Box<dyn> rowsparser 您可以通过使StoreroomIterator对两个成员的两种类型通用来避免这种情况。 唯一需要改变的是在构造函数中取rows

struct StoreroomIterator<R: Iterator<Item = Row>, P: StoreroomParser> {
    rows: R,
    parser: P
}

impl<R: Iterator<Item = Row>> StoreroomIterator<R, StoreroomParserX> {
    fn new(rows: R) -> Result<Self, Error> {
        let parser = StoreroomParserX {};
        Ok(Self { rows, parser })
    }
}

操场

也有可能让一切都与生命周期一起工作,但从你不完整的例子来看,很难准确地说。 您可能希望在Storeroom中存储一个包含版本的String ,然后添加一个version()方法来按需生成一个Version ,而不是预先生成它们。 但是,如果不知道这一切是为了什么,就很难说。 您可能只想切换到不同的库来处理版本比较。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM