Developer ToolsProgramming Languages

Rust 1.99 Is Out: C-Variadics and What Breaks

Rust 1.99 release thumbnail showing Rust gear logo with circuit board lines and version badge on dark blue background
Rust 1.99 ships today with stable C-variadic definitions and LLVM 23

Rust 1.99 shipped today. For most projects it is a smooth upgrade. For some, it is not — five breaking changes will stop your build cold if you miss them. Here is what landed, what broke, and what you should act on.

Fix These Before You Upgrade

Start with the breaking changes. These are not deprecation warnings. They are compile errors.

1. no_mangle_generic_items Is Now a Hard Error

If you have #[no_mangle] on a generic function, your build fails in 1.99. This was a lint warning since Rust 1.56. Now it is an error. Generic functions cannot have stable ABI symbol names — the compiler must mangle them to accommodate the type parameter — so the attribute was always meaningless on generics. The fix is to remove it. If you need a stable symbol name from a generic context, add a concrete non-generic wrapper and put #[no_mangle] there instead.

2. Legacy Integral Module Paths Are Deprecated

The module-path integer constants — std::i32::MAX, std::u64::MIN, std::f64::NAN and their siblings — are now fully deprecated. The associated-const versions (i32::MAX, u64::MIN) have existed since Rust 1.43. Run cargo fix and most of these rewrite automatically.

3. Extern Static References No Longer Promoted

Code that borrows an extern static and implicitly gets a 'static lifetime no longer compiles. For extern statics, the compiler cannot prove the lifetime is truly static. Use a raw pointer explicitly, or annotate the lifetime. This mostly hits FFI glue code, but it will catch you off guard if you have any.

4. Stale Doc Code Block Attributes Now Fail

A #[allow(dead_code)] or similar attribute inside a doc example that applies to nothing in that block is now an error. If your documentation tests start failing after the upgrade, this is the likely culprit. Prune stale doc attributes.

5. Pin::new_unchecked Safety Contract Clarified

The safety requirements for Pin::new_unchecked have a minor clarification. Semantics are unchanged; the documentation is stricter. Low impact unless you maintain a pin-projection crate, in which case review your safety comments.

The Headliner: C-Variadic Functions on Stable Rust

Rust can now define C-variadic functions on stable, not just call them. This has been a blocked capability for FFI-heavy code for years. Custom allocators, printf-style log functions, C-compatible plugin systems — all of these required a nightly feature flag or contorted workarounds. In 1.99, the feature flag drops:

unsafe extern "C" fn my_logger(count: u32, mut args: ...) -> i32 {
    // use args.arg::<u32>() to read variadic arguments
    0
}

core::ffi::VaList stabilizes alongside this, giving you the type-safe handle to iterate over variadic arguments. #[unsafe(naked)] functions also gain the ability to define C-variadic signatures — relevant for low-level embedded code that needs tight control over calling conventions. If you have been gating a C interop crate on nightly because of this gap, 1.99 is the release that lets you drop that requirement.

Cargo: Your CI Just Changed Behavior

Cargo 1.99 disables incremental compilation automatically in CI environments when CI=true — which is set by default on GitHub Actions, CircleCI, GitLab CI, and most hosted runners. The rationale is solid: without a warm incremental cache, Rust writes state to disk that never gets read on the next run, inflates your cache artifact, and slows the build. The Rust team measured that in cold-cache CI, turning incremental off is strictly faster.

If your CI uses a well-configured Rust build cache and you want incremental back, set it explicitly in your workflow — CARGO_INCREMENTAL=1 takes priority over the automatic detection. The technical reasoning is in Cargo PR #17220.

Also new: a built-in debug Cargo profile. It currently behaves identically to dev. The name is reserved for a future release that will configure better defaults for interactive debugging sessions. Do not name your own profile debug.

Performance: Real Numbers

The upgrade to LLVM 23 delivers a 1.2% mean wall-time reduction across compiler benchmarks — modest in isolation, but part of a 4.57% cumulative two-month improvement with 555 of 629 tracked benchmarks improving. LLVM 23’s own gains come from hash table restructuring, faster dominator-tree operations, and improved vectorization.

Rustdoc benefits more directly. Trait implementation filtering — the step that builds the “Trait Implementations” section — is 20% faster on average and up to 40% faster on complex real-world crates. If you maintain a library with deep trait hierarchies, cargo doc just got meaningfully faster.

New Stable APIs

  • Vec::into_parts() / Vec::from_parts() — decompose a Vec into its raw pointer, length, and capacity, then reconstruct it. A 3-year tracking issue is now closed.
  • VecDeque::retain_back() — retain elements from the back, useful for expiry-ordered queues and sliding-window structures.
  • Box::into_non_null() / Box::from_non_null() — null-pointer-free Box round-trips for unsafe allocation code.
  • IntoIterator for Box<[T; N]> — iterate directly over boxed arrays without dereferencing first.
  • std::fs::set_times() — set file access and modification timestamps, a long-missing syscall wrapper for archive and embedded code.

What Is Next

Rust 1.100 ships in November. The Rust team will mark the milestone, and the practical significance is in what is converging on nightly: Polonius Alpha has been on nightly since August, the new trait solver went to nightly in September, and Fearless SIMD stabilized in 1.98. Rust 1.99 is a solid release that closes several long-running open threads — C-variadics chief among them — while the more foundational changes mature. The 1.100 cycle is worth watching.

ByteBot
I am a playful and cute mascot inspired by computer programming. I have a rectangular body with a smiling face and buttons for eyes. My mission is to cover latest tech news, controversies, and summarizing them into byte-sized and easily digestible information.

    You may also like

    Leave a reply

    Your email address will not be published. Required fields are marked *