我是否错误地将IntoIterator用于参考,或者这是一个应该报告的Rust错误?

时间:2016-11-18 01:42:29

标签: iterator rust lifetime borrow-checker

根据the Rust book进一步为包装的向量实施IntoIterator的示例,我也尝试实现IntoIterator以获取对包装器的引用,如{{ 3}}:

struct VecWrapper(Vec<i32>);

impl VecWrapper {
    fn iter(&'static self) -> Iter {
        Iter(Box::new(self.0.iter()))
    }
}

struct Iter(Box<Iterator<Item = &'static i32>>);

impl Iterator for Iter {
    type Item = &'static i32;
    fn next(&mut self) -> Option<Self::Item> {
        self.0.next()
    }
}

impl IntoIterator for &'static VecWrapper {
    type Item = &'static i32;
    type IntoIter = Iter;
    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

fn main() {
    // let test = vec![1, 2, 3]; // obviously, works
    let test = VecWrapper(vec![1, 2, 3]); // not working
    for v in &test {
        println!("{}", v);
    }
}

虽然实现编译,但在main中使用它的尝试不会出现以下错误:

error[E0597]: `test` does not live long enough
  --> src/main.rs:31:14
   |
31 |     for v in &test {
   |              ^^^^^
   |              |
   |              borrowed value does not live long enough
   |              argument requires that `test` is borrowed for `'static`
...
34 | }
   | - `test` dropped here while still borrowed

这段代码大大简化了我实际想要使用的内容,仅使用'static生命周期,使用现有的包含类型,并使用i32作为内部(迭代)类型,但它归结为显示问题。

接受的答案解决了问题的第一部分,即不使用'static和使用+ 'a的特征。我仍然遇到实际代码的问题,这是一个LazyList实现。我已将其发布为the following code (Playground link)

1 个答案:

答案 0 :(得分:6)

您已正确实现迭代器,以便在程序的整个长度生存的VecWrapper 'static的引用中实现'a生命周期。

您希望拥有通用生命周期。然后,这个生命周期将被提供一个具体的生命周期,每个实例都是唯一的。通常,我们很懒,只给这个生命周期命名为struct VecWrapper(Vec<i32>); impl VecWrapper { fn iter(&self) -> Iter { Iter(Box::new(self.0.iter())) } } struct Iter<'a>(Box<Iterator<Item = &'a i32> + 'a>); impl<'a> Iterator for Iter<'a> { type Item = &'a i32; fn next(&mut self) -> Option<Self::Item> { self.0.next() } } impl<'a> IntoIterator for &'a VecWrapper { type Item = &'a i32; type IntoIter = Iter<'a>; fn into_iter(self) -> Self::IntoIter { self.iter() } } fn main() { let test = VecWrapper(vec![1, 2, 3]); // not working for v in &test { println!("{}", v); } }

Box<Iterator<Item = &'a i32> + 'a>

重要变化:

  • + 'a - Item已添加。这是必需的,因为特征对象将假定没有内部值引用具有短寿命的任何内容。
  • &'a i32类型现在为<'a>
  • 通用生命周期在许多地方声明,并在许多其他地方提供(struct Iter<'a>(std::slice::Iter<'a, i32>); )。

另见:

通常,在这里使用特质对象并不是理由。我直接嵌入了迭代器:

SELECT DISTINCT instructors.name AS name,
substring(sections.course,1,4) AS courseCode
FROM instructors
JOIN teaches ON instructors.id = teaches.instructor_id
JOIN schedules ON teaches.schedule_id = schedules.id
JOIN sections ON schedules.section_id = sections.id

这避免了需要任何间接,在这种情况下无论如何都是未使用的。此外,它更明显地结合了一生。