Module alloc::rc 1.0.0[−][src]
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>
本身的方法是关联函数,使用 fully qualified syntax 进行调用:
use std::rc::Rc; let my_rc = Rc::new(()); 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 可能属于同一个 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 的引用计数为零,因此他也被销毁了。 // }Run
Structs
Rc | 单线程引用计数指针。 |
Weak |
|