Rust 1.99 Makes FFI and Raw-Pointer Contracts Explicit
Rust 1.99.0 stabilizes defining C-ABI variadic functions, adds raw-pointer layout APIs, and turns several low-level safety assumptions into clearer review points for systems and interoperability code.

Rust 1.99.0, released on October 1, gives developers a sharper boundary between Rust’s safety model and low-level interfaces. The release is especially relevant to teams maintaining native helpers, FFI adapters, allocators, and build tooling around WebAssembly or ICP projects.
The headline change is stable support for defining C-ABI variadic functions with the C and C-unwind ABIs. Rust could already call functions such as printf supplied by external libraries; it can now implement compatible variadic functions itself. The arguments are read through VaList, with permitted argument types constrained by VaArgSafe. Rust also stabilizes support for naked variadic functions that use inline assembly.
This is useful for compatibility layers, but it is not a removal of the FFI safety burden. A variadic function still depends on the caller supplying the expected argument count, types, and ABI. The release announcement’s example marks the function unsafe and documents its precondition. For production bindings, that contract should remain visible in the wrapper API rather than being left to every call site.
Rust 1.99 also stabilizes Layout::for_value_raw, size_of_val_raw, and align_of_val_raw. These APIs provide size and alignment information from raw pointers, including pointers to dynamically sized types. That is valuable in allocators, object representations, and FFI code that must inspect memory layouts without first converting everything into ordinary references.
The practical implication is precision, not permission. Raw-pointer layout queries can support correct metadata handling, but they do not make an invalid pointer valid or remove the need to uphold provenance, initialization, alignment, and lifetime requirements. Teams should treat the new APIs as narrow tools inside audited unsafe abstractions.
The release carries a related warning for memory-management code: Rust’s documentation now recommends against patterns that deallocate memory after round-tripping through Box::leak. The announcement points developers toward Box::into_non_null or Box::into_raw instead. This is documentation guidance rather than a language-semantics change, but it matters for libraries that transfer ownership across custom allocators or foreign runtimes.
For ICP developers, the takeaway is to review the native edge of the build and deployment pipeline. WebAssembly canisters generally avoid C variadics, but host-side tools, cryptographic libraries, transport bindings, and custom build integrations may cross these boundaries. Rust 1.99 makes more of those operations available on stable; it does not make their contracts automatic.
Cargo also adds a built-in debug profile as preparation for a future transition of dev toward faster iteration, while the release notes say the two profiles currently behave the same. CI users should additionally review the release’s compatibility note that incremental compilation is disabled by default when Cargo detects the CI environment variable. That can affect build-time and cache assumptions even when source code is unchanged.
The safest upgrade path is incremental: install Rust 1.99 with rustup update stable, compile FFI and allocator-heavy crates under the new toolchain, and inspect warnings and CI timings before changing profile policy. The release is less about one dramatic feature than about making low-level contracts more explicit at the points where Rust meets foreign ABIs and raw memory.
Get the wire in your inbox
Every new signal, straight from the generator. No noise, unsubscribe anytime.


