关于锈:在RefCell中返回Vec的迭代器

Returning iterator of a Vec in a RefCell

给定以下structimpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use std::slice::Iter;
use std::cell::RefCell;

struct Foo {
    bar: RefCell<Vec<u32>>,
}

impl Foo {
    pub fn iter(&self) -> Iter<u32> {
        self.bar.borrow().iter()
    }
}

fn main() {}

我收到有关终身问题的错误消息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
error: borrowed value does not live long enough
  --> src/main.rs:9:9
   |
9  |         self.bar.borrow().iter()
   |         ^^^^^^^^^^^^^^^^^ does not live long enough
10 |     }
   |     - temporary value only lives until here
   |
note: borrowed value must be valid for the anonymous lifetime #1 defined on the body at 8:36...
  --> src/main.rs:8:37
   |
8  |       pub fn iter(&self) -> Iter<u32> {
   |  _____________________________________^ starting here...
9  | |         self.bar.borrow().iter()
10 | |     }
   | |_____^ ...ending here

我如何能够返回并使用bar的迭代器?


您不能执行此操作,因为它可以绕过运行时检查是否存在唯一性违规。

RefCell为您提供了一种"推迟"对运行时进行可变性排他性检查的方法,以交换方式允许通过共享引用对其内部的数据进行突变。这是使用RAII保护实现的:您可以使用对RefCell的共享引用来获取保护对象,然后使用该保护对象访问RefCell内部的数据:

1
2
3
&'a RefCell< T >        -> Ref<'a, T> (with borrow) or RefMut<'a, T> (with borrow_mut)
&'b Ref<'a, T>        -> &'b T
&'b mut RefMut<'a, T> -> &'b mut T

此处的关键点是'b'a不同,后者允许在不具有&mut引用RefCell的情况下获取&mut T引用。但是,这些引用将链接到防护对象,并且生存期不能超过防护对象。这是有意完成的:RefRefMut析构函数在其RefCell中切换各种标志,以强制进行可变性检查,并在这些检查失败时强制borrow()borrow_mut()紧急。

您可以做的最简单的事情是返回Ref周围的包装器,该包装器将实现IntoIterator的引用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use std::cell::Ref;

struct VecRefWrapper<'a, T: 'a> {
    r: Ref<'a, Vec< T >>
}

impl<'a, 'b: 'a, T: 'a> IntoIterator for &'b VecRefWrapper<'a, T> {
    type IntoIter = Iter<'a, T>;
    type Item = &'a T;

    fn into_iter(self) -> Iter<'a, T> {
        self.r.iter()
    }
}

(在操场上尝试)

您不能直接为VecRefWrapper实现IntoIterator,因为内部的Ref将被into_iter()占用,从而给您基本上与现在相同的情况。