DEV Community

Subesh Yadav
Subesh Yadav

Posted on

Day 24 of #100DaysOfRust: Loops vs Iterators & Zero-Cost Abstractions

Welcome to Day 24! Today I explored how to choose between using loops or iterators in Rust and why iterators are preferred in most idiomatic code. I also learned about zero-cost abstractions, an important concept in systems programming that Rust excels at.


🔁 Choosing Between Loops or Iterators

At first glance, using a traditional for loop might feel more intuitive, especially if you’re coming from C-style or JavaScript programming. But Rust encourages using iterators for a cleaner and more expressive approach.

Why Iterators Are Preferred:

  • Focus on what needs to be done, not how.
  • Cleaner and more declarative.
  • Less boilerplate.
  • Easier to reason about complex chains of operations.

Iterators abstract away common loop mechanics and let you concentrate on the filtering, mapping, and reducing logic.

Think of iterators as the Rusty version of chaining methods in JavaScript: expressive, powerful, and composable.


⚖️ Comparing Performance: Loops vs Iterators

You might assume that lower-level for loops are faster than iterators—but here’s the truth:

Benchmark (searching for the word "the" in Sherlock Holmes):

test bench_search_for  ... bench:  19,620,300 ns/iter (+/- 915,700)
test bench_search_iter ... bench:  19,234,900 ns/iter (+/- 657,200)
Enter fullscreen mode Exit fullscreen mode

🔍 The result? Almost identical performance.

This is because Rust’s iterators are zero-cost abstractions. The abstraction is stripped away during compilation, resulting in code that’s just as fast as hand-written loops.


🚀 What Are Zero-Cost Abstractions?

The term comes from C++ but is a core principle of Rust as well:

"What you don’t use, you don’t pay for. And what you do use, you couldn’t hand-write any better."

Rust compiles iterator chains to highly optimized machine code, often even better than what you’d write manually.

Here’s a great example from an audio decoder:

let buffer: &mut [i32];
let coefficients: [i64; 12];
let qlp_shift: i16;

for i in 12..buffer.len() {
    let prediction = coefficients.iter()
                                 .zip(&buffer[i - 12..i])
                                 .map(|(&c, &s)| c * s as i64)
                                 .sum::<i64>() >> qlp_shift;
    let delta = buffer[i];
    buffer[i] = prediction as i32 + delta;
}
Enter fullscreen mode Exit fullscreen mode

Key Optimizations:

  • Loop unrolling: No overhead loop at runtime.
  • Register allocation: All coefficients live in CPU registers.
  • No bounds checks: Rust’s safety guarantees allow removal.

These all contribute to performance that’s nearly indistinguishable from (or better than) manual code.


🧠 Summary

  • Iterators express intent clearly and reduce boilerplate.
  • Performance of iterators and loops is nearly identical.
  • Rust’s zero-cost abstractions let you write expressive high-level code with low-level performance.
  • Don't fear iterators—embrace them!

That’s it for Day 24.


📢 Follow for more on my #100DaysOfRust journey!

Top comments (0)