如何从 std::Rc<std::RefCell>智能指针的向量实现迭代器<T>?



我正在努力了解如何处理内部可变性。这个问题与我之前的问题密切相关。

我有一个通用结构Port<T>,它拥有一个Vec<T>。我们可以";链";端口B到端口A,因此,当读取端口A的内容时,我们能够读取端口B的内容。然而,这种链接对端口A的读取器是隐藏的。这就是我实现iter(&self)方法的原因:

use std::rc::Rc;
pub struct Port<T> {
values: Vec<T>,
ports: Vec<Rc<Port<T>>>,
}
impl <T> Port<T> {
pub fn new() -> Self {
Self { values: vec![], ports: vec![] }
}
pub fn add_value(&mut self, value: T) {
self.values.push(value);
}
pub fn is_empty(&self) -> bool {
self.values.is_empty() && self.ports.is_empty()
}
pub fn chain_port(&mut self, port: Rc<Port<T>>) {
if !port.is_empty() {
self.ports.push(port)
}
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
self.values.iter().chain(
self.ports.iter()
.flat_map(|p| Box::new(p.iter()) as Box<dyn Iterator<Item = &T>>)
)
}
pub fn clear(&mut self) {
self.values.clear();
self.ports.clear();
}
}

应用程序具有以下伪代码行为:

  • 创建端口
  • 循环:
    • 用值填充端口
    • 链式端口
    • 迭代端口的值
    • 清除端口

main函数应该如下所示:

fn main() {
let mut port_a = Rc::new(Port::new());
let mut port_b = Rc::new(Port::new());
loop {
port_a.add_value(1);
port_b.add_value(2);
port_a.chain_port(port_b.clone());
for val in port_a.iter() {
// read data
};
port_a.clear();
port_b.clear();
}
}

然而,编译器抱怨道:

error[E0596]: cannot borrow data in an `Rc` as mutable
--> src/modeling/port.rs:46:9
|
46 |         port_a.add_value(1);
|         ^^^^^^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<Port<i32>>`

我已经读了好几篇文章等,似乎我需要使用Rc<RefCell<Port<T>>>才能更改端口。我更改了Port<T>:的实现

use std::cell::RefCell;
use std::rc::Rc;
pub struct Port<T> {
values: Vec<T>,
ports: Vec<Rc<RefCell<Port<T>>>>,
}
impl<T> Port<T> {
// snip
pub fn chain_port(&mut self, port: Rc<RefCell<Port<T>>>) {
if !port.borrow().is_empty() {
self.ports.push(port)
}
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
self.values.iter().chain(
self.ports
.iter()
.flat_map(|p| Box::new(p.borrow().iter()) as Box<dyn Iterator<Item = &T>>),
)
}
// snip
}

这也不编译:

error[E0515]: cannot return value referencing temporary value
--> src/modeling/port.rs:35:31
|
35 |                 .flat_map(|p| Box::new(p.borrow().iter()) as Box<dyn Iterator<Item = &T>>),
|                               ^^^^^^^^^----------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|                               |        |
|                               |        temporary value created here
|                               returns a value referencing data owned by the current function

我想我知道问题出在哪里了:p.borrow()返回了对被链接端口的引用。我们使用该引用来创建迭代器,但函数一完成,引用就超出范围,迭代器就不再有效。

我不知道该怎么处理。我设法实施了以下不安全的方法:

pub fn iter(&self) -> impl Iterator<Item = &T> {
self.values.iter().chain(self.ports.iter().flat_map(|p| {
Box::new(unsafe { (&*p.as_ref().as_ptr()).iter() }) as Box<dyn Iterator<Item = &T>>
}))
}

虽然这是有效的,但它使用了不安全的代码,并且必须有一个安全的解决方法。

我为我的申请设置了一个游乐场。应用程序编译并输出预期结果(但使用不安全的代码(。

您不能修改Rc后面的任何内容,这是正确的。虽然这可能通过RefCell来解决,但您不想走这条路。你可能会遇到这样的情况,你需要执行特定的clean()命令或类似的恐怖行为。

更重要的是:从根本上讲,你的主体是有缺陷的。采取以下路线:

let mut port_a = Port::new();
let mut port_b = Port::new();
loop {
// Creates an iummutable borrow of port_b with same lifetime as port_a!
port_a.chain_port(port_b);
// ...
// A mutable borrow of port_b.
// But the immutable borrow from above persists across iterations.
port_b.clear();
// Or, even if you do fancy shenanigans at least until this line.
port_a.clear();
}

要克服这个问题,只需将端口的生存期限制为一次迭代。无论如何,您目前都是手动清理它们的,所以这已经是您在概念上所做的了。

此外,我去掉了递归迭代,只是为了稍微简化一些。

#[derive(Clone)]
pub struct Port<'a, T> {
values: Vec<T>,
ports: Vec<&'a Port<'a, T>>,
}
impl<'a, T> Port<'a, T> {
pub fn new() -> Self {
Self {
values: vec![],
ports: vec![],
}
}
pub fn add_value(&mut self, value: T) {
self.values.push(value);
}
pub fn is_empty(&self) -> bool {
self.values.is_empty() && self.ports.is_empty()
}
pub fn chain_port(&mut self, port: &'a Port<T>) {
if !port.is_empty() {
self.ports.push(&port)
}
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
let mut port_stack: Vec<&Port<T>> = vec![self];
// Sensible estimate I guess.
let mut values: Vec<&T> = Vec::with_capacity(self.values.len() * (self.ports.len() + 1));
while let Some(port) = port_stack.pop() {
values.append(&mut port.values.iter().collect());
port_stack.extend(port.ports.iter());
}
values.into_iter()
}
}
fn main() {
loop {
let mut port_a = Port::new();
let mut port_b = Port::new();
port_a.add_value(1);
port_b.add_value(2);
port_a.chain_port(&port_b);

print!("values in port_a: [ ");
for val in port_a.iter() {
print!("{} ", val);
}
println!("]");
}
}

相关内容

最新更新