Skip to main content
  1. Blog/

Rust 1.81 Drops — Core Error Trait, Sorted Lints, and Why Rust Keeps Getting Better

·949 words·5 mins
Osmond van Hemert
Author
Osmond van Hemert
Systems & Emerging Languages - This article is part of a series.
Part : This Article

Rust 1.81.0 landed today, and while it’s not a headline-grabbing release, it contains a change that the Rust community has been wanting for years: the Error trait is now available in core. That might sound incremental if you’re not deep in the Rust ecosystem, but it’s the kind of foundational improvement that makes the language more viable in exactly the environments where it needs to grow.

core::error::Error — Why It Matters
#

Until now, the Error trait lived exclusively in std, which meant it was only available in environments with access to the standard library. If you were writing code for embedded systems, kernels, WebAssembly, or any other no_std context, you couldn’t use the Error trait at all. This created a frustrating split in the ecosystem: libraries that wanted to be usable in no_std environments had to avoid Error entirely, often implementing their own ad-hoc error handling patterns.

With Rust 1.81, core::error::Error is stabilized. This means no_std libraries can now implement and work with the standard Error trait. The practical impact is significant:

  • Embedded Rust gets proper error handling infrastructure. No more choosing between no_std compatibility and idiomatic error handling.
  • Library authors can implement Error for their error types without forcing std on their users.
  • The ecosystem moves toward a single, unified error handling pattern regardless of target environment.

I’ve been watching Rust’s embedded story develop for a few years now, and this is one of those changes that removes a real papercut. It’s not glamorous, but it’s the kind of infrastructure work that makes a language mature.

#[expect(lint)] — Better Lint Management
#

Rust 1.81 also stabilizes the #[expect()] attribute, which is a smarter version of #[allow()]. Where #[allow(unused)] silences a warning indefinitely — even after you fix the underlying issue — #[expect(unused)] tells the compiler “I expect this warning to fire here, suppress it.” If the warning doesn’t fire (because you fixed the code or the lint changed), #[expect()] itself produces a warning, alerting you that the suppression is no longer needed.

This is excellent for code hygiene. I’ve seen codebases accumulate dozens of stale #[allow()] attributes that were added during development and never cleaned up. #[expect()] makes these self-cleaning: once the suppressed issue is resolved, the attribute tells you to remove it.

// Old way - this silently stays even after you fix the issue
#[allow(unused_variables)]
let x = compute_something();

// New way - warns you when the suppression is no longer needed
#[expect(unused_variables)]
let x = compute_something();

It’s a small thing, but it’s the kind of thoughtful ergonomic improvement that makes Rust tooling a pleasure to work with.

Lint Sorting and Reason Parameter
#

Related to lint management, Rust 1.81 stabilizes a reason parameter for lint attributes:

#[expect(clippy::needless_return, reason = "Explicit returns for clarity in error paths")]

This is documentation for your future self and your teammates. When someone encounters a suppressed lint, they can immediately see why it was suppressed instead of having to dig through git blame and commit messages.

The Broader Rust Trajectory
#

Stepping back from the specific release, I think Rust’s trajectory is worth commenting on. The language continues to follow a pattern of steady, incremental improvement rather than dramatic upheaval. Each six-week release adds a few stabilizations, polishes a few rough edges, and occasionally delivers a significant feature.

Compare this to the landscape two years ago. Async Rust was notoriously painful, the learning curve was steep, and ecosystem fragmentation was a real concern. Today, async Rust is still more complex than async in other languages, but it’s dramatically better. The error handling ecosystem has largely converged around thiserror and anyhow. The tooling — rust-analyzer, cargo, clippy — is genuinely world-class.

The results show in adoption numbers. The 2023 Stack Overflow survey marked Rust as the most admired language for the eighth consecutive year. More importantly, actual production usage is growing. Linux kernel support, Android adoption, Windows kernel components, AWS (Firecracker, Lambda), Cloudflare Workers — Rust is showing up in serious production infrastructure.

Where Rust Still Has Growing to Do
#

That said, let’s be honest about the gaps:

Compile times remain the most common complaint. Large Rust projects can have punishing incremental build times. The compiler team is making progress (parallel front-end, cranelift backend for debug builds), but it’s still a real productivity drag.

The learning curve hasn’t fundamentally changed. Ownership and borrowing still trip up newcomers, and the borrow checker still occasionally rejects code that you know is correct. Improvements like non-lexical lifetimes and better error messages help, but Rust remains harder to learn than Go, Python, or TypeScript.

GUI development is still immature compared to other ecosystems. There are promising projects (Iced, Slint, Dioxus), but none have reached the maturity of, say, Qt, Electron, or SwiftUI.

Async ecosystem fragmentation is improving but not resolved. The tokio vs async-std split has largely resolved in tokio’s favor, but the lack of async traits in stable Rust (coming soon with async fn in traits) still creates friction.

My Take
#

Rust 1.81 is a good release. It’s not going to make anyone rewrite their codebase, but the core::error::Error stabilization is the kind of foundational work that enables real ecosystem growth. The #[expect()] attribute is the kind of small, thoughtful feature that makes daily Rust development more pleasant.

I continue to recommend Rust for systems programming, performance-critical services, and any context where correctness matters more than development speed. It’s not the right tool for every job — I wouldn’t reach for it to build a CRUD API when Go or even Node.js would get me there faster — but for the domains where it fits, nothing else comes close.

The six-week release train keeps delivering. That consistency is Rust’s secret weapon.

Systems & Emerging Languages - This article is part of a series.
Part : This Article