我试图定义一个结构,它可以作为Vec
内RefCell
的{{1}}的迭代器:
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
的结构,它同时包含Ref
(保持不可变的RefCell
借用)和Iter
(保持迭代器) next()
的状态,而不是为Vec
或其他任何容器滚动我自己的迭代器,而第二个是从第一个派生的(并保存从中获得的引用)吗?
我已经尝试了几种方法来实现这一点,并且所有方法都与借用检查器发生冲突。如果我把两个状态都作为裸结构成员,比如
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?)。如果我尝试使用像
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
}
}
然后我招致一个可变的自我借用结构,阻止从iter()
返回它。如果您尝试在结构本身(Why can't I store a value and a reference to that value in the same struct?)中存储对结构的一部分的引用,则也会发生结构的自行借用,这会阻止安全地移动结构的实例。相比之下,它看起来像HoldsVecInRefCellIter
之类的结构,如果你可以完成初始化,那么在移动时会做正确的事情,因为内部的所有引用都是在比其他地方更长的数据。
有一些技巧可以避免使用Rc
创建自引用(请参阅https://internals.rust-lang.org/t/self-referencing-structs/418/3中的示例),但如果您想要存储现有的{我不知道如何应用这些功能{1}}实现的结构,用于保存对底层容器的直接引用,而不是Iterator
。
作为一个来自C ++的Rust新手,这感觉就像一个经常出现的问题(&#34;我在一个代码块中有一些复杂的状态初始化逻辑,我想抽象出那个逻辑并保持在结构中使用状态&#34;)。
答案 0 :(得分:6)
我们必须在生命中作弊和撒谎。
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()
}
}
这只有效,因为移动Iter
时Ref
无效,Ref
指向Vec
,Iter
指向Vec
的存储空间,而不是Ref
本身。
但是,这也可以让vec_iter
移出HoldsVecInRefCellIter
;如果您提取vec_iter
并删除vec_ref
,那么借用将被释放并且Iter
可能无效而没有Rust给出编译器错误('a
是{{1}终身)。通过适当的封装,您可以将结构的内容保密,并避免用户执行此不安全的操作。
顺便说一句,我们也可以定义迭代器来返回引用:
RefCell