在 Rust 中,Weak<T> 如何知道内部值何时被丢弃?

6

std::rc::Weak<T> 的定义如下:

pub struct Weak<T: ?Sized> {
    ptr: NonNull<RcBox<T>>,
}

在我的理解中,当没有剩余的Rc<T>时,RcBox<T>将被释放,Weak<T>.ptr现在指向一个可能包含任何内容的位置。因此,当在Weak<T>上调用upgrade()时,它如何知道指针现在无效? Weak<T>::upgrade的实现如下:
    pub fn upgrade(&self) -> Option<Rc<T>> {
        let inner = self.inner()?;

        if inner.strong() == 0 {
            None
        } else {
            unsafe {
                inner.inc_strong();
                Some(Rc::from_inner(self.ptr))
            }
        }
    }

这是不是意味着当野指针中没有 Rc<T> 剩余时,RcBox<T> 实际上并没有被释放?如果是的话,那不会造成内存泄漏吗?

1个回答

11

RcBox 在没有强引用或弱引用时才会被销毁。

然而,当强引用计数为零时,包含的 T被放置。这实际上销毁了包含的 T,但不会释放 T 本身的内存。这就是为什么 Weak 知道内部值是否已被丢弃 -- 如果在强引用计数为零时尝试将 Weak 升级为 Rc,则操作失败并返回 None

一旦弱引用计数也达到零,RcBox 本身就会被释放。所以,没有内存泄漏。

请注意,许多类型(例如StringVec)管理单独的堆分配。例如,如果您有一个Rc<Vec<_>>,当强引用计数为零但弱引用计数不为零时,内部Vec的删除代码将运行,并且会删除所有拥有的元素以及释放用于存储它们的堆分配。Vec本身用于保存堆指针、长度和容量的内存不会被释放,因为它是由RcBox拥有的。只有在强引用计数和弱引用计数都为零时才会释放该分配。

2
这就是 Weak 如何使循环引用成为可能,因为 Rc 的释放将导致 Weak 也被释放。 - Chayim Friedman

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