doliphin
doliphin

Reputation: 1014

Why is it considered safe to `mem::forget` Boxes?

The following code creates boxes, each pointing to a block of 4096 bytes. If I run this on release, everything is optimised away :(, but on debug, this behaves as expected and leaks a bunch of memory... or does it?

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);
    }
}

Why is this not considered unsafe?

Upvotes: 3

Views: 1265

Answers (3)

Joe Clay
Joe Clay

Reputation: 35797

The other answers have given good information on why this is considered safe today, but it's also useful to know the context of how this came to be the case.

Long ago (before 1.0), Rust did guarantee that safe code could never cause a memory leak (and as such mem::forget was marked as an unsafe fn). As a consequence of this, other unsafe code was written that relied on this assumption.

However, this turned out to be completely untrue - for example, you can trivially create a memory leak by creating cyclic Rcs! As a result, several APIs in std (most notably, the original implementation of scoped threads) had to be removed or rewritten, as they were unsound.

This is occassionally referred to within the Rust community as the 'leakpocalypse', and it led to Rust's definition of memory safety being changed to no longer cover memory leaks. Since leaking memory was no longer considered unsafe, mem::forget was changed to no longer be an unsafe fn.

Upvotes: 13

user1937198
user1937198

Reputation: 5348

Because a memory leak is not considered a violation of safety. Applications can suffer live leaks (where memory is still referenced, but will never be used), as much as dead leaks (where the memory is no longer referenced). Additionally, a leak will never lead to an application returning incorrect or invalid results, or any other form of observable functional impact. It will only impact the non-functional behavior of the amount of memory used by the application.

Another way to leak memory in safe rust is creating a cyclic Rc structure.

Upvotes: 0

The documentation is pretty clear:

forget is not marked as unsafe, because Rust’s safety guarantees do not include a guarantee that destructors will always run. For example, a program can create a reference cycle using Rc, or call process::exit to exit without running destructors. Thus, allowing mem::forget from safe code does not fundamentally change Rust’s safety guarantees.

"Safety" in rust means some very specific things. "Safety" in rust means only that it is impossible to access invalid memory, or to have multiple mutable references to a object. The book describes it very nicely:

Rust is otherwise quite permissive with respect to other dubious operations. Rust considers it "safe" to:

  • Deadlock
  • Have a race condition
  • Leak memory
  • Fail to call destructors
  • Overflow integers
  • Abort the program
  • Delete the production database

std::mem::forget allows you to leak memory, which is explicitly allowed. If you would want to get the memory back (eg with transmute or with Box::from_raw_parts) you would need unsafe code, since that could violate the mutable references rules.

Upvotes: 7

Related Questions