Async Rust is not safe with io_uring

A blog post arguing that “async Rust is not safe with io_uring” prompts debate over what “safety” means in Rust and whether the real issue is resource leaks and cancellation semantics rather than memory unsoundness. Commenters explain that Rust’s async model assumes futures can be safely cancelled by dropping them, but io_uring’s completion-based I/O and tricky cancellation behavior can cause file descriptor leaks or dropped connections if runtimes are not carefully designed. Many see this as a bug or immature design in specific io_uring-based libraries and a hard engineering problem around liveness and cancellation, not a fundamental flaw in Rust’s safety guarantees.

Safety in Rust: memory vs I/O vs leaks

  • Commenters stress the issue is not memory unsafety or UB.
  • Rust’s notion of “safety” covers memory safety (no corruption, UB, data races), not resource leaks.
  • I/O safety RFC is about preventing unauthorized handle access, not guaranteeing clean shutdowns.
  • Several people say the article conflates “I/O safety” with “not leaking FDs,” which is a regular bug, not “unsafety” in Rust’s usual sense.

Async Rust model and cancellation

  • Rust Futures assume: progress only when polled, and cancellation by dropping the future.
  • This works well with epoll-style readiness, where spurious wakeups are cheap.
  • With io_uring and Windows-style completion APIs, operations may complete in the kernel after a future is “cancelled,” creating tricky races.
  • Lack of “async drop” makes non-blocking cleanup on cancellation difficult.

io_uring integration challenges

  • The core bug discussed is FD leaks (and possibly dropped connections) when accept futures are cancelled while an io_uring accept completes.
  • Some argue this is a known, fundamental race in any cancellation model: cancellation can always lose to completion.
  • Others say a correct executor can still manage this by tracking completions and either cancelling or cleaning up resources when tasks lose interest.

Is this a Rust problem or a library problem?

  • Many commenters frame it as a flawed or incomplete io_uring runtime API (e.g., monoio), not a flaw in “Async Rust” itself.
  • Examples are given of alternative designs that:
    • store completed-but-unconsumed accepts and yield them later, or
    • register cancellation callbacks that run when completions arrive.
  • There is disagreement whether Async Rust and io_uring are fundamentally mismatched or just require careful reactor design.

Broader views on async, APIs, and abstractions

  • Some see this as another example of async Rust being easy to misuse and argue many codebases should avoid async entirely.
  • Others defend async/await and Rust’s model as powerful but unfinished and demanding careful engineering.
  • A side discussion notes that each OS async mechanism shift (select → epoll → io_uring) tends to invalidate large parts of existing abstractions; generic “one-size-fits-all” async layers are hard to design without leaks in the abstraction.