Course contentsShow
← Rust
Lesson 419 of 1,9104. Lifetimes and Advanced OwnershipFree lesson

Memory Mapping Files Safely

Map large files into memory and expose them via safe, lifetime-bound slice interfaces.

In this lesson, you will learn how to wrap raw memory-mapped file pointers into Rust slices that respect the borrow checker’s lifetime rules.

The Challenge of Memory Mapping

When you "memory map" a file, the operating system gives you a pointer to a chunk of memory that corresponds directly to the bytes on your disk. Because this memory exists outside of Rust’s normal heap allocator, the compiler cannot automatically know when that memory is valid. If the file is truncated or closed while your program is still reading the bytes, your application will crash—or worse, exhibit undefined behavior.

Binding Lifetimes to the Mapping

To bridge this gap, we use the Newtype Pattern to ensure that any slice derived from the memory-mapped file cannot outlive the mapping itself. We treat the file mapping as the "owner" of the memory and tie the slice's lifetime to that owner.

Consider this simplified abstraction:

struct MmapFile<'a> {
    ptr: *const u8,
    len: usize,
    _marker: std::marker::PhantomData<&'a [u8]>,
}

impl<'a> MmapFile<'a> {
    // We bind the returned slice to the lifetime of the MmapFile
    pub fn as_slice(&'a self) -> &'a [u8] {
        unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
    }
}

By adding 'a to our MmapFile struct and using it in as_slice, we tell the borrow checker: "The bytes returned by this function are only valid for as long as this MmapFile instance exists." If you try to drop the file mapping while a part of your code is still holding onto that &[u8] slice, the compiler will refuse to compile the code.

Why This Matters

By enforcing these constraints, we treat a volatile system resource like a standard Rust reference. You get the high-performance benefits of direct disk-to-memory access (avoiding costly read calls into buffers) without sacrificing memory safety. You are effectively "wrapping" the unsafe mmap call in a safe, lifetime-bound interface that is guaranteed to be memory-safe by the compiler.

Key Takeaway: By tying the lifetime of an exposed slice to the container holding the raw memory-mapped pointer, we ensure that files cannot be unmapped while data is still being accessed.