生命周期冲突-隐藏的内容无法列出容器



我正在尝试为我的一个结构实现Iterator

pub struct Node {
pub val: Vec<usize>,
}
pub struct NodeIter<'a, 'b> {
val: &'a Vec<usize>,
env: &'b mut Env,
index: usize,
}
impl<'a, 'b> Iterator for NodeIter<'a, 'b> {
type Item = &'b mut Obj;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.val.len() {
return Some(self.env.get_obj_at_mut(self.val[self.index]))
}
self.index += 1;
None
}
}

并且在编译时得到以下错误:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> srccorenodes.rs:20:23
|
20 |         Some(self.env.get_obj_at_mut(self.val[self.index]))
|                       ^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime defined here...
--> srccorenodes.rs:19:13
|
19 |     fn next(&mut self) -> Option<&'a mut Obj> {
|             ^^^^^^^^^
note: ...so that reference does not outlive borrowed content
--> srccorenodes.rs:20:14
|
20 |         Some(self.env.get_obj_at_mut(self.val[self.index]))
|              ^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined here...
--> srccorenodes.rs:16:6
|
16 | impl<'a> Iterator for NodeIter<'a> {
|      ^^
note: ...so that the types are compatible
--> srccorenodes.rs:20:9
|
20 |         Some(self.env.get_obj_at_mut(self.val[self.index]))
|         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: expected `Option<&'a mut Obj>`
found `Option<&mut Obj>`

我认为问题是get_obj_at_mut中的&mut Obj(它返回一个寿命为'b的&mut Obj,与env相同(可能比&mut self更长寿,在它的位置留下一个空引用。问题是env被可变引用绑定,并且可能持续超过NodeIter

所以我尝试了两种选择:

  1. 'b: 'c
impl<'a, 'b> NodeIter<'a, 'b> {
fn next_impl<'c>(&'c mut self) -> Option<&'b mut Obj> where 'b: 'c {
if self.index < self.val.len() {
return Some(self.env.get_obj_at_mut(self.val[self.index]))
}
self.index += 1;
None
}
}

这失败了,说明'b的寿命不能超过'c

  1. 'c: 'b
impl<'a, 'b> NodeIter<'a, 'b> {
fn next_impl<'c>(&'c mut self) -> Option<&'b mut Obj> where 'c: 'b {
if self.index < self.val.len() {
return Some(self.env.get_obj_at_mut(self.val[self.index]))
}
self.index += 1;
None
}
}

这允许next-impl成功编译,但由于的寿命限制,它不能从next调用

我知道这可能与其他关于Rust寿命的问题类似,但我还没能弄清楚为什么'b的寿命不能超过'c,更普遍地说,如何实现Iterator

我还是Rust的新手,所以如果有任何帮助,将不胜感激

编辑

pub fn get_obj_at_mut(&mut self, index: usize) -> &mut Obj {
&mut self.symbols[index]
}

这是在Envimpl块中,其中symbolsObj的拥有Vec

如果val包含重复索引,那么您尝试的实现可以将两个可变借用保持为相同的Obj值。考虑一下如果val包含一个重复的索引,并且此代码的用户.collect()将迭代器转换为Vec会发生什么!Rust的内存安全规则不允许出现这种情况,这也是next()实现编译失败的最终原因。

事实上,你能想到的任何实现都会遇到这个根本问题:当你仍然持有从上一次调用返回的可变引用时,没有什么能阻止Iterator::next()再次被调用,而且语言不能保证迭代器产生的所有可变引用都会别名不同的值。

那么像iter_mut()这样的切片是如何工作的呢?简单:他们使用不安全的代码。unsafe是你告诉编译器你自己已经完成了工作,以证明你所做的事情没有违反Rust的任何安全规则,因此它可以让你做一些通常不能做的额外事情。

如果您想在不使用不安全代码的情况下执行此操作,则必须交换迭代,以便对Env内部的任何集合调用iter_mut(),并根据Node中的索引对其进行筛选。这显然会影响性能,而且很可能会以不同的顺序进行迭代。

下面是一个完整的例子,展示了如何使用Env:的占位符方法

pub struct Env;
pub struct Obj;
impl Env {
#[allow(unreachable_code)]
pub fn iter_mut(&mut self) -> impl Iterator<Item=&mut Obj> {
todo!();

std::iter::empty()
}
pub fn get_obj_at_mut(&mut self, _: usize) -> &mut Obj {
todo!();
}
}
pub struct Node {
pub val: Vec<usize>,
}
impl Node {
pub fn iter_mut<'a>(&'a self, env: &'a mut Env)
-> impl Iterator<Item=&'a mut Obj>
{
env.iter_mut()
.enumerate()
.filter_map(move |(index, v)|
if self.val.contains(&index) { Some(v) } else { None }
)
}
}

如果这种方法的性能和排序影响是不可接受的,那么不安全的代码是您唯一的选择。特别是,您必须验证val不包含重复索引,否则您将违反Rust的规则,即对任何给定值最多必须有一个可用的可变引用。

如果您在构造NodeIter值之前已经验证了这一点,那么您可以使用不安全的代码来说服Rust您在这里所做的是可以的。请注意,在这段代码中,我还修复了索引从不递增的错误,因为您会提前返回。

impl<'a, 'b> Iterator for NodeIter<'a, 'b> {
type Item = &'b mut Obj;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.val.len() {
let obj = self.env.get_obj_at_mut(self.val[self.index]);
self.index += 1;
// SAFETY: We already verified that self.val contains
// no duplicate indices, and nobody can modify it while
// we hold a reference to it, so it's not possible for
// this iterator to produce two references to the same
// value.
// Convert to a pointer then back, which makes Rust
// blind to the lifetime of the original reference.
Some(unsafe { &mut *(obj as *mut _) })
} else {
None
}
}
}

最新更新