gpt4 book ai didi

rust - 如何解开由Weak::upgrade获得的具有多个强引用的Rc?

转载 作者:行者123 更新时间:2023-12-03 11:42:20 26 4
gpt4 key购买 nike

我正在尝试实现一个链表,以供学习之用,而我现在实现的功能是 fn pop_back ,它可以在链表实例的最后一个位置弹出数据。 RefCell,Rc和Weak主要用于将数据存储到列表中。
这是我的代码( fn pop_back 在代码底部):

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>>>>,
}

impl<T> DbList<T> {

pub fn new() -> Self {
DbList {
first: None,
last: None,
}
}

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_front(&mut self) -> Option<T> {
match self.first.take() {
Some(first) => {
match Rc::try_unwrap(first) {
Ok(refc) => {
let inner = refc.into_inner();
self.first = inner.next;
if let None = self.first {
self.last = None;
};
Some(inner.data)
},
Err(_) => None,
}
},
None => None,
}
}

pub fn pop_back(&mut self) -> Option<T> {
match self.last.take() {
Some(last) => {
// todo: try_unwrap goes to err: the 'prev' in the 'last' holding the reference of this?
match Rc::try_unwrap(Weak::upgrade(&last).unwrap()) {
Ok(refc) => {
let inner = refc.into_inner();
self.last = inner.prev;
Some(inner.data)
},
Err(_) => None,
}
},
None => None,
}
}
}
如果可能, fn pop_back 应该返回最后一个DbNode,并将现有“last”的上一个DbNode中的“next” DbNode设置为“new last”。因此,我需要拆开现有的包装以获得先前的DbNode,这将是新的DbNode。但是Rc::try_unwrap转到Err。我猜这是因为现有的鞋last本身是弱的,所以它已经有一个强引用,并且引用计数随着 Rc::try_unwrap(Weak::upgrade(&last).unwrap())的增加而增加。我不确定我的猜测是否100%。
我该怎么办?
完全的
我是根据@Masklinn建议的方法完成此操作的。除了该方法外,在 Rc::try_unwrap 之前,我只需要删除列表中仅剩一个DbNode的DbList中“第一”字段保留的Rc,因为“上一个”字段具有 时列表中只剩下一个DbNode。它遇到了我第一个遇到的相同错误,而没有删除“第一个”。
这是关于 fn pop_back 的最终代码:
pub fn pop_back(&mut self) -> Option<T> {
match self.last.take() {
Some(last) => {
let last = Weak::upgrade(&last).unwrap();
if Rc::ptr_eq(self.first.as_ref().unwrap(), &last) {
self.first = None;
} else {
let prev = Weak::upgrade(last.borrow().prev.as_ref().unwrap());
prev.as_ref().unwrap().borrow_mut().next = None;
self.last = Some(Rc::downgrade(prev.as_ref().unwrap()));
}
match Rc::try_unwrap(last) {
Ok(iv) => Some(iv.into_inner().data),
Err(_) => None,
}
},
None => None,
}
}

最佳答案

I guess this is because the existing last itself is an Weak, so it already has one strong reference and the reference count increases


这种使用弱引用的方案确实很奇怪(这并不是真正的目的),但是您需要做的是:
  • 来自last()
  • 升级last(),创建第二个Rc
  • 跟随其prev()链接到达倒数第二个
  • 将其设置为新的最后
  • 是倒数第二个,即第一个/现有的Rc
  • 删除升级后的值或从倒数第二个得到的值,它们都是前last的强引用,这将为您提供refcount 1
  • 这意味着您可以将其解开

  • 您不能在最后一步之前打开Rc的包装,因为在此之前它将始终有两个强引用。
    与此类似:
        pub fn pop_back(&mut self) -> Option<T> {
    self.last.take().and_then(|last| {
    // get a strong reference of last
    let last = Weak::upgrade(&last).expect("Last element of the list was already deallocated.");
    // get a strong reference of second-to-last
    let prev = last.borrow().prev.as_ref().and_then(Weak::upgrade).expect("Previous-to-last element of the list was already deallocated");
    // remove the old last from the new last
    prev.borrow_mut().next = None;
    // set second to last as the new last
    self.last = Some(Rc::downgrade(&prev));
    // extract the old last's payload and return it
    Rc::try_unwrap(last).ok().map(|v| v.into_inner().data)
    })
    }
    我交换了设置新的最后一个并更新其下一个的原因,因为那样,所有必须成功的事物(仅在数据结构处于不连贯状态时才会出现 panic )将在我们执行第一个新变异之前运行。然后,我们使用不应该失败的方式进行所有变异,因此我们应该没有“ panic 症”。
    我也只是直接将其 next设置为 None,因为我们在最后已经有很强的引用文献,所以我们只关心旧的被销毁。

    关于rust - 如何解开由Weak::upgrade获得的具有多个强引用的Rc?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66258586/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com