Rust 1.99.0 has officially been released. The headline feature of this release is the stabilization of extern "C" variadics (support for C variadic parameter lists). In addition, this release refines querying layout information directly from raw pointers, provides an important warning regarding Box::leak memory deallocation practices, and stabilizes several low-level conversion APIs to further elevate the systems programming experience. Let’s take a deep dive into these exciting new capabilities.
Support for extern “C” Variadics
The Problem It Solves:
In previous Rust releases, developers could easily call external variadic functions written in C or other languages (such as the printf family in libc). However, there was no direct, first-class language support for defining a variadic function in Rust that could be exposed to and called by C. As a result, developers replacing low-level C libraries or writing OS drivers that implement specific C interfaces often had to rely on complex macros or inline assembly.
Starting with 1.99, Rust officially allows directly defining variadic functions with the "C" or "C-unwind" ABI. The parameter list explicitly uses ..., which maps internally in Rust to the new VaList type. This type maintains full cross-target ABI compatibility with C’s va_list structure at the low level. To guarantee memory safety, types readable from VaList are strictly guarded by the VaArgSafe trait. Furthermore, this update stabilizes support for writing naked variadic functions for non-"C" ABIs using inline assembly.
Official Example Code:
/// SAFETY: must be called with (at least) 2 i32 arguments.
unsafe extern "C" fn sum(mut args: ...) -> i32 {
// SAFETY: guaranteed by the caller.
let a = unsafe { args.next_arg::<i32>() };
let b = unsafe { args.next_arg::<i32>() };
a + b
}
fn foo() -> i32 {
unsafe { sum(0i32, 2i32) }
}
Inspecting Memory Layout via Raw Pointers
The Problem It Solves:
When pursuing maximum performance optimization or constructing custom data structures, raw pointer manipulation is inevitable. A fundamental requirement when working with raw pointers is safely obtaining the size and alignment of the data they point to. For types implementing Sized, this is straightforward; but for dynamically sized types (DSTs such as [T] or dyn Trait), fat pointer representations make obtaining layout information cumbersome and prone to undefined behavior (UB).
This release thoroughly clarifies safety requirements in this domain and stabilizes a suite of layout-querying functions specifically tailored for raw pointers:
Layout::for_value_rawmem::size_of_val_rawmem::align_of_val_raw
The stabilization of these functions means developers can pass raw pointers directly to them without first converting them into references (which can easily trigger UB when dealing with partially initialized memory or strict borrow rules). This makes low-level memory allocation, deallocation, and custom allocator implementations safer and more ergonomic than ever.
Breaking Notice: Round-Trip Unleaking of Box::leak Is Discouraged
The Problem It Solves:
This is an essential behavioral specification update. In Rust’s memory management model, Box::leak is frequently used to leak dynamically allocated memory in order to obtain a mutable reference with a 'static lifetime. Historically, some developers relied on a trick: leaking memory for temporary usage and then, at the end of some lifecycle, unsafely reconstructing a Box to drop it—a practice known as “round-trip unleaking”.
However, the official documentation received a major overhaul in 1.99.0, explicitly warning developers against this anti-pattern. The compiler optimization model (particularly future LLVM optimization passes) may make aggressive assumptions known as “unreachable deallocations” upon observing Box::leak. Deallocating memory after leaking it not only breaks compiler aliasing analysis, but can also trigger unpredictable crashes once custom allocators are stabilized.
As a safer alternative, the Rust team recommends using Box::into_non_null or Box::into_raw. These two APIs perform straightforward pointer conversions without communicating “never deallocate” semantics to the compiler. This guideline applies equally to other standard library leak functions, such as String::leak.
Stabilization of Core APIs
The Problem It Solves: Beyond the major highlights above, Rust 1.99 stabilizes a wealth of highly practical standard library APIs, providing developers with more ergonomic and standardized building blocks for complex data handling.
- Vec and Zero-Copy Decomposition:
The newly stabilized
Vec::into_partsandVec::from_partsresolve longstanding safety pain points when handling C-FFI, where developers had to manually decompose aVecinto a raw pointer, length, and capacity, and subsequently reassemble it. Previously, manual unpacking and passing were vulnerable to field order mismatches or borrow check errors that caused memory leaks or dangling pointers. The new interfaces provide a well-typed, structured vehicle that strengthens code safety at FFI boundaries. - Lossy String Conversions:
The new
String::from_utf8_lossy_ownedandstring::FromUtf8Error::into_utf8_lossymethods offer a way to convert an already-allocated byte buffer directly into aString. If invalid UTF-8 sequences are encountered, lossy replacement occurs in place within the existing allocation, eliminating unnecessary intermediate string allocations—critical for high-performance network protocol parsers. - File Metadata Operations and Collection Updates:
std::fs::set_timesandstd::fs::set_times_nofollowallow direct, granular modification of filesystem timestamps (access and modification times), filling a longstanding gap in cross-platform timestamp manipulation. In addition, the stabilization ofBox::into_non_nullandBox::from_non_null, alongside anIntoIteratorimplementation for references toBox<[T; N]>, enhances collection flexibility.VecDeque::retain_backhas also officially landed in the standard library, offering an efficient reverse-filtering mechanism for double-ended queues.
Upgrade Recommendations
Here is a practical upgrade guide for different developer profiles:
| Target Audience | When to Upgrade | Key Considerations |
|---|---|---|
| Developers interfacing heavily with C variadic functions | Upgrade immediately | The new feature significantly streamlines FFI binding layers. Note that operating on ... (i.e., VaList) remains unsafe, requiring strict adherence to calling conventions and safety boundaries. |
| Library authors working on low-level memory management and custom allocators | Upgrade with a smooth transition | Thoroughly audit codebases for any round-trip unleaking patterns with Box::leak, and replace them with Box::into_raw or Box::into_non_null to prevent triggering undefined compiler behavior. |
| General application, web service, and backend developers | Upgrade at regular cadence | Run $ rustup update stable for a smooth, drop-in upgrade without breaking existing code. You will immediately benefit from compiler backend improvements, faster compilation times, and standard library additions. |