What Is New in Rust 1.71
| Category | Highlights |
|---|---|
| New Features | C-unwind ABI, #[debug_visualizer] attributes, raw-dylib linking, const-initialized thread locals |
| Improvements | musl upgraded to 1.2.3, const-compatible APIs stabilized |
How does Rust 1.71 improve FFI safety with the new C-unwind ABI?
Rust 1.71 stabilizes the C-unwind ABI, letting you safely unwind across FFI boundaries.
- All existing "C" ABIs remain unchanged; the new
-unwindvariants add defined behavior for unwinding. - When
panic=unwind, a panic can propagate into foreign code and be caught on the originating side without aborting. - With
panic=abort, crossing the boundary aborts immediately, matching current expectations.
In practice, you add the suffix to your extern block:
extern "C-unwind" {
fn foreign_fn();
}
Watch out for future releases that will make the non-unwind ABIs abort at the boundary, so adopting -unwind now future-proofs your code.
Can I embed debugger visualizers in my Rust libraries in 1.71?
Yes, Rust 1.71 introduces the #[debug_visualizer] attribute for Natvis and GDB scripts.
- Place a Natvis file alongside your crate and reference it with
#[debug_visualizer(natvis_file = "my.natvis")]. - Similarly, embed a GDB script using
#[debug_visualizer(gdb_script_file = "my.gdb")].
Example:
#![crate_type = "rlib"]
#[debug_visualizer(natvis_file = "my.natvis")]
pub struct Point { pub x: i32, pub y: i32 }
This matters if you ship a library that will be inspected in Visual Studio or GDB; users get richer visual representations without extra setup.
What changes does Rust 1.71 bring for Windows dynamic linking?
Rust 1.71 adds raw-dylib linking and ordinal-based symbol binding on Windows.
- Use
#[link(kind = "raw-dylib", name = "foo")]to link against a DLL that may not be present at compile time. - The new
#[link_ordinal]attribute lets you bind to a DLL export by its ordinal number instead of its name.
Code example:
#[link(kind = "raw-dylib", name = "user32")]
extern "system" {
#[link_ordinal(0x123)]
fn SomeFunction();
}
This simplifies cross-compilation scenarios and removes the need for stub libraries in crates that wrap Windows APIs.
How are thread-local variables initialized in Rust 1.71?
Const-initialized thread locals are now stable, allowing compile-time setup of TLS values.
- The syntax uses
const { ... }inside thethread_local!macro. - This yields more optimal code because the initializer runs at program start rather than on first access.
Example:
use std::cell::Cell;
thread_local! {
pub static FOO: Cell = const { Cell::new(1) };
}
Most teams will see negligible runtime impact, but the guarantee of a constant initializer can be important for low-latency or embedded workloads.
Frequently Asked Questions
Is the C-unwind ABI backward compatible with the existing C ABI?
It behaves like the C ABI for normal calls and only defines safe behavior when unwinding crosses the boundary.
Do I need to change my Cargo.toml to use raw-dylib linking?
Add #[link(kind = "raw-dylib", name = "foo")] to your source; no Cargo.toml changes are required.
Can I provide a Natvis file for my crate's types in Rust 1.71?
Yes, annotate the crate with #[debug_visualizer(natvis_file = "my.natvis")] and the file will be embedded.
What is the syntax for const-initialized thread locals?
thread_local! { static FOO: Cell
Will upgrading to Rust 1.71 break existing code that uses the C ABI?
No, existing C ABI usage remains unchanged; the new -unwind variants are additive.
Does the musl upgrade affect binary size?
The musl 1.2.3 upgrade is transparent and should not noticeably change binary size.