Rust 语言内存管理小记 😭
概述
std::mem::forget
:该函数用于“忘记”给定的值,即在不运行该值的析构函数的情况下释放该值的所有权。Box::leak
:该函数可以将 Box 包装的变量转换为静态变量,从而延长变量的生命周期。std::cell::RefCell
:可以在运行时动态地检查和修改数据,非线程安全。std::rc::Rc
:可在多个线程间共享变量,但需要注意线程安全问题。std::sync::Arc:Arc
:Rc 的线程安全版本。
Forget
std::mem::forget
是一个函数,它用于“忘记”给定的值。这个函数接受一个类型为 T
的值作为参数,并在不运行该值的析构函数的情况下释放该值的所有权(回收变量占用的空间)。这意味着,如果一个值被传递给 std::mem::forget
函数,它将不会被释放,并可能导致内存泄漏或其他问题。
当底层资源的所有权先前被转移到 Rust 之外的代码,例如通过将原始文件描述符传输到 C 代码时,这很有用。
示例
1use std::mem;
2
3struct MyType {
4 value: i32,
5}
6
7impl Drop for MyType {
8 fn drop(&mut self) {
9 println!("Dropping MyType({})", self.value);
10 }
11}
12
13fn main() {
14 let my_value = MyType { value: 42 };
15
16 // 忘记 my_value 的所有权
17 mem::forget(my_value);
18
19 println!("The program is still running");
20}
在这个例子中,定义了一个名为 MyType
的结构体,它包含一个 value
字段并实现了 Drop
trait。Drop
trait 的实现在值被释放时打印一条消息。在 main
函数中,我们创建一个 MyType
的值,并使用 std::mem::forget
函数来忘记它的所有权。这意味着在程序运行期间,该值的 Drop
实现不会被调用,即使该值已经离开了它的作用域。因此,在该程序中,我们只会看到打印的第一条消息,“Dropping MyType(42)” 不会被打印。
Leak
Box::leak
是一个 unsafe
函数,用于将 Box
中的值 泄漏
到堆上,以将其变为 静态生命周期
。它的函数声明如下:
1pub fn leak<T: ?Sized>(b: Box<T>) -> &'static mut T
其会获取一个 Box<T>
,并返回一个指向堆上分配的 T
的 &'static mut T
指针。注意,其返回的是一个 静态生命周期
的引用。
示例
1fn main() {
2 let x = Box::new(10);
3 let p = Box::leak(x);
4 assert_eq!(*p, 10);
5
6 // p 在程序结束前不会释放内存
7}
在这个例子中,我们将 Box<i32>
类型的 x
变量传递给 Box::leak
函数,其返回一个指向静态生命周期的 i32
类型的指针的引用。
RefCell
RefCell
实现了 内部可变性(Interior Mutability)
,它允许在不可变引用的前提下改变值,这是通过运行时的借用检查来实现的。它是在运行时检查可变性(而不是在编译时),这样可以更灵活地控制可变性,并且可以在必要的时候避免复制数据。
RefCell 类似于一个容器,可以通过 borrow 方法获得一个可变引用或者一个不可变引用。获取可变引用后,可以改变容器中的值,但是如果同一时间存在其他的引用,它们将不再是有效的引用,这时候程序将 panic。
示例
1use std::cell::RefCell;
2
3let x = RefCell::new(42);
4let y = x.borrow(); // 获取一个不可变引用
5let mut z = x.borrow_mut(); // 获取一个可变引用
6
7*z = 13; // 改变容器中的值
本文番外章节中,有介绍到它的亲戚 Cell
,闲时可以了解一下。
RC & ARC
RC 是 Rust 中的引用计数指针,它允许多个所有权持有者共享同一个值,并在没有任何所有权持有者时自动释放它。但是,RC 只能用于单线程环境,因为它不能保证并发操作的安全性。当有一个新的所有权持有者时,RC 会增加计数器,当计数器为零时,它将释放指向堆上数据的内存。
Arc 是 Rust 中的原子引用计数指针,与 RC 基本相同。主要的差异是 Arc 是线程安全的,即可以在不同的线程之间安全地共享数据。性能方面,Arc 比 RC 慢,因为它需要原子性地增加和减少计数器,以确保并发访问时的安全性。
RC 和 ARC 都是可变的,这意味着它们可以修改内部值。不当使用 Arc 和 RC 可能会导致循环引用,务必小心。
Cell
std::cell::Cell
是一个提供了内部可变性的类型。它允许你在不可变变量内部修改值,但是其限制了这个值的访问范围。具体来说,Cell
中的值可以被修改,但只能通过方法调用来访问。
Cell
通常用于一些需要在不可变变量中存储可变状态的情况,例如在多线程编程中。它的方法包括:
get
:获取Cell
中的值的不可变引用;set
:将Cell
中的值设置为新的值;replace
:将Cell
中的值替换为新值,并返回旧的值;take
:将Cell
中的值替换为默认值,并返回旧的值;into_inner
:将Cell
中的值移出Cell
并返回它。
需要注意的是,Cell
中存储的值必须实现 Copy
trait,这意味着它们必须是可以复制的类型,例如整数、浮点数和指针类型。如果需要存储的值不支持 Copy
trait,可以考虑使用 RefCell
或 Mutex
等类型来实现内部可变性。
番外
内部可变性
实现了内部可变性的有 Cell
、RefCell
、Mutex
等,此处就不一一列举了。
终末
有兴趣的话,可以去了解一下 lazy_static
和 static_cell
这两个包,也蛮有意思的。