Rust in 2025: What's New? Practical Features
2025 has become a landmark year for the Rust ecosystem. The language, which has already won the hearts of systems programmers, web developers, and CLI tool creators, has received a number of long-awaited updates. While 2023-2024 focused primarily on stabilization and tooling improvements, 2025 shifts the emphasis to radically simplifying the writing of complex code and boosting performance without sacrificing safety.
In this article, we will break down three key innovations already available in the stable version of Rust 2025 and demonstrate them with concrete examples. Whether you follow Rust's development or are just starting to learn it, this material will help you get up to speed quickly.
1. Async Iterators: async gen and async for
One of the hottest topics in 2025 has been the stabilization of async generators and iterators. Previously, writing an async iterator required manually implementing the Stream trait or using third-party crates. Now it's much simpler and more elegant.
The key innovation is the async keyword combined with gen and the ability to use async for in loops. This allows you to write code that asynchronously generates and consumes sequences of data without blocking the execution thread.
Example: Async Fibonacci Number Generator
use std::time::Duration;use tokio::time::sleep;
// An async generator that "yields" Fibonacci numbers with a delayasync fn async_fibonacci(limit: u32) -> impl AsyncIterator<Item = u64> { async gen { let (mut a, mut b) = (0, 1); for _ in 0..limit { sleep(Duration::from_millis(100)).await; // Simulating async work yield b; // The yield keyword let next = a + b; a = b; b = next; } }}
#[tokio::main]async fn main() { println!("Async Fibonacci numbers (first 10):"); // Using async for to consume async for number in async_fibonacci(10) { println!("Received: {}", number); } println!("Done!");}What does this mean in practice?
- Simplified code: No more need to write complex combinators from the
futurescrate. - Readability: The code looks like a regular synchronous generator but works asynchronously.
- Performance: The compiler optimizes such constructs, minimizing overhead.
This is especially useful for processing data streams from the network, reading large files, or working with WebSocket connections.
2. Improved Borrow Checker: Polonius Stabilized
The borrow checker is the "soul" of Rust, responsible for memory safety without a garbage collector. However, beginners (and even experienced developers) often encountered situations where the compiler rejected perfectly safe code. In 2025, the Polonius borrow model has finally become part of the stable compiler.
Polonius uses a more precise data-flow analysis, allowing the compiler to "understand" more complex patterns of working with references.
Example: The "Two Mutable Borrows" Problem
Previously, this code would not compile:
fn push_and_pop(vec: &mut Vec<i32>) -> &i32 { vec.push(1); // Error: cannot borrow as mutable (push) and then as immutable (pop) let last = vec.last().unwrap(); last}In Rust 2025 with Polonius, this works! The compiler sees that after push, the mutable reference is no longer used, and allows taking an immutable reference to the last element. Here's how