Expand description
单线程引用计数指针。Rc
代表引用计数。
Rc<T>
类型提供了在堆中分配的 T
类型值的共享所有权。
在 Rc
上调用 clone
会产生一个指向堆中相同分配的新指针。
当指向给定分配的最后一个 Rc
指针被销毁时,存储在该分配中的值 (通常称为 “内部值”) 也将被丢弃。
默认情况下,Rust 中的共享引用不允许可变,Rc
也不例外:您通常无法获得 Rc
内部内容的可变引用。
如果需要可变性,则将 Cell
或 RefCell
放在 Rc
内; 请参见 Rc
中的可变性示例。
Rc
使用非原子引用计数。
这意味着开销非常低,但是不能在线程之间发送 Rc
,因此 Rc
不实现 Send
。
结果,Rust 编译器将检查 at compile time 您是否不在线程之间发送 Rc
。
如果需要多线程的原子引用计数,请使用 sync::Arc
。
downgrade
方法可用于创建非所有者 Weak
指针。
Weak
指针可以被 upgrade
到 Rc
,但是如果已经丢弃了分配中存储的值,则它将返回 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);
RunRC<T> 也可以使用完全限定语法来调用 traits 的
Clone` 等实现。
有些人喜欢使用完全限定的语法,而另一些人则喜欢使用方法调用语法。
use std::rc::Rc;
let rc = Rc::new(());
// 方法调用语法
let rc2 = rc.clone();
// 完全限定的语法
let rc3 = Rc::clone(&rc);
RunWeak<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 相同的内存位置。
RunRc::clone(&from)
语法是最常见的语法,因为它更明确地传达了代码的含义。
在上面的示例中,使用此语法可以更轻松地看到此代码正在创建新的引用,而不是复制 foo 的全部内容。
Examples
考虑一个场景,其中给定的 Owner
拥有一组 Gadget
。
我们想让我们的 Gadget
指向他们的 Owner
。我们不能用唯一的所有权来做到这一点,因为一个以上的 gadget 可能属于同一个 Owner
。
Rc
允许我们在多个 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
,我们将遇到问题。
从 Owner
到 Gadget
的 Rc
指针引入了一个循环。
这意味着它们的引用计数永远不会达到 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 的引用计数为零,因此他也被销毁了。
//
}
RunStructs
- 单线程引用计数指针。
Rc
代表引用计数。