Module std::rc

1.0.0 · source ·
Expand description

单线程引用计数指针。Rc 代表引用计数。

Rc<T> 类型提供了在堆中分配的 T 类型值的共享所有权。 在 Rc 上调用 clone 会产生一个指向堆中相同分配的新指针。 当指向给定分配的最后一个 Rc 指针被销毁时,存储在该分配中的值 (通常称为 “内部值”) 也将被丢弃。

默认情况下,Rust 中的共享引用不允许可变,Rc 也不例外:您通常无法获得 Rc 内部内容的可变引用。 如果需要可变性,则将 CellRefCell 放在 Rc 内; 请参见 Rc 中的可变性示例

Rc 使用非原子引用计数。 这意味着开销非常低,但是不能在线程之间发送 Rc,因此 Rc 不实现 Send。 结果,Rust 编译器将检查 at compile time 您是否不在线程之间发送 Rc。 如果需要多线程的原子引用计数,请使用 sync::Arc

downgrade 方法可用于创建非所有者 Weak 指针。 Weak 指针可以被 upgradeRc,但是如果已经丢弃了分配中存储的值,则它将返回 None。 换句话说,Weak 指针不会使分配内部的值保持活动状态。但是,它们确实使分配 (内部值的后备存储) 保持活动状态。

Rc 指针之间的循环将永远不会被释放。 因此,Weak 用于中断循环。 例如,一棵树可以具有从父节点到子节点的强 Rc 指针,以及从子节点到其父节点的 Weak 指针。

Rc<T> 自动取消对 T 的引用 (通过 Deref trait),因此您可以在类型为 Rc<T> 的值上调用 T 的方法。 为了避免与 T 方法的名称冲突,Rc<T> 本身的方法是关联函数,使用 完全限定语法 进行调用:

use std::rc::Rc;

let my_rc = Rc::new(());
let my_weak = Rc::downgrade(&my_rc);
Run

