如何获取一个指向由rc::Weak<RefCell<T>>指向的节点的rc :: Ref <T>引用?

3

我正在编写一个链表,其中有弱引用链接到前一个节点。

use std::cell::{Ref, RefCell, RefMut};
use std::rc::{Rc, Weak};

pub struct List<T> {
    head: NextLink<T>,
    tail: PrevLink<T>,
}

type NextLink<T> = Option<Rc<RefCell<Node<T>>>>;
type PrevLink<T> = Weak<RefCell<Node<T>>>;

struct Node<T> {
    elem: T,
    prev: PrevLink<T>,
    next: NextLink<T>,
}

impl<T> Node<T> {
    fn new(elem: T) -> Rc<RefCell<Self>> {
        Rc::new(RefCell::new(Self {
            elem,
            prev: Weak::new(),
            next: None,
        }))
    }
}

impl<T> List<T> {
    pub fn new() -> Self {
        Self {
            head: None,
            tail: Weak::new(),
        }
    }

    // add to tail
    pub fn push(&mut self, elem: T) {
        let new_node = Node::new(elem);
        match self.tail.upgrade().take() {
            Some(old_tail) => {
                new_node.borrow_mut().prev = Rc::downgrade(&old_tail);
                old_tail.borrow_mut().next = Some(new_node);
            }
            None => {
                self.tail = Rc::downgrade(&new_node);
                self.head = Some(new_node);
            }
        }
    }

    pub fn pop(&mut self) -> Option<T> {
        self.tail.upgrade().map(|old_tail| {
            match old_tail.borrow_mut().prev.upgrade() {
                Some(new_tail) => {
                    new_tail.borrow_mut().next = None;
                    self.tail = Rc::downgrade(&new_tail);
                }
                None => {
                    self.head.take();
                    self.tail = Weak::new();
                }
            };
            Rc::try_unwrap(old_tail).ok().unwrap().into_inner().elem
        })
    }

    // add to head
    pub fn unshift(&mut self, elem: T) {
        let new_node = Node::new(elem);
        match self.head.take() {
            Some(old_head) => {
                old_head.borrow_mut().prev = Rc::downgrade(&new_node);
                new_node.borrow_mut().next = Some(old_head);
            }
            None => {
                self.tail = Rc::downgrade(&new_node);
                self.head = Some(new_node);
            }
        }
    }

    pub fn shift(&mut self) -> Option<T> {
        self.head.take().map(|old_head| {
            match old_head.borrow_mut().next.take() {
                Some(new_head) => {
                    new_head.borrow_mut().prev = Weak::new();
                    self.head = Some(new_head);
                }
                None => {
                    self.tail = Weak::new();
                }
            };
            Rc::try_unwrap(old_head).ok().unwrap().into_inner().elem
        })
    }

    pub fn peek_head(&self) -> Option<Ref<T>> {
        self.head
            .as_ref()
            .map(|node| Ref::map(node.borrow(), |node| &node.elem))
    }

    pub fn peek_tail(&self) -> Option<Ref<T>> {
        self.tail
            .upgrade()
            .map(|node| Ref::map(node.borrow(), |node| &node.elem))
    }

    pub fn peek_head_mut(&mut self) -> Option<RefMut<T>> {
        self.head
            .as_ref()
            .map(|node| RefMut::map(node.borrow_mut(), |node| &mut node.elem))
    }

    pub fn peek_tail_mut(&mut self) -> Option<RefMut<T>> {
        unimplemented!()
    }
}

impl<T> Drop for List<T> {
    fn drop(&mut self) {
        while let Some(old_head) = self.head.take() {
            self.head = old_head.borrow_mut().next.take();
        }
    }
}

出现了编译错误:

error[E0515]: cannot return value referencing function parameter `node`
   --> src/lib.rs:106:25
    |
106 |             .map(|node| Ref::map(node.borrow(), |node| &node.elem))
    |                         ^^^^^^^^^----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |                         |        |
    |                         |        `node` is borrowed here
    |                         returns a value referencing data owned by the current function

当尾节点的弱引用升级为 Rc 时,这个 Rc 是由此函数拥有的,并将在最后使用。

我该如何解决这个问题?


在安全的 Rust 中编写双向链表几乎是不可能的(我可能错了,我们有 Pin API,但我无法想象它有多么有用或如何实现它)。我建议要么考虑其他方法,要么使用原始指针,就像在其他本质上不安全的语言中一样;与普遍的看法相反,链表并不是第一次就容易正确地实现的,而且你不会每天都写链表。标准库中有一个用这种方式实现的实现。https://doc.rust-lang.org/src/alloc/collections/linked_list.rs.html#38 - Yamirui
3
使用弱引用可以让你在Rust中安全地编写双向链表,因此你的评论并不是非常相关。参见如何在Rust中安全地表示互递归数据结构? - Shepmaster
1
@Shepmaster并不比为了证明Rust的安全性而存在的无意义开销更不相关。每个相关的数据结构都是从底层开始构建的,而且永远都是这样。Unsafe是一个有效的选项,而不是你总是避免的幽灵。关于你链接的内容,你是故意忽略“想出其他办法”吗?因为Arena就是“其他办法”。如果有什么不相关的东西,那就是你对我的评论。你可以指出“其他办法”给提问者,而不用告诉我我的相关评论是不相关的。 - Yamirui
1个回答

5

你的问题

使用 Weak::upgradeRefCell::borrow 方法:

fn example<T>(foo: Weak<RefCell<T>>) {
    let strong: Rc<RefCell<T>> = foo.upgrade().unwrap();
    let the_ref: Ref<T> = strong.borrow();
}

你的问题

self.head.as_ref() 返回一个Option<&Rc<_>>self.tail.upgrade() 返回一个Option<Rc<_>>。当你调用 Option::map 时,内部值的所有权被转移到闭包中,你会得到你发布的错误。你可以调用Option::as_ref,但是Option由函数拥有,因此不允许返回引用,因为一旦Rc被销毁,引用就不再有效。

你唯一能做的事情就是从函数中返回Rc

另请参见:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接