Blocking code is a leaky abstraction

Debate over “blocking” versus asynchronous code in Rust and other languages highlights confusion about what actually counts as a leaky abstraction: is it blocking calls that hide potentially long waits, or async/await systems that force “function coloring” and dependency on specific runtimes like Tokio? Commenters compare models ranging from OS threads and green threads (Go, Erlang) to event loops (JavaScript, Node.js), arguing over ergonomics, performance, and safety when mixing sync and async layers. Many conclude that both approaches leak in different ways, and that clearer annotations or effect systems — rather than ad‑hoc conventions — are needed to make I/O behavior and concurrency guarantees explicit.

What is a “leaky abstraction” here?

  • Several commenters argue the article misuses the term.
  • A leaky abstraction is described as one where hidden implementation details still affect visible behavior (e.g., ORMs over SQL, “everything is a file”, fsync semantics).
  • Some say “difficulty integrating two styles” (sync vs async) is not itself “leakiness” but just incompatibility or design cost.
  • Others argue blocking is “leaky” because whether something may block is not expressed in types and can surprise higher-level code.

Blocking vs async, and function coloring

  • Strong disagreement over whether blocking or async is the real “leak”:
    • One side: blocking is the leaky abstraction because any plain function might hide I/O or long waits.
    • Other side: async is leaky/“viral” because once one function is async, many callers must become async too.
  • Some propose three-way distinctions: async / pure / blocking, or using effect systems to express I/O and blocking explicitly.
  • A recurring theme is “function coloring” pain: having to thread async upward through large call stacks.

Rust async/await and Tokio

  • Many find Rust’s async model powerful but complex, especially around Send, runtimes, and integration with blocking code.
  • Frustration that many crates assume a specific async runtime (often Tokio) running in the background, causing runtime panics and version conflicts.
  • Suggestions include explicit runtime “tokens” in types, or effect-like annotations, though practical designs look hard.
  • Some feel async/await is a poor fit for Rust compared with green-thread models; others say it’s necessary for very high concurrency.

Threads, green threads, and alternative models

  • Several people advocate n:m green threads (Go, Erlang/BEAM, Haskell) as a better user-facing abstraction than explicit futures.
  • Others note async/await can be seen as a lower-level generalization of those models, but with more manual bookkeeping.

Domain-specific perspectives

  • Embedded developers split: some say turning blocking into async is easy; others say building sync APIs atop async can be fragile and deadlock-prone.
  • In single-threaded environments (browsers, Node), blocking is clearly harmful; async is essential.
  • UI toolkits requiring a single UI thread are seen as standard, not as a “leaky abstraction”.