What Is New in Rust 1.77
| Category | Highlights |
|---|---|
| New Features | C-string literals, recursive async functions, offset_of! macro, default strip in release profiles |
| Improvements | Release profiles now strip debug info by default, reducing binary bloat when debuginfo is disabled |
How do C-string literals simplify FFI in Rust?
The new c"..." literal creates a &'static CStr with compile-time validation of interior nul bytes.
- Write
c"hello"instead of manually constructing aCStror usingCString::new. - The compiler guarantees the string ends with a single nul byte and contains no interior nul, preventing undefined behaviour at runtime.
- This matters if your crate talks to C APIs, Windows DLLs, or any foreign interface that expects nul-terminated strings.
use std::ffi::CStr;
extern "C" {
fn puts(s: *const i8);
}
fn greet() {
unsafe { puts(c"Hello from Rust!\0".as_ptr()) };
}
Can async functions be recursive in Rust 1.77?
Yes, async functions can now call themselves as long as the recursion is indirected to keep the future size bounded.
- The compiler lifted the previous limitation that treated self-calls as infinite size.
- Typical pattern: box the recursive call with
Box::pinor use a custom future type. - This is useful for algorithms like async Fibonacci, tree traversal, or state-machine loops.
async fn fib(n: u32) -> u32 {
match n {
0 | 1 => 1,
_ => Box::pin(fib(n - 1)).await + Box::pin(fib(n - 2)).await,
}
}
What is offset_of! and when should I use it?
The offset_of! macro returns the byte offset of a public struct field without needing an instance.
- It expands to a
usizeconstant, safe to use in const contexts. - Ideal for low-level interop, custom memory layout calculations, or implementing safe wrappers around raw pointers.
- Watch out for changes in struct layout across compiler versions; the macro reflects the layout of the current compilation target.
#[repr(C)]
struct Header {
magic: u32,
version: u16,
flags: u16,
}
const VERSION_OFFSET: usize = offset_of!(Header, version);
How does the default strip setting affect release builds?
Release profiles that disable debug info now automatically strip debug symbols from the final binary.
- This reduces binary size for statically linked executables, especially when the standard library is compiled with debuginfo.
- If you need full debug information for profiling, re-enable it with
debug = 1(or higher) in the relevant Cargo profile. - Most CI pipelines benefit from smaller artifacts without sacrificing runtime performance.
Frequently Asked Questions
Do I need to change existing code to use C-string literals?
No existing code is affected; you can adopt c"..." literals where appropriate without breaking changes.
Is recursion in async functions safe for large call depths?
It is safe as long as each recursive step is boxed or otherwise heap-allocated, preventing stack overflow.
Can offset_of! be used on private fields?
No, the macro only works with public fields; private fields remain inaccessible to preserve encapsulation.
Will the new default strip behavior remove symbols I need for crash reports?
You can keep symbols by enabling debug info in the Cargo profile, which overrides the default strip setting.
Is there a command-line example showing how to enable debug info again?
Add debug = 2 under [profile.release] in Cargo.toml to retain full debug symbols.