On this page

Allocator and Memory Layout

Rust uses the system malloc/free by default, but you can replace the global allocator via the GlobalAlloc trait or specify a custom allocator for specific containers. Layout describes memory size and alignment, Pin ensures values are not moved (crucial for self-referential types and async Futures), and #[repr(align)] and #[repr(C)] provide precise control over memory layout.

Default Allocator

Rust uses the system malloc/free by default (ptmalloc2 from glibc on Linux), but this can be replaced. Implement the GlobalAlloc trait and mark it with #[global_allocator]:

use std::alloc::{GlobalAlloc, Layout, System};

struct MyAllocator;
unsafe impl GlobalAlloc for MyAllocator {
    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
        // Implement allocation logic: slice from a pre-allocated buffer, call mmap, etc.
    }
    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
        // Implement deallocation logic
    }
}

#[global_allocator]
static GLOBAL: MyAllocator = MyAllocator;

Common alternative allocators include: mimalloc (Microsoft, low fragmentation), jemalloc (FreeBSD, multi-threaded optimization), and snmalloc (Microsoft Research, message-passing based).

Layout: Describing Allocation Requests

Allocators do not directly receive "give me N bytes"—instead, they receive a Layout struct containing size and alignment:

use std::alloc::Layout;
let layout = Layout::new::<u64>();   // size=8, align=8
let layout2 = Layout::from_size_align(1024, 64).unwrap();

The allocator must return memory that satisfies the alignment requirements. If allocation fails, it returns null (not a panic).

Allocator API: Manual Allocation

use std::alloc::{alloc, dealloc, Layout};
let layout = Layout::new::<u64>();
let ptr = unsafe { alloc(layout) as *mut u64 };
unsafe { *ptr = 42 };
unsafe { dealloc(ptr as *mut u8, layout) };

This is almost identical to C's malloc/free, but with a type-safe Layout parameter. Most code does not need to call alloc directly—types like Box, Vec, and String handle this for you.

Pin: Ensuring Immutability of Location

Rust's move semantics mean that any value can be memcpy'd to a new address. However, for self-referential structures (where a struct's field references another field within the same struct), moving the struct leaves references dangling. Pin ensures that pinned values will not be moved:

use std::pin::Pin;
// Typical self-referential structure (simplified):
struct SelfRef { value: String, ptr: *const String }
// When constructed, ptr points to value, but if SelfRef is moved → ptr becomes dangling
// Pin<Box<SelfRef>> guarantees that SelfRef will not be moved — safe

Futures generated by async fn are typical self-referential structures—they reference local variables from before the await point. Pin<&mut Future> guarantees that the Future will not be moved during polling. This is the foundational implementation detail for the async/await compiler.

References

  • Rustonomicon: Allocator, Pin
  • RFC 1398: Custom allocators
  • RFC 2349: Pin

Keywords: GlobalAlloc, Layout, custom allocator, Pin, self-referential, #[repr(align)], alloc API