Recent Rust Changes

In May last year I wrote a blog post on how Rust had evolved from the 1.0 release to 1.78. I found it really interesting to group all the changes together by topic, rather than seeing the language evolve one release at a time. We're now at 1.90, so I thought it was time for another update. It's only been a year and a half, but quite a lot has happened (much more than I realised before writing this post). As well as a bunch of new features (listed below), there was the 2024 edition (released in February 2025), Rust had it's 10th birthday in May, and Rust now has an official language specification in the form of the FLS.

I've picked the most exciting stuff (IMO) and summarised it below. Of course it's only a fraction of everything which has happened; in particular, there's been more std library stabilisations and const-ifications than I could possibly list. Check the release announcements on the Rust blog for full summaries of each version and for future updates.

Language

  • Async closures async |...| { ... } (1.85).
  • let chains, i.e., using let with any && clause in an if or while expression (not just the first, as in if let) (1.88 and 2024).
  • Naked functions (1.88).
  • Changes to rules around lifetimes with impl Trait in return position (2024), and use<..>` syntax for lifetime bounds (see the chapter in the edition guide or the RFC for details) (1.82, 1.87 for trait functions).
  • &raw ... oeprator to create a raw pointer (and to take a reference to a mutable or extern static without an unsafe block) (1.82, using non-raw & is an error in 2024).
  • Trait object upcasting (implicit coercion) (1.86).
  • unsafe extern blocks and safe keyword (1.82).
  • unsafe in attributes (1.82, error to use unsafe attributes without unsafe in 2024).
  • Unsafe code inside an unsafe function requires an unsafe block (2024).
  • _ to infer const generics arguments (1.89).
  • Inline const blocks, e.g., const { std::mem::size_of::<T>() + 1 } (1.79).
  • inline assembly: const immediates (1.82) and jumps to Rust code (1.87).
  • Exclusive ranges in patterns, e.g., match i { 0..10 => println!("9 or under"), ... } (1.80).
  • expect lint level, e.g., #[expect(unused)] (as a better alternative to #[allow(dead_code)]) and reason in lint attributes (1.81).
  • Bounds on associated types in bounds, e.g., trait CopyIterator: Iterator<Item: Copy> {} (1.79).
  • #[diagnostic::do_not_recommend] attribute (1.85).
  • Unwinding inside an extern(C) function now aborts (1.81).
  • Dereferencing or reborrowing a null raw pointer will panic rather than being undefined behaviour in debug builds (1.86).
  • Lifetime extension from inside if and match blocks, e.g., let x = if ... { &new() } ... ; is legal because the lifetime of the reference is extended to the enclosing scope, rather than being restricted to the block which is part of the if expression (1.79). Changes to rules around temporary lifetimes for if let and the last expression in a block (2024).
  • No need for pattern matching branches for impossible variants (e.g, using ! or Infallible) (1.82).
  • Restrictions on using explicit referencing in patterns with match ergonomics (2024).

Standard library

  • LazyCell and LazyLock, alternatives to the lazy_static and once_cell crates (1.80).
  • io::pipe and associated types (1.87).
  • File::lock, etc. (1.89).
  • Error trait moved from std to core (1.81).
  • Most of the API for NonNull (1.80, 1.84).
  • get_disjoint_mut on slices and hashmaps (1.86).
  • Option::take_if (1.80), Option::is_none_or (1.82), Option::get_or_insert_default (1.83).
  • collect iterators into tuples (1.85).
  • Vec::into_flattened and as_flattened on arrays (1.80).
  • Vec::pop_if (1.86) and Vec::extract_if (1.87, for hashmaps in 1.88).
  • is_sorted on various types (1.82).
  • Cell::update (1.88).
  • str::from_utf8 (1.87).
  • API for uninitialised memory in Box, Rc, and Arc (1.82).
  • next_down and next_up on floats (1.86).
  • More ControlFlow API (1.83).
  • Entry::insert_entry (1.83).
  • hint::assert_unchecked (1.81).
  • hint::select_unpredictable (1.88).
  • wait on Once and OnceLock (1.86).
  • Rename PanicInfo to PanicHookInfo and some panic message changes (1.81).
  • API for deconstructing Wakers (1.83).
  • More io::ErrorKind variants (1.83 and 1.85).
  • More proc_macro::Span API (1.88).
  • Strict provenance for pointers (1.84).
  • Improvements to sort algorithms (1.81).
  • Future and IntoFuture added to the prelude (2024).

Tooling

  • cargo info to display information about a crate (1.82).
  • Cargo MSRV-aware resolver (1.84, default in 2024).
  • Cargo automatically cleans up its cache (1.88).
  • cargo publish --workspace (1.90).
  • Checking of cfg names and values (1.80).
  • Apple ARM is a tier 1 target (1.82) and Apple intel demoted to tier 2 (1.90).
  • Standard library is built with frame pointers (great for profiling!) (1.79).
  • Delete crates from crates.io.
  • 'Report crate' on crates.io.
  • Trusted publishing and alerts in README.md on crates.io.
  • rustc on Linux uses lld by default (1.90).

I currently have availability for Rust coaching, adoption, or development; from a single call to ongoing 3 days/week. I can help your team get things done, adopt Rust and use it more effectively, or to accurately evaluate Rust as a new technology.

If you're adopting Rust, I can help make that a success with advice, 1:1 or group mentoring, design and code review, or online support. Coaching.

If you're building with Rust and need a short or medium-term boost, I can join your team, quickly get up to speed, and deliver value. I have expertise with async and unsafe code, database implementation, distributed systems, dev tools, and language implementation. Consulting.