我试图定义一个 struct ,它可以作为RefCell中包含的Vec的迭代器:

use std::slice::Iter;
use std::cell::Ref;
use std::cell::RefCell;

struct HoldsVecInRefCell {
    vec_in_refcell: RefCell<Vec<i32>>,
}

// TODO: struct HoldsVecInRefCellIter implementing Iterator ...

impl HoldsVecInRefCell {
    fn new() -> HoldsVecInRefCell {
        HoldsVecInRefCell { vec_in_refcell: RefCell::new(Vec::new()) }
    }

    fn add_int(&self, i: i32) {
        self.vec_in_refcell.borrow_mut().push(i);
    }

    fn iter(&self) -> HoldsVecInRefCellIter {
        // TODO ...
    }
}

fn main() {
    let holds_vec = HoldsVecInRefCell::new();
    holds_vec.add_int(1);
    holds_vec.add_int(2);
    holds_vec.add_int(3);

    let mut vec_iter = holds_vec.iter();  // Under the hood: run-time borrow check

    for i in vec_iter {
        println!("{}", i);
    }
}

相比之下,vec_iter可以在main()中在线初始化,如下所示(故意冗长):

// Elided: lifetime parameter of Ref
let vec_ref: Ref<Vec<i32>> = holds_vec.vec_in_refcell.borrow();
// Elided: lifetime parameter of Iter
let mut vec_iter: Iter<i32> = vec_ref.iter();

当第二个容器从第一个容器派生(并保存从第一个容器获得的引用)时,有没有办法定义一个实现Iterator的 struct ,该 struct 同时保存Ref(保持不变的RefCellborrow 活动)和Iter(保持next()的迭代器状态,而不是滚动我自己的Vec迭代器或任何其他容器)?

我try 了几种方法来实现这一点,但都与借阅判断器相冲突.如果我把两个状态都作为裸 struct 成员,比如

struct HoldsVecInRefCellIter<'a> {
    vec_ref: Ref<'a, Vec<i32>>,
    vec_iter: Iter<'a, i32>,
}

然后我不能用HoldsVecInRefCellIter { ... }语法同时初始化这两个字段(例如,参见Does Rust have syntax for initializing a struct field with an earlier field?).如果我try 用类似

struct HoldsVecInRefCellIter<'a> {
    vec_ref: Ref<'a, Vec<i32>>,
    vec_iter: Option<Iter<'a, i32>>,
}

// ...

impl HoldsVecInRefCell {
    // ...

    fn iter(&self) -> HoldsVecInRefCellIter {
        let mut new_iter = HoldsVecInRefCellIter { vec_ref: self.vec_in_refcell.borrow(), vec_iter: None };
        new_iter.vec_iter = new_iter.vec_ref.iter();
        new_iter
    }
}

then I incur a mutable self-borrow of the struct that prevents returning it from iter(). This self-borrowing of a struct can also happen if you try to store a reference to one part of a struct in the struct itself (Why can't I store a value and a reference to that value in the same struct?), which would prevent safely moving instances of the struct. By comparison, it seems like a struct like HoldsVecInRefCellIter, if you could complete initialization, would do the correct thing when moved, since all references internally are to data elsewhere that outlives this struct.

有一些技巧可以避免使用Rc创建自引用(参见第https://internals.rust-lang.org/t/self-referencing-structs/418/3页的示例),但如果您想存储现有的Iterator struct ,而该 struct 的实现是为了保存对底层容器的直接引用,而不是Rc,我不知道如何应用这些技巧.

作为来自C++的 rust 新手,这感觉起来像是一个经常出现的问题("我在代码块中有一些复杂的状态初始化逻辑,我想抽象掉那个逻辑,并将结果状态保存在 struct 中以供使用)).

Related Question: Returning iterator of a Vec in a RefCell

推荐答案

我们将不得不在一生中欺骗和撒谎.

use std::mem;

struct HoldsVecInRefCellIter<'a> {
    vec_ref: Ref<'a, Vec<i32>>,
    vec_iter: Iter<'a, i32>, // 'a is a lie!
}

impl HoldsVecInRefCell {
    fn iter(&self) -> HoldsVecInRefCellIter {
        unsafe {
            let vec_ref = self.vec_in_refcell.borrow();
            // transmute changes the lifetime parameter on the Iter
            let vec_iter = mem::transmute(vec_ref.iter());
            HoldsVecInRefCellIter { vec_ref: vec_ref, vec_iter: vec_iter }
        }
    }
}

impl<'a> Iterator for HoldsVecInRefCellIter<'a> {
    type Item = i32;

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

这只是因为移动Ref不会使Iter失效,因为Ref指向VecIter指向Vec的存储器,而不是Ref本身.

然而,这也能让你从HoldsVecInRefCellIter中移出vec_iter;如果提取vec_iter并删除vec_ref,那么借阅将被释放,Iter可能会失效,而不会出现编译器错误('aRefCell的生命周期).通过适当的封装,可以使 struct 的内容保持私有,避免用户执行这种不安全的操作.

顺便说一下,我们也可以定义迭代器来返回引用:

impl<'a> Iterator for HoldsVecInRefCellIter<'a> {
    type Item = &'a i32;

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

Rust相关问答推荐

在Tauri中获取ICoreWebView 2_7以打印PDF

为什么是!为Rust中的RwLockReadGuard和RwLockWriteGuard实现的发送特征?

什么样的 struct 可以避免使用RefCell?

创建包含缺失值的框架

下载压缩文件

如何在Bevy/Rapier3D中获得碰撞机的计算质量?

如何计算迭代器适配器链中过滤的元素的数量

程序在频道RX上挂起

如何实现Deref;多次;?

在 Rust 中用问号传播错误时对类型转换的困惑?

是否可以在不直接重复的情况下为许多特定类型实现一个函数?

Rust 中的静态引用

从 rust 函数返回 &HashMap

使用占位符获取用户输入

Rust Option 的空显式泛型参数

为什么我可以使用 &mut (**ref) 创建两个实时 &mut 到同一个变量?

没有分号的返回表达式的性能是否比使用返回更好?在Rust ?

为什么这个 Trait 无效?以及改用什么签名?

为什么 u64::trailing_zeros() 在无分支工作时生成分支程序集?

加入动态数量的期货