Rust 1.28 gives programs a stable global allocator choice
Programs can select the system allocator or supply a GlobalAlloc implementation on stable Rust. The interface makes allocation policy explicit while retaining strict memory-layout obligations.
The allocator influences a program's memory behavior even when its application code never names it. Rust's collections need backing storage, and a toolchain-selected default determines how that storage is obtained. Rust 1.28 makes that choice available to programs on stable Rust through #[global_allocator] and the GlobalAlloc interface. Today's announcement describes selecting the system allocator and providing an application-specific implementation.
The distinction is between controlling allocation policy and manually managing every allocation. A program can select a global allocator while continuing to use ordinary collection APIs. The selection changes the implementation beneath those operations. It does not require callers to replace every collection with raw pointers.
I find that degree of control appropriate for Rust's systems role. An application should be able to state an important runtime choice without reconstructing its entire data model around that choice. The interface also makes clear that supplying an allocator carries obligations beyond implementing a convenient callback.
A static selects the allocator
The release provides std::alloc::System as a handle to the system allocator. A static annotated with #[global_allocator] selects it for the program. This small example follows the mechanism demonstrated in the announcement.
use std::alloc::System;
#[global_allocator]
static GLOBAL: System = System;
fn main() {
let mut values = Vec::new();
values.push(7u8);
assert_eq!(values[0], 7);
}
The selection appears separately from the vector operation. That separation is the point of a global policy. Code can request storage through its ordinary abstraction while the application decides which allocator supplies it.
The release notes say the previous default is jemalloc on some platforms and the system allocator on others. An explicit selection should therefore not be described as replacing jemalloc everywhere. Its effect depends on the platform's earlier default. The stable capability is that the program can now make the choice itself.
Choosing System is also different from writing an allocator. The standard implementation already supplies the required operations. A custom implementation takes responsibility for satisfying their contracts, including memory layout and failure behavior. The annotation is small because it identifies an implementation, not because every possible implementation is simple.
Layout describes more than a byte count
The Rust 1.28 allocator definitions describe allocation through Layout. A request includes a size and alignment requirement. Returning enough bytes at an unsuitable address does not fulfill that request. Alignment is part of the contract the caller may rely on.
This is a concrete reason an allocator interface cannot be treated as a generic request for an arbitrary buffer. The storage has to accommodate the requested type's placement requirements. A safe collection depends on the allocator implementation honoring those requirements beneath its own safe operations.
The allocation method returns a pointer to a block or a null pointer indicating failure. The documentation also restricts callers to nonzero-size layouts for this interface. Those rules define the exchange between caller and allocator. Neither side can infer that arbitrary pointer arithmetic will satisfy the other's requirements.
Deallocation has a corresponding history requirement. The pointer must identify memory currently allocated through that allocator, and the supplied layout must match the original allocation. Releasing a block through an unrelated allocator does not become valid because both implementations expose methods with the same names.
These rules are why global selection belongs at a clear program boundary. A library casually mixing allocation origins would create obligations for every transfer and release. Selecting a common implementation for the program avoids turning ordinary collection users into coordinators of several incompatible allocation histories.
An unsafe implementation supports safe callers
GlobalAlloc is an unsafe trait. Its source explains that implementers must preserve the method contracts and must not unwind from allocator methods. The compiler cannot prove these properties merely from the presence of the required function signatures.
That has a direct consequence for custom allocator design. Reporting a failure through the documented return value is different from panicking through the allocation interface. A helper that is harmless in ordinary application control flow may be unsuitable inside an allocator if it can unwind. The implementation must account for its own dependencies as well as its allocation algorithm.
The default implementations also make the contract visible. The source's zeroed-allocation method delegates to allocation and then initializes the returned storage when it succeeds. Reallocation has separate rules about whether ownership transfers and whether the old block remains usable after failure. These operations rely on precise agreements about state, not just on acquiring an address.
For reallocation, the documentation says a null result leaves the original block's ownership and contents unchanged. A successful non-null result transfers the old block to the allocator, and the caller must use the returned storage under the new contract. This difference is essential for code handling failure without accidentally losing or releasing a block that remains live.
I regard those explicit requirements as part of the feature's usefulness. A customizable mechanism is most valuable when its obligations are inspectable. The application can choose an implementation, while the unsafe trait identifies where a reviewer needs to examine the guarantees that safe consumers cannot establish themselves.
The global choice is not a per-collection parameter
This release stabilizes global allocator selection. It should not be read as a claim that every possible allocator API is stable. The versioned source still marks broader allocator interfaces as unstable. The available mechanism addresses the program-wide choice through a specific stable contract.
That scope is enough for applications needing a different default policy while preserving existing collection use. It is also a reason not to promise independent allocator selection for every vector from this announcement. A global setting and a parameter supplied separately to each collection would provide different control over ownership and storage.
Rust 1.28 also stabilizes nonzero unsigned integer wrappers. The announcement shows that excluding zero permits compact optional representations, including through wrapper structs. That concerns how values represent their states, while allocator selection concerns where storage comes from. Both affect memory use, but one changes representation and the other changes allocation policy. A program choosing a global allocator still benefits from data types whose representations express the states they actually permit.