当前位置: 首页 > 知识库问答 >
问题:

返回对内部集合的引用时的迭代器生存期问题

白浩荡
2023-03-14

我有一个将数据延迟加载到内部向量的结构(但是对于这个例子来说这是不允许的)。然后,我为IntoIterator类型实现了< code>IntoIterator和< code>Iterator:

struct EntryOwned(u32);
struct Entry<'a>(&'a u32);

impl<'a> EntryOwned {
    fn to_entry(&'a self) -> Entry<'a> {
        Entry(&self.0)
    }
}

struct LazyStruct {
    cache: Vec<EntryOwned>,
}

impl<'a> LazyStruct {
    fn new(data: Vec<EntryOwned>) -> LazyStruct {
        Self {
            cache: data,
        }
    }

    fn get_entry(&'a self, index: usize) -> Option<Entry<'a>> {
        match self.cache.get(index) {
            Some(entry_owned) => Some(entry_owned.to_entry()),
            None => None,
        }
    }
}

impl<'a> IntoIterator for &'a mut LazyStruct {
    type Item = Entry<'a>;
    type IntoIter = LazyStructIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        LazyStructIter {
            inner: self,
            current_index: 0,
        }
    }
}

struct LazyStructIter<'a> {
    inner: &'a mut LazyStruct,
    current_index: usize,
}

impl<'a> LazyStructIter<'a> {
    fn next_item(&'a mut self) -> Option<Entry<'a>> {
        if self.current_index > self.inner.cache.len() {
            return None;
        }
        let ret = self.inner.get_entry(self.current_index);
        self.current_index += 1;
        ret
    }
}

impl<'a> Iterator for LazyStructIter<'a> {
    type Item = Entry<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        self.next_item()
    }
}

这给了我一个终生的问题:

   Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/main.rs:64:14
   |
64 |         self.next_item()
   |              ^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 63:5...
  --> src/main.rs:63:5
   |
63 | /     fn next(&mut self) -> Option<Self::Item> {
64 | |         self.next_item()
65 | |     }
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:64:9
   |
64 |         self.next_item()
   |         ^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 60:6...
  --> src/main.rs:60:6
   |
60 | impl<'a> Iterator for LazyStructIter<'a> {
   |      ^^
note: ...so that the types are compatible
  --> src/main.rs:64:14
   |
64 |         self.next_item()
   |              ^^^^^^^^^
   = note: expected  `&mut LazyStructIter<'_>`
              found  `&mut LazyStructIter<'a>`

引用的生存期应该绑定到< code>LazyStruct,但是我不能改变Iter特性不接受任何生存期说明符。我已经检查了一些类似问题的答案:迭代器通过引用返回项目,生存期问题如何编写一个迭代器返回对自身的引用?

编辑:Entry是一个更复杂的数据结构,无法复制它。我必须将其绑定到特定的生命周期,因为它包含对事物的引用。

其中之一指出迭代器应该持有对原始集合的引用,而不是拥有它,因为不允许它返回具有自身生存期的引用。但我不能让它工作。那么,我应该如何播放这里的引用?

这是操场的例子

共有2个答案

单于经纬
2023-03-14

通常,您不需要在rust中指定生命周期,除非两件事中的一件为真,引用绑定到未知的所有者,或者引用是静态的。Rust说它期望'_生命周期,这意味着它了解所有权并自行借用。

https://play.rust-lang.org/?version=stable

struct EntryOwned(u32);
struct Entry(u32);

impl EntryOwned {
    fn to_entry(&self) -> Entry {
        Entry(self.0)
    }
}

struct LazyStruct {
    cache: Vec<EntryOwned>,
}

impl LazyStruct {
    fn new(data: Vec<EntryOwned>) -> LazyStruct {
        Self {
            cache: data,
        }
    }

    fn get_entry(&self, index: usize) -> Option<Entry> {
        match self.cache.get(index) {
            Some(entry_owned) => Some(entry_owned.to_entry()),
            None => None,
        }
    }
}

impl IntoIterator for LazyStruct {
    type Item = Entry;
    type IntoIter = LazyStructIter;

    fn into_iter(self) -> Self::IntoIter {
        LazyStructIter {
            inner: self,
            current_index: 0,
        }
    }
}

struct LazyStructIter {
    inner: LazyStruct,
    current_index: usize,
}

impl LazyStructIter {
    fn next_item(&mut self) -> Option<Entry> {
        if self.current_index > self.inner.cache.len() {
            return None;
        }
        let ret = self.inner.get_entry(self.current_index);
        self.current_index += 1;
        ret
    }
}

impl Iterator for LazyStructIter {
    type Item = Entry;

    fn next(&mut self) -> Option<Self::Item> {
        self.next_item()
    }
}


fn main() {
    // let json_data = r#"{"item_1" : 10, "item_2" : 100, "item_3" : 1000}"#;
    // if let Ok(data) = serde_json::from_str::<A>(json_data) {
    //     println!("{:?}", data);
    // }
    // else if let Ok(data) = serde_json::from_str::<B>(json_data) {
    //     println!("{:?}", data);
    // }
    for _ in 0..0 {
        println!("foo");    
    }
}

删除生命周期规范后,它会进行精细编译并运行。

余阳秋
2023-03-14

你不能。

您需要next()返回的每个项都有一个引用,其生存期与LazyStructIteriterator的存在相关。

这在Iterator接口中是不可能的:

pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;

但如果特性定义为:

trait FakeIterator<'a> {
    type Item;

    fn next(&'a mut self) -> std::option::Option<Self::Item>;
}

impl<'a> FakeIterator<'a> for LazyStructIter<'a> {
    type Item = Entry<'a>;

    fn next(&'a mut self) -> Option<Self::Item> {
        self.next_item()
    }
}

