Syntax Cache
BlogMethodFeaturesHow It WorksBuild a Game
  1. Home
  2. Compare
  3. Python vs Rust
  4. Python vs Rust Loops

Python vs Rust Loops

Iteration styles, iterator adapters, and the functional pipeline approach that Rust favors over Python comprehensions.

For Loops

Both languages use for...in syntax, which is unusual enough to note -- most C-family languages use index-based for loops, but Python and Rust both iterate over values directly. Python's for x in items: and Rust's for x in items {} look nearly identical, and the behavior aligns: each iteration binds x to the next value. The difference hides in ownership. Python objects are reference-counted, so iterating over a list does not consume it. Rust's iterator consumes the source by default: for x in vec moves the vector, and you cannot use vec afterward. To iterate without consuming, you iterate over a reference: for x in &vec (immutable borrow) or for x in &mut vec (mutable borrow). This three-way choice (move, borrow, mutable borrow) has no Python analogue. Python developers can start by always using &vec and treating it as a read-only iteration, which matches Python's behavior. The explicit borrow syntax feels verbose at first, but it documents intent: anyone reading for x in &mut items knows the loop body mutates elements.

Iterate over a collection

Python
colors = ["red", "green", "blue"]
for color in colors:
    print(color)
# colors still usable
Rust
let colors = vec!["red", "green", "blue"];
for color in &colors {
    println!("{color}");
}
// colors still usable (borrowed)

Range-based loop

Python
for i in range(5):
    print(i)  # 0..4
Rust
for i in 0..5 {
    println!("{i}"); // 0..4
}

Enumerate and Zip

Python's enumerate() returns (index, value) pairs and is one of the first standard library functions new Python developers learn. Rust has .enumerate() as a method on iterators, producing (usize, T) tuples. The syntax difference is minor: Python wraps the iterable (enumerate(items)), while Rust chains the method (items.iter().enumerate()). zip() also exists in both ecosystems with matching semantics -- it pairs elements from two iterables and stops at the shorter one. Python's itertools.zip_longest fills missing values with a default; Rust's equivalent is the itertools crate's zip_longest or the standard library's .zip() combined with .chain(). The pattern that trips Python developers: Rust's .enumerate() returns a tuple where the index is the first element, matching Python, but destructuring syntax differs. Python uses for i, item in enumerate(items):, while Rust uses for (i, item) in items.iter().enumerate() {}. Parentheses around the destructured pattern are mandatory in Rust because tuples use parentheses, while Python allows either form. Both communities consider enumerate preferable to manual index tracking.

Enumerate

Python
for i, color in enumerate(colors):
    print(f"{i}: {color}")
Rust
for (i, color) in colors.iter().enumerate() {
    println!("{i}: {color}");
}

Zip two collections

Python
names = ["Alice", "Bob"]
scores = [95, 87]
for name, score in zip(names, scores):
    print(f"{name}: {score}")
Rust
let names = ["Alice", "Bob"];
let scores = [95, 87];
for (name, score) in names.iter().zip(scores.iter()) {
    println!("{name}: {score}");
}

List Comprehension vs Iterator Chains

Python developers reach for list comprehensions instinctively: [x * 2 for x in nums if x > 0]. Rust has no comprehension syntax. The equivalent is an iterator chain: nums.iter().filter(|&&x| x > 0).map(|&x| x * 2).collect::<Vec<_>>(). The chain reads in processing order (filter, then map, then collect), while comprehensions read in generation order (source, transform, filter). Neither ordering is universally clearer; preference tends to align with whichever language you learned first. The collect::<Vec<_>>() call at the end is unfamiliar to Python developers. Rust iterators are lazy -- .filter() and .map() produce new iterator adapters without doing any work. collect() drives the chain and assembles the results into a concrete collection. The turbofish (::<Vec<_>>) tells the compiler what collection type to produce, since Rust iterators can collect into Vec, HashSet, HashMap, String, and other types. Python's comprehensions always produce a list (or dict/set with the appropriate brackets), so the collection type is implicit. Rust's explicitness adds verbosity but also flexibility -- the same iterator chain can produce different collection types by changing the turbofish annotation.

Filter and transform

Python
nums = [1, -2, 3, -4, 5]
result = [n * 2 for n in nums if n > 0]
# => [2, 6, 10]
Rust
let nums = vec![1, -2, 3, -4, 5];
let result: Vec<i32> = nums.iter()
    .filter(|&&n| n > 0)
    .map(|&n| n * 2)
    .collect();
// => [2, 6, 10]

Collect into different types

Python
words = ["hello", "world", "hello"]
unique = set(words)
# => {"hello", "world"}
Rust
use std::collections::HashSet;
let words = vec!["hello", "world", "hello"];
let unique: HashSet<_> = words.into_iter().collect();
// => {"hello", "world"}

Loop Control and Labeled Breaks

Python and Rust share break and continue with identical semantics for single-level loops. The divergence appears with nested loops. Python has no mechanism for breaking out of an outer loop from an inner one -- developers use flag variables, extract the nested loop into a function, or restructure the logic. Rust supports labeled breaks: 'outer: for ... { for ... { break 'outer; } }. The label syntax uses a leading apostrophe (a lifetime-like annotation) and lets you target any enclosing loop by name. Rust also has a loop {} construct -- an infinite loop that you exit with break. This is distinct from while true {} because the compiler knows that loop {} always runs at least once, enabling better flow analysis. break inside a loop {} can return a value: let result = loop { break 42; }. Python has no value-returning break; you assign to a variable before breaking. The loop-as-expression pattern is useful for retry logic and input validation, where you want to run code until a condition is met and then use the result. It is one of the places where Rust's "everything is an expression" philosophy shows clear ergonomic benefit.

Labeled break from nested loop

Python
# No labeled break in Python
found = False
for row in matrix:
    for cell in row:
        if cell == target:
            found = True
            break
    if found:
        break
Rust
'outer: for row in &matrix {
    for cell in row {
        if *cell == target {
            break 'outer;
        }
    }
}

Value-returning loop

Python
while True:
    line = input("> ")
    if line.strip():
        result = line.strip()
        break
Rust
let result = loop {
    let line = read_input();
    let trimmed = line.trim();
    if !trimmed.is_empty() {
        break trimmed.to_string();
    }
};

Practice Both Languages

10 free exercises a day. No credit card required. Build syntax muscle memory with spaced repetition.

Free forever. No credit card required.

← Back to Python vs Rust
Syntax Cache

Build syntax muscle memory with spaced repetition.

Product

  • Pricing
  • Our Method
  • Daily Practice
  • Design Patterns
  • Interview Prep

Resources

  • Blog
  • Compare
  • Cheat Sheets
  • Vibe Coding
  • Muscle Memory

Languages

  • Python
  • JavaScript
  • TypeScript
  • Rust
  • SQL
  • GDScript

Legal

  • Terms
  • Privacy
  • Contact

© 2026 Syntax Cache

Cancel anytime in 2 clicks. Keep access until the end of your billing period.

No refunds for partial billing periods.