如何在结构体中使用Rc<RefCell<T>>数据类型?

3

我试图为学习目的实现一个链表。主要使用std::cell::RefCell和stc::rc::{Rc,Weak}将数据存储到链表实例中。现在我正在实现 fn pop ,它消耗并返回列表中第一个位置上的值,但我不知道如何消耗包装在Rc和RefCell中的值。

以下是我的代码:

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

#[derive(Debug)]
pub struct DbNode<T> {
    data: T,
    next: Option<Rc<RefCell<DbNode<T>>>>,
    prev: Option<Weak<RefCell<DbNode<T>>>>,
}

#[derive(Debug)]
pub struct DbList<T> {
    first: Option<Rc<RefCell<DbNode<T>>>>,
    last: Option<Weak<RefCell<DbNode<T>>>>,
}

pub fn push_front(&mut self, data: T) {
    match self.first.take() {
        Some(e) => {
            let new_front = Rc::new(RefCell::new(DbNode {
                data,
                next: Some(e.clone()),
                prev: None,
            }));
            let mut me = e.borrow_mut();
            me.prev = Some(Rc::downgrade(&new_front));
            self.first = Some(new_front);
        },
        None => {
            let new_data = Rc::new(RefCell::new(DbNode {
                data,
                next: None,
                prev: None,
            }));
            self.last = Some(Rc::downgrade(&new_data));
            self.first = Some(new_data);
        },
    }
}

pub fn push_back(&mut self, data: T) {
    match self.last.take() {
        Some(l) => {
            let new_back = Rc::new(RefCell::new(DbNode {
                data,
                next: None,
                prev: Some(l.clone()),
            }));
            let st = Weak::upgrade(&l).unwrap();
            let mut ml = st.borrow_mut();
            self.last = Some(Rc::downgrade(&new_back));
            ml.next = Some(new_back);
        },
        None => {
            let new_data = Rc::new(RefCell::new(DbNode {
                data,
                next: None,
                prev: None,
            }));
            self.last = Some(Rc::downgrade(&new_data));                
            self.first = Some(new_data);
        },
    }
}

pub fn pop(&mut self) -> Option<T> {
    match self.first.take() {
        Some(f) => {
            // How can I??
            // let result = Some(f.into_inner().data);
            // result
        },
        None => None,
    }
}

我想要实现的是返回位于 struct DbList 的 'first' 中的 struct DbNode 内部的 'data' 值,并将其设置为 'None',如果 'next' 为 None,则将数据所在的 'first' 设置为 'None',否则将 'next' 设置为 'first'。 起初,我尝试使用 Rc::downcast 消耗内部值,但匹配块中 'f' 的类型为 'RefCell' 而不是 'Rc',然后我尝试使用 RefCell::into_inner(),但编译器显示:

无法移动出 Rc 移动发生是因为值具有类型为 std::cell::RefCell<ds::dll::DbNode<T>>,它没有实现 Copy trait

我完全理解这是什么意思,但我不知道该怎么做。正确的方法是什么?
1个回答

6
你离正确答案很近了。可以使用Rc::try_unwrap()方法:

如果 Rc 只有一个强引用,返回内部值;否则,返回带有传入的相同 RcErr。即使有未解除的弱引用,此操作也会成功。

pub fn pop(&mut self) -> Option<T> {
    match self.first.take() {
        Some(f) => {
            match Rc::try_unwrap(f) {
                Ok(refcell) => {
                    // you can do refcell.into_inner here
                    Some(refcell.into_inner().data)
                },
                Err(_) => {
                    // another Rc still exists so you cannot take ownership of the value
                    None
                }
            }
        },
        None => None,
    }
}

它有效了,非常感谢!我有一个额外的问题:在选项匹配中如何获取Rc而不是RefCell?据我所知,它是RefCell因为它被自动解引用。如果我想要获取Rc,我该怎么做? - ParkCheolu
@ParkCheolu 我不明白,这个示例中的 f 是一个 Rc - kmdreko
真的吗?如果是这样,那我的Visual Studio就有点奇怪了。当我在"f"后面输入句点时,IDE显示的是RefCell的方法列表,而不是Rc的列表... - ParkCheolu
这是由于解引用,f 是一个 Rc 但是 Rc 实现了 Deref 来提供对其内部值的直接访问,从而访问它的字段和方法。编译器会透明地完成此操作。这也适用于 Box 和甚至 Vec - kmdreko
1
我明白了。你是正确的。我刚刚在文档中检查了Rc::try_unwrap,它只接受Rc。所以,“f是Rc,因为有Deref,我可以将其视为Rc和其内部值,这样说是正确的吗? - ParkCheolu

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