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

如何解决如何从 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 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?