Software 44949 Published by

Rust 1.99.0 has been released today, landing in the language's usual six-month release cycle with a feature systems programmers have wanted for years. The headline change lets you finally define C-ABI variadic functions in stable Rust, closing a gap that previously forced developers to rely on inline-assembly shims. Alongside it arrives a batch of upgrades, including raw-pointer memory layout access, a fresh Box::leak recommendation, dozens of standard-library stabilizations, a new Cargo debug profile, and RISC-V Tier 2 support powered by LLVM 23.



Rust 1.99.0 ships with stable C-variadic functions, plus a mountain of smaller changes

The latest stable Rust lands today, capping its usual six-month cycle with a feature systems programmers have been waiting years for: you can finally define C-ABI variadic functions in stable Rust. No more inline-assembly shims just to write a printf-style helper.

Rust built its reputation on memory safety without a garbage collector. That lets you ship fast code that still catches buffer overflows, use-after-free errors, and data races at compile time. Variadic functions live squarely in that systems-programming corner, and until now the language could only call them, never write them.

Rust198

The headline change closes that gap. Here's the minimal example from the announcement:

/// SAFETY: must be called with (at least) 2 i32 arguments.
unsafe extern "C" fn sum(mut args: ...) -> i32 {
    let a = unsafe { args.next_arg::<i32>() };
    let b = unsafe { args.next_arg::<i32>() };
    a + b
}

fn foo() -> i32 {
    unsafe { sum(0i32, 2i32) }
}

The ... syntax declares the variadic parameter, and its type is the newly stable VaList. That type is ABI-compatible with C's va_list across targets, so a Rust-written function can be called from C code and vice versa without special handling. Arguments arrive one at a time through next_arg::<T>(), while the VaArgSafe trait keeps you from reading the wrong type out of the stream — a classic route to undefined behavior in C. Like any genuinely dangerous tool, it stays behind unsafe.

There's a bit more to it. Defining naked variadic functions with non-"C" ABIs is stable too, though those have to be written in inline assembly. Low-level and embedded developers get finer control over calling conventions on exotic targets as a result, and the spec now lives in the Rust Reference.

More than just the headline

The second major language stabilization settles the safety rules for querying memory layout through a raw pointer. Three functions now work on raw pointers for both Sized and non-Sized types: Layout::for_value_raw, mem::size_of_val_raw, and mem::align_of_val_raw. That matters for unsafe code manipulating dynamically-sized types like str or trait objects, a pattern you'll find throughout custom allocators, serialization formats, and FFI layering.

Rust also rewrote some guidance without touching language semantics at all. The docs for Box::leak now steer you away from the "round-trip unleak" pattern, where you leak a box and later try to free the underlying memory. It plays badly with current and future compiler optimizations, and it'll degrade further once custom allocators finally land. The suggested replacement is Box::into_non_null.

Then there's the grab bag of standard-library stabilizations. VecDeque gains retain_back, Vec gets into_parts and from_parts for round-trip buffer access, and String::from_utf8_lossy_owned joins the mix to skip an extra copy. FusedIterator now applies to StepBy<I>, while std::fs::set_times gives you programmatic control over file timestamps, including on symlinks.

Keep in mind that Cargo shipped a matching 1.99.0 alongside the language itself. The standouts are a new built-in profile called debug and a couple of workspace tweaks. It's functionally identical to dev today, so if that profile name sounds like a big deal, temper your expectations — it's really just paving the road. Someday dev is meant to shift toward "fast development iterations" instead of "build with debugging info."

Cargo also lets workspace members on edition 2024 or later override an inherited dependency's default-features field, per RFC 3945. On earlier editions the override is silently ignored with a warning. On top of that, incremental compilation is now disabled by default when the CI environment variable is set, which should make CI builds a little faster and more reproducible.

The boring-but-important stuff

On the platform side, riscv64-unknown-linux-musl is promoted to Tier 2 with host tools. It's a meaningful step for RISC-V, an architecture that matters for embedded work and for China's growing push into domestic silicon, the T-Head C910 among them.

Under the hood the compiler now runs on LLVM 23, which usually brings performance gains and support for newer hardware. Rust did some hardening too, enabling static_position_independent_executables on gnu and musl targets to fight code-relocation attacks. There's also a Rustdoc speedup of roughly 20% on average, up to 40% on some real-world crates, from smarter filtering of trait implementations.

You'll likely run into the compatibility notes if you maintain anything sizable. Legacy integral modules are fully deprecated, so std::i32::MAX should become i32::MAX. no_mangle_generic_items is now a hard error. Pin::new_unchecked's safety invariants shifted slightly. And a new lint, semicolon_in_expressions_from_non_local_macros, will flag semicolons from macros in other crates instead of hiding them behind crate boundaries. If you see it, report it to the owning crate rather than burying it in your own config.

As with every Rust release, 1.99.0 was a collaborative effort, with pre-release testing kicking off on the Inside Rust blog on September 29. Thanks to the contributors live at thanks.rust-lang.org/rust/1.99.0.

Rust is heading somewhere specific here. Recent Inside Rust posts point to experiments with function overloading, continued progress on custom allocators, and more platform expansion. C-variadic functions are just one more piece of a multi-year push to make Rust a first-class citizen for writing complete systems software that talks cleanly to C's massive existing codebase.

To get it, run rustup update stable on a machine that already has rustup installed. If you don't have it, grab the installer from the Rust install page, or switch to beta or nightly with rustup default beta and rustup default nightly to help test upcoming releases.

Head here to the full release notes and here to the GitHub release page.