RC<T> 也可以使用完全限定语法来调用 traits 的 Clone` 等实现。 有些人喜欢使用完全限定的语法,而另一些人则喜欢使用方法调用语法。

use std::rc::Rc;

let rc = Rc::new(());
// 方法调用语法
let rc2 = rc.clone();
// 完全限定的语法
let rc3 = Rc::clone(&rc);
Run

Weak<T> 不会自动解引用到 T,因为内部值可能已经被丢弃了。

克隆引用

使用为 Rc<T>Weak<T> 实现的 Clone trait,可以创建与现有引用计数指针相同分配的新引用。

use std::rc::Rc;

let foo = Rc::new(vec![1.0, 2.0, 3.0]);
// 以下两种语法是等效的。
let a = foo.clone();
let b = Rc::clone(&foo);
// a 和 b 都指向与 foo 相同的内存位置。
Run

Rc::clone(&from) 语法是最常见的语法,因为它更明确地传达了代码的含义。 在上面的示例中,使用此语法可以更轻松地看到此代码正在创建新的引用,而不是复制 foo 的全部内容。

Examples

考虑一个场景,其中给定的 Owner 拥有一组 Gadget。 我们想让我们的 Gadget 指向他们的 Owner。我们不能用唯一的所有权来做到这一点,因为一个以上的 gadget 可能属于同一个 OwnerRc 允许我们在多个 Gadget 之间共享一个 Owner,并且只要有任何 Gadget 指向它,Owner 就会保持分配状态。

use std::rc::Rc;

struct Owner {
    name: String,
    // ...其他字段
}

struct Gadget {
    id: i32,
    owner: Rc<Owner>,
    // ...其他字段
}

fn main() {
    // 创建一个引用计数的 `Owner`。
    let gadget_owner: Rc<Owner> = Rc::new(
        Owner {
            name: "Gadget Man".to_string(),
        }
    );

    // 创建属于 `gadget_owner` 的 `Gadget`。
    // 克隆 `Rc<Owner>` 为我们提供了指向同一个 `Owner` 分配的新指针,从而增加了该进程中的引用计数。
    //
    let gadget1 = Gadget {
        id: 1,
        owner: Rc::clone(&gadget_owner),
    };
    let gadget2 = Gadget {
        id: 2,
        owner: Rc::clone(&gadget_owner),
    };

    // 处理我们的局部变量 `gadget_owner`。
    drop(gadget_owner);

    // 尽管丢弃了 `gadget_owner`,我们仍然可以打印出 `Gadget` 的 `Owner` 的名称。
    // 这是因为我们只删除了一个 `Rc<Owner>`,而不是它指向的 `Owner`。
    // 只要还有其他 `Rc<Owner>` 指向相同的 `Owner` 分配,它将保持活动状态。
    // 字段投影 `gadget1.owner.name` 之所以起作用,是因为 `Rc<Owner>` 自动取消了对 `Owner` 的引用。
    //
    //
    println!("Gadget {} owned by {}", gadget1.id, gadget1.owner.name);
    println!("Gadget {} owned by {}", gadget2.id, gadget2.owner.name);

    // 在该函数的末尾,`gadget1` 和 `gadget2` 被销毁,并且它们与我们的 `Owner` 一起被算作最后引用。
    // `Gadget` 现在也被摧毁。
    //
}
Run

如果我们的要求发生变化,并且还需要能够从 Owner 遍历到 Gadget,我们将遇到问题。 从 OwnerGadgetRc 指针引入了一个循环。 这意味着它们的引用计数永远不会达到 0,并且分配也永远不会被销毁: 内存泄漏。为了解决这个问题,我们可以使用 Weak 指针。

实际上,Rust 使得在某种程度上很难产生此循环。为了最终得到两个指向彼此的值,其中之一必须是可变的。 这很困难,因为 Rc 仅通过对其包装的值给出共享的引用来强制执行内存安全性,而这些不允许直接更改。 我们需要将希望可变的的部分值包装在 RefCell 中,该值提供 interior 可变性: 一种通过共享引用实现可变性的方法。 RefCell 在运行时强制执行 Rust 的借用规则。

use std::rc::Rc;
use std::rc::Weak;
use std::cell::RefCell;

struct Owner {
    name: String,
    gadgets: RefCell<Vec<Weak<Gadget>>>,
    // ...其他字段
}

struct Gadget {
    id: i32,
    owner: Rc<Owner>,
    // ...其他字段
}

fn main() {
    // 创建一个引用计数的 `Owner`。
    // 请注意,我们已将 `Gadget` 的所有者的 vector 放在 `RefCell` 内,以便我们可以通过共享的引用对其进行可变。
    //
    let gadget_owner: Rc<Owner> = Rc::new(
        Owner {
            name: "Gadget Man".to_string(),
            gadgets: RefCell::new(vec![]),
        }
    );

    // 如前所述,创建属于 `gadget_owner` 的 `Gadget`。
    let gadget1 = Rc::new(
        Gadget {
            id: 1,
            owner: Rc::clone(&gadget_owner),
        }
    );
    let gadget2 = Rc::new(
        Gadget {
            id: 2,
            owner: Rc::clone(&gadget_owner),
        }
    );

    // 将 `Gadget` 添加到其 `Owner` 中。
    {
        let mut gadgets = gadget_owner.gadgets.borrow_mut();
        gadgets.push(Rc::downgrade(&gadget1));
        gadgets.push(Rc::downgrade(&gadget2));

        // `RefCell` 动态借用到此结束。
    }

    // 遍历我们的 `Gadget`,将其详细信息打印出来。
    for gadget_weak in gadget_owner.gadgets.borrow().iter() {

        // `gadget_weak` 是一个 `Weak<Gadget>`。
        // 由于 `Weak` 指针不能保证分配仍然存在,因此我们需要调用 `upgrade`,它返回 `Option<Rc<Gadget>>`。
        //
        //
        // 在这种情况下,我们知道分配仍然存在,因此我们只用 `unwrap` 和 `Option`。
        // 在更复杂的程序中,可能需要适当的错误处理才能获得 `None` 结果。
        //

        let gadget = gadget_weak.upgrade().unwrap();
        println!("Gadget {} owned by {}", gadget.id, gadget.owner.name);
    }

    // 在该函数的末尾,`gadget_owner`,`gadget1` 和 `gadget2` 被销毁。
    // 现在没有指向该 `Gadget` 的强大 (`Rc`) 指针,因此它们已被销毁。
    // 这会使 Gadget Man 的引用计数为零,因此他也被销毁了。
    //
}
Run

Structs

  • 单线程引用计数指针。Rc 代表引用计数。
  • WeakRc 的一个版本,它持有对托管分配的非所有权引用。 通过在 Weak 指针上调用 upgrade 来访问分配,它返回一个 Option<Rc<T>>