RFC 1598 - GAT的目标是拥有一种迭代器的能力,这种迭代器返回从自身借用的项目。

有关此主题的解决方法集合,另请参阅此文章。

 类似资料:
  • 我有一个包含私有可变数据列表的类。 我需要在以下条件下公开列表项: 列表不应在外部可修改; 对于使用 getter 函数的开发人员来说,应该很清楚,他们获得的列表不能被修改。 哪个getter函数应该标记为推荐方法?或者您能提供更好的解决方案吗? UPD:这个问题来自关于列表getter实现的最佳实践的真正代码审查讨论

  • 将这些视为对象: 查看java文档,对于LinkedList类,LinkedList类中没有迭代器方法的实现,但是,实现是在AbstractSequentialList类中。 listIterator()方法在AbstractList类中实现,AbstractSequentialList的父类,总结一下,如果我没弄错的话,它返回一个不使用节点概念的迭代器对象。 但是方法是在LinkedList类中

  • IM有下一个模型和集合定义: 日志记录显示,集合创建正确: {长度:15,模型:数组[15],_byId:对象,构造函数:函数,模型:函数...} 但是,当尝试迭代集合(在渲染视图方法中)时,显然无法正确获取当前元素: console.log,而不是返回模型,返回: {cid:“c1”,属性:对象,集合:r,_changing:false,_previousAttributes:Object…}

  • 问题内容: 我有一个〜10M记录的MySQL表,可以使用SqlAlchemy进行交互。我发现对这个表的大子集的查询将消耗过多的内存,即使我以为我使用的是内置生成器,它可以智能地获取数据集的一口大小的块: 为了避免这种情况,我发现我必须构建自己的迭代器,该迭代器会分块地进行处理: 这是正常的还是关于SA内置发电机我缺少什么? 这个问题的答案似乎表明内存消耗是不希望的。 问题答案: 大多数DBAPI实

  • 问题内容: 我了解像Hashtable这样的集合是同步的,但是有人可以向我解释它是 如何 工作的,在什么时候访问仅限于并​​发调用?例如,假设我使用了一些像这样的迭代器: 有人可以解释一下从不同线程中随机调用这些函数是否有陷阱吗?特别是,迭代器如何进行同步,尤其是在使用entrySet()时,似乎也需要同步?如果在循环之一进行时调用clear()会发生什么?如果removesomething()删

  • 问题内容: 在Java中为集合的集合设计一个迭代器。迭代器应隐藏嵌套,使您可以迭代属于所有集合的所有元素,就像使用单个集合一样 问题答案: 这是一个可能的实现。请注意,我没有执行remove():