HN
Today

The Cost of Indirection in Rust

This Rust deep dive challenges the common wisdom that 'indirection has a cost,' particularly in async code, arguing that compilers often optimize it away for free. It meticulously breaks down how modern Rust compilers handle function calls and await points, showing that perceived performance hits are often negligible. Ultimately, the article advocates for prioritizing code readability and maintainability over premature micro-optimizations, a sentiment often debated and valued by the HN community.

15
Score
1
Comments
#8
Highest Rank
4h
on Front Page
First Seen
Mar 12, 4:00 PM
Last Seen
Mar 12, 7:00 PM
Rank Over Time
8101217

The Lowdown

The article 'The Cost of Indirection in Rust' tackles a pervasive misconception in software development: the belief that every function call adds significant overhead, leading developers to sacrifice code clarity for perceived performance gains. Author Sebastian Sastre, however, argues that in modern Rust, especially with async functions, this worry is almost always unfounded, and in fact, prioritizing maintainability often aligns with optimal performance in practice.

  • A common scenario involves extracting complex logic from a large match arm into a separate async function for improved readability, only to face concerns about 'extra function calls' and 'indirection costs.'
  • While technical concerns like parameter passing, future state machine setup, and stack frame management exist with function calls, the article asserts these are often negligible compared to the actual work being done (e.g., I/O, locks, allocations).
  • Crucially, when awaiting an async function, the Rust compiler can merge the callee's state into the parent Future's state machine, making the abstraction 'genuinely free' in many cases.
  • In release mode with optimizations, the compiler frequently inlines small extracted functions, potentially resulting in identical assembly for both the inlined and extracted versions of code.
  • The author provides a practical methodology for verification: examining generated assembly using cargo rustc --release -- --emit asm and performing benchmarks with tools like Criterion, looking for overlapping error bars to indicate statistically insignificant differences.
  • Indirection does matter in specific, performance-critical scenarios such as tight inner loops, dynamic dispatch via dyn Trait, or explicit performance-critical paths where the compiler loses visibility.
  • However, these are distinct from typical application-level event handling; if a match arm runs millions of times per second, one would have more fundamental architectural issues.
  • The article emphasizes the 'human-centric costs' of avoiding indirection: increased cognitive load, slower code reviews, higher bug risk, and decreased developer productivity due to reduced readability and maintainability.
  • Rust's design philosophy encourages clean abstractions and trusting the optimizer, reserving explicit inlining (#[inline]) for measured problems.

The core takeaway is that the 'cost' of indirection in Rust is often negligible in release builds, frequently optimizing to zero, while the real cost of foregoing it is the sacrifice of clarity, testability, and developer productivity. The author urges developers to measure performance and optimize for the human element first, letting compilers handle micro-optimizations, and to use well-named functions to provide meaning and improve system understanding, especially in an era of increasingly sophisticated AI coding agents.