冲突的生命周期——埋入的内容无法比容器长寿

Conflicting Lifetimes - Burrowed Content is Unable to Outlive Container

我正在尝试为我的一个结构实施 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
  --> src\core\nodes.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...
  --> src\core\nodes.rs:19:13
   |
19 |     fn next(&mut self) -> Option<&'a mut Obj> {
   |             ^^^^^^^^^
note: ...so that reference does not outlive borrowed content
  --> src\core\nodes.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...
  --> src\core\nodes.rs:16:6
   |
16 | impl<'a> Iterator for NodeIter<'a> {
   |      ^^
note: ...so that the types are compatible
  --> src\core\nodes.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(returns 一个生命周期为 '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()在你仍然持有可变引用 return 来自先前的调用,并且该语言不能保证迭代器生成的所有可变引用都将别名为不同的值。

那么切片上的 iter_mut() 是如何工作的呢?简单:they use unsafe codeunsafe 是你告诉编译器你自己完成了工作以证明你正在做的事情没有违反 Rust 的任何安全规则的地方,因此它允许你做一些你不能做的额外事情通常。

如果你想在不使用不安全代码的情况下做到这一点,你将不得不交换迭代,这样你就可以调用 iter_mut() 对你的 Env 内部的任何集合进行过滤根据 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,您在这里所做的是可以的。请注意,在这段代码中,我还修复了索引永远不会递增的错误,因为你 return 早了。

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
        }
    }
}