The std::mem module in Rust provides a variety of tools for manipulating memory and interacting with low-level details of Rust’s memory management system. It is particularly useful in advanced scenarios, such as manually managing resources, optimizing performance, or interfacing with FFI (Foreign Function Interface). Among the most notable utilities in std::mem is mem::forget.

mem::forget

Purpose: mem::forget prevents Rust from automatically running the destructor of a value when it goes out of scope. When you call mem::forget, Rust simply “forgets” about the value, leaving the underlying memory or resource as-is without deallocating it.

This is particularly useful when you intentionally want to bypass Rust’s ownership and drop semantics, such as when working with FFI or creating a memory leak deliberately for specific use cases.

Example of mem::forget

use std::mem;
 
fn main() {
    let v = vec![1, 2, 3]; // A vector allocated on the heap
    mem::forget(v); // The memory for `v` is not deallocated, and its destructor is not called
    // Note: This causes a memory leak.
}

In this example, the mem::forget function prevents the Vec’s destructor from running, which means the heap memory allocated for the vector is never reclaimed.

When to Avoid mem::forget

  • Memory Leaks: Using mem::forget can cause memory leaks if the value being forgotten contains heap-allocated resources.
  • Better Alternatives: For many scenarios, there are safer alternatives, such as Box::leak, which allows controlled memory leaks by returning a static reference.

mem::forget vs std::ptr::drop_in_place

mem::forget skips the destructor entirely, while ptr::drop_in_place explicitly invokes it. The two serve different purposes and are often used in different contexts.

Other Utilities in std::mem

The std::mem module contains many other utilities that are helpful for manipulating memory and understanding Rust’s ownership system. Below are some of the most notable ones:

size_of and size_of_val

These functions provide the size of a type or value in bytes at compile-time or runtime, respectively.

use std::mem;
 
fn main() {
    println!("Size of i32: {}", mem::size_of::<i32>()); // 4 bytes
    let v = vec![1, 2, 3];
    println!("Size of Vec: {}", mem::size_of_val(&v)); // Size of the Vec struct, not its contents
}

replace

replace swaps the value at a mutable reference with a new value and returns the old one. This is useful when you need to overwrite a value without borrowing it again.

use std::mem;
 
fn main() {
    let mut x = 42;
    let old = mem::replace(&mut x, 100);
    println!("Old value: {}, New value: {}", old, x); // Old value: 42, New value: 100
}

take

take is a shorthand for replacing a value with its default and returning the old value. It’s commonly used to take ownership of an option or other resource.

use std::mem;
 
fn main() {
    let mut data = Some(42);
    let taken = mem::take(&mut data);
    println!("Taken: {:?}, Remaining: {:?}", taken, data); // Taken: Some(42), Remaining: None
}

drop

drop explicitly invokes the destructor of a value, immediately releasing its resources. This is typically used when you want to clean up a value before the end of its scope.

fn main() {
    let s = String::from("Rust");
    drop(s); // The String is explicitly dropped here
    // println!("{}", s); // This would be a compile-time error
}

MaybeUninit

MaybeUninit allows you to work with uninitialized memory safely. It is commonly used in performance-critical scenarios where initialization can be deferred or partially performed.

use std::mem::MaybeUninit;
 
fn main() {
    let mut uninit: MaybeUninit<i32> = MaybeUninit::uninit();
    unsafe {
        uninit.as_mut_ptr().write(42); // Initialize the memory
        let value = uninit.assume_init(); // Take ownership of the initialized value
        println!("Value: {}", value);
    }
}

ManuallyDrop

ManuallyDrop allows you to bypass Rust’s automatic destructor calls, giving you finer control over when (or if) a value’s destructor is invoked.

use std::mem::ManuallyDrop;
 
fn main() {
    let mut data = ManuallyDrop::new(vec![1, 2, 3]);
    unsafe {
        ManuallyDrop::drop(&mut data); // Explicitly drop the Vec
    }
}
  • FFI Interfacing: When working with foreign code that expects Rust to relinquish control of memory, mem::forget can prevent Rust from interfering.
  • Avoiding Double Drops: In complex ownership scenarios, you might need to disable Rust’s default drop behavior temporarily.
  • Performance Optimization: Using ManuallyDrop or MaybeUninit can reduce initialization overhead or allow you to manage lifetimes more explicitly.