如何解决如何从 std::Rc<std::RefCell<T>> 智能指针向量实现迭代器?
我正在尝试了解如何处理内部可变性。这个问题与my previous question密切相关。
我有一个拥有 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>>
}))
}
虽然这行得通,但它使用了不安全的代码,因此必须有一个安全的解决方法。
我设置了 playground 以获得我的应用程序的更多详细信息。应用程序编译并输出预期结果(但使用了不安全的代码)。
解决方法
您不能修改 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!("]");
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。