我正在尝试为我的一个结构实现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
所以我尝试了两种选择:
'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
'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]
}
这是在Env
的impl
块中,其中symbols
是Obj
的拥有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
}
}
}