Function core::mem::forget 1.0.0 (const: 1.46.0)[−][src]
pub const fn forget<T>(t: T)
Expand description
在不运行其析构函数 的情况下,获取所有权和 “forgets” 值。
该值管理的任何资源 (例如堆内存或文件句柄) 将永远处于无法访问的状态。但是,它不能保证指向该内存的指针将保持有效。
- 如果要泄漏内存,请参见
Box::leak
。 - 如果要获取内存的裸指针,请参见
Box::into_raw
。 - 如果要正确处理某个值,请运行其析构函数,请参见
mem::drop
。
Safety
forget
未将其标记为 unsafe
,因为 Rust 的安全保证不包括析构函数将始终运行的保证。
例如,程序可以使用 Rc
创建引用循环,或调用 process::exit
退出而不运行析构函数。
因此,从安全代码允许 mem::forget
不会从根本上改变 Rust 的安全保证。
也就是说,通常不希望泄漏诸如内存或 I/O 对象之类的资源。
在某些特殊的用例中,对于 FFI 或不安全代码提出了需求,但即使这样,通常还是首选 ManuallyDrop
。
因为允许忘记一个值,所以您编写的任何 unsafe
代码都必须允许这种可能性。您不能返回值,并且期望调用者一定会运行该值的析构函数。
Examples
mem::forget
的规范安全使用是为了避免 Drop
trait 实现的值的析构函数。例如,这将泄漏 File
,即
回收变量占用的空间,但不要关闭基础系统资源:
use std::mem; use std::fs::File; let file = File::open("foo.txt").unwrap(); mem::forget(file);Run
当基础资源的所有权先前已转移到 Rust 之外的代码时 (例如,通过将原始文件描述符传输到 C 代码),这很有用。
与 ManuallyDrop
的关系
虽然 mem::forget
也可以用于转移 内存 所有权,但是这样做很容易出错。
ManuallyDrop
应该改用。例如,考虑以下代码:
use std::mem; let mut v = vec![65, 122]; // 使用 `v` 的内容构建 `String` let s = unsafe { String::from_raw_parts(v.as_mut_ptr(), v.len(), v.capacity()) }; // 泄漏 `v`,因为它的内存现在由 `s` 管理 mem::forget(v); // 错误 - v 无效,不得将其传递给函数 assert_eq!(s, "Az"); // `s` 被隐式丢弃,并且其内存被释放。Run
上面的示例有两个问题:
- 如果在
String
的构造与mem::forget()
的调用之间添加了更多代码,则其中的 panic 将导致双重释放,因为v
和s
均处理同一内存。 - 调用
v.as_mut_ptr()
并将数据所有权传输到s
之后,v
值无效。 即使将值仅移动到mem::forget
(不会检查它),某些类型对其值也有严格的要求,以使它们在悬空或不再拥有时无效。 以任何方式使用无效值,包括将它们传递给函数或从函数中返回它们,都构成未定义的行为,并且可能会破坏编译器所做的假设。
切换到 ManuallyDrop
可以避免两个问题:
use std::mem::ManuallyDrop; let v = vec![65, 122]; // 在将 `v` 拆解为原始零件之前,请确保它不会丢弃掉! let mut v = ManuallyDrop::new(v); // 现在拆卸 `v`。这些操作不能 panic,因此不会有泄漏。 let (ptr, len, cap) = (v.as_mut_ptr(), v.len(), v.capacity()); // 最后,构建一个 `String`。 let s = unsafe { String::from_raw_parts(ptr, len, cap) }; assert_eq!(s, "Az"); // `s` 被隐式丢弃,并且其内存被释放。Run
ManuallyDrop
强有力地防止了双重释放,因为在执行其他任何操作之前,我们先禁用了 v 的析构函数。
mem::forget()
不允许这样做,因为它消耗了其参数,仅在从 v
中提取了我们需要的所有内容后,才迫使我们调用它。
即使在 ManuallyDrop
的构建与字符串的构建之间引入了 panic (这在所示的代码中不能发生),也将导致泄漏,而不是双重释放。
换句话说,ManuallyDrop
在泄漏的一侧发生错误,而不是在 (两次) 丢弃的一侧发生错误。
同样,ManuallyDrop
避免了在将所有权转让给 s
之后必须使用 “touch” v
的情况-完全避免了与 v
交互以处置它而不运行其析构函数的最后一步。