Rust,如何从 Rc<RefCell<T>> 中复制内部值并返回它?

Rust, how to copy the inner value out from Rc<RefCell<T>> and return it?

提问人:Saplyn 提问时间:8/24/2023 更新时间:8/24/2023 访问量:98

问:

简介:我是 Rust 的新手,所以我决定通过实现双链表来练习。出于调试目的,我实现了该方法,但未能从 .(对不起,问了愚蠢的问题)get()Rc<RefCell<_>>

问题:我正在尝试返回一个,其中 是存储在节点中的数据类型,是错误消息字符串。借用检查器告诉我,我无法返回对方法内变量的引用,因此我尝试复制内部值并返回它,但未能成功。Result<T, &'static str>.get()T&str

源代码

use std::{rc::Rc, cell::RefCell};

struct Node<T> {
    data: Option<T>,
    prev: Option<Rc<RefCell<Node<T>>>>,
    next: Option<Rc<RefCell<Node<T>>>>,
}

impl<T> Node<T> {
    /// Instantiate a new dummy node.
    /// This node is used to mark the start and end of the list.
    /// It is not counted in the size of the list.
    fn new() -> Self {
        Node {
            data: None,
            prev: None,
            next: None,
        }
    }
    /// Instantiate a new content node.
    /// This node is used to store data.
    /// It is counted in the size of the list.
    fn from(data: T) -> Self {
        Node {
            data: Some(data),
            prev: None,
            next: None,
        }
    }
}

struct List<T> {
    head: Rc<RefCell<Node<T>>>,
    tail: Rc<RefCell<Node<T>>>,
    size: usize,
}

impl<T> List<T> {
    pub fn new() -> Self {
        let head = Rc::new(RefCell::new(Node::new()));
        let tail = Rc::new(RefCell::new(Node::new()));
        head.borrow_mut().next = Some(Rc::clone(&tail));
        tail.borrow_mut().prev = Some(Rc::clone(&head));
        List { head, tail, size: 0 }
    }
    pub fn prepend(&self, data: T) {
        let node = Rc::new(RefCell::new(Node::from(data)));
        let mut head = self.head.borrow_mut();
        
        node.borrow_mut().next = Some(head.next.take().unwrap());
        node.borrow_mut().prev = Some(Rc::clone(&self.head));
        head.next = Some(Rc::clone(&node));
        if let Some(next) = node.borrow().next.as_ref() {
            next.borrow_mut().prev = Some(Rc::clone(&node));
        };
    }
    pub fn append(&self, data: T) {
        let node = Rc::new(RefCell::new(Node::from(data)));
        let mut tail = self.tail.borrow_mut();
        
        node.borrow_mut().prev = Some(Rc::clone(&tail.prev.take().unwrap()));
        node.borrow_mut().next = Some(Rc::clone(&self.tail));
        tail.prev = Some(Rc::clone(&node));
        if let Some(prev) = node.borrow().prev.as_ref() {
            prev.borrow_mut().next = Some(Rc::clone(&node));
        };
    }
    pub fn get(&self, index: isize) -> Result<T, &'static str> {
        let mut current: Rc<RefCell<Node<T>>> = Rc::clone(self.head.borrow().next.as_ref().unwrap());
        for _ in 0..index {
            let tmp = Rc::clone(current.borrow().next.as_ref().ok_or("Index out of range")?);
            current = tmp;
        }
        let result = current.borrow().data.as_ref().ok_or("Index out of range")?;  // error[E0716]
        Ok(*result)  // error[E0507]
    }
}
/*
error[E0716]: temporary value dropped while borrowed
  --> src\linked.rs:74:22
   |
74 |         let result = current.borrow().data.as_ref().ok_or("Index out of range")?;
   |                      ^^^^^^^^^^^^^^^^                                           - temporary value is freed at the end of this statement
   |                      |
   |                      creates a temporary value which is freed while still in use
75 |         Ok(*result)
   |            ------- borrow later used here
   |
help: consider using a `let` binding to create a longer lived value
   |
74 ~         let binding = current.borrow();
75 ~         let result = binding.data.as_ref().ok_or("Index out of range")?;
   |

error[E0507]: cannot move out of `*result` which is behind a shared reference
  --> src\linked.rs:75:12
   |
75 |         Ok(*result)
   |            ^^^^^^^ move occurs because `*result` has type `T`, which does not implement the `Copy` trait
*/

我试过了

另外:我可能做错了这些,如果其中一个帖子解决了我的问题,但我没有以正确的方式实施它,请原谅我,请教我如何正确地做到这一点。非常感谢。

Rust 借用检查器 refcell

评论

8赞 Chayim Friedman 8/24/2023
“我是 Rust 的新手,所以我决定通过实现双链表来练习”到此为止。这是个坏主意。不要通过实现链表来练习 Rust,对于双链表来说更是如此。这不会教你惯用的或好的 Rust,这只会让你感到困惑。
2赞 Toerktumlare 8/24/2023
阅读此 rust-unofficial.github.io/too-many-lists
3赞 Chayim Friedman 8/24/2023
如果你必须,或者在你已经掌握了 Rust 之后,你想挑战(是的,挑战!这是一个很难的问题)自己构建这个数据结构,一定要阅读 Learn Rust with Totally Too Many Linked Lists
1赞 Chayim Friedman 8/24/2023
尽管如此,这是一个很好的帖子,值得点赞,因为你做了你的研究。干得好!

答:

3赞 Chayim Friedman 8/24/2023 #1

你没有。

如果值为 ,则可以复制它。如果是,则可以克隆它。但如果不是,或者克隆成本太高了?CopyClone

你不走运。

内部可变性(如)对 API 不利。它往往会跨 API 边界传播,有时甚至会完全阻止您实现它们。它(有时)在实现内部很好,但不是因为它泄漏了。RefCell

当然,有一种正确的方法可以实现双链表(因为 std 有一个),但这种正确的方法需要不安全的代码。当你已经掌握了 Rust 时,你可能想回到它,作为一种练习。请务必阅读 Learn Rust with Totally Too Many Linked Lists

1赞 Jason Orendorff 8/24/2023 #2

这对我有用:

    pub fn get(&self, index: isize) -> Result<T, &'static str>
        where T: Clone
    {
        let mut current: Rc<RefCell<Node<T>>> = Rc::clone(self.head.borrow().next.as_ref().unwrap());
        for _ in 0..index {
            let tmp = Rc::clone(current.borrow().next.as_ref().ok_or("Index out of range")?);
            current = tmp;
        }
        let guard = current.borrow();
        guard.data.clone().ok_or("Index out of range")
    }

(游乐场链接)

您需要该行来启用该方法。where T: Clone.clone()


有人说,为了学习 Rust 而实现双链表是错误的......我很抱歉地报告他们是对的。当我开始的时候,我做了完全相同的事情。我尝试的另一件事是编写光线追踪器;这好多了。

核心数据结构是使用原始指针实现的。这意味着编写不安全的代码并围绕它放置一个安全的 API:一种高级 Rust 技能。