为什么“mem::forget”盒子被认为是安全的?

dol*_*hin 1 unsafe rust

以下代码创建框,每个框都指向 4096 字节的块。如果我在发布时运行它,一切都会被优化掉:(,但在调试时,这会按预期运行并泄漏大量内存......或者是吗?

fn main() {
    for _ in 0..1000 {
        for _ in 0..100000 {
            let b = Box::new([!0u64; 1 << 10]);
            std::mem::forget(b);
        }
        let mut buf = String::new();
        let _ = std::io::stdin().read_line(&mut buf);
    }
}
Run Code Online (Sandbox Code Playgroud)

为什么这不被认为是不安全的?

Joe*_*lay 11

其他答案提供了关于为什么今天这被认为是安全的很好的信息,但了解这种情况的背景也很有用。

很久以前(1.0 之前),Rust确实保证安全代码永远不会导致内存泄漏(因此mem::forget被标记为unsafe fn)。因此,其他不安全的代码也依赖于这个假设。

然而,事实证明这是完全不正确的——例如,您可以通过创建循环来轻松创建内存泄漏Rc!结果,其中的几个 API std(最值得注意的是作用域线程的原始实现)必须被删除或重写,因为它们不健全。

这在 Rust 社区中偶尔被称为“ leakpocalypse ”,它导致 Rust 的内存安全定义被更改为不再涵盖内存泄漏。由于泄漏内存不再被认为是不安全的,mem::forget因此已更改为不再是unsafe fn.


mou*_*ail 6

文档非常清楚:

\n
\n

忘记没有被标记为不安全,因为Rust\xe2\x80\x99s的安全保证不包括析构函数将始终运行的保证。例如,程序可以使用 Rc 创建引用循环,或者调用 process::exit 退出而不运行析构函数。因此,允许安全代码中的 mem::forget 并不会从根本上改变 Rust\xe2\x80\x99s 的安全保证。

\n
\n

Rust 中的“安全”意味着一些非常具体的事情。Rust 中的“安全”意味着不可能访问无效内存,或者对一个对象有多个可变引用。这本书描述得非常好

\n
\n

Rust 对于其他可疑操作是相当宽容的。\nRust 认为它“安全”:

\n
    \n
  • 僵局
  • \n
  • 有竞争条件
  • \n
  • 内存泄漏
  • \n
  • 无法调用析构函数
  • \n
  • 溢出整数
  • \n
  • 中止程序
  • \n
  • 删除生产数据库
  • \n
\n
\n

std::mem::forget允许您泄漏内存,这是明确允许的。如果您想取回内存(例如使用 transmute 或 with Box::from_raw_parts),您将需要不安全的代码,因为这可能违反可变引用规则。

\n