
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 aVecinto 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-freeBoxround-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.













