Mehdi Akiki
Rust Failure Atlas / Language and diagnostics

RFA-656 · Case file with fixtures · Case 628 of 694 · Runtime evidence

Collecting Result Short-Circuits at the First Error

Result collection is fail-fast. Use it for one-error pipelines; collect or partition outcomes explicitly when validation must inspect every input.

Reviewed
Rust
Rust 1.98.1, edition 2024
Targets
all Rust targets
Profiles
dev, release, test

Direct answer

What this Rust failure means

Why it happens
A fail-fast FromIterator destination was used where complete validation or all-item side effects were expected.
First discriminating check
Choose first-error, accumulated-error, or per-item outcome semantics before composing the lazy pipeline.

Rust can collect Iterator<Item = Result<T, E>> into Result<Vec<T>, E>. The operation stops at the first error and returns it. It does not evaluate the remaining iterator. The failing fixture counts visits and shows only values zero, one, and the failing two were processed.

FromIterator defines fail-fast behaviour

collect chooses behaviour from the destination type. For Result<V, E>, the standard FromIterator implementation accumulates Ok values and stops on the first Err.

The collect documentation emphasises that type annotation determines the collection. The repaired fixture asserts the fail-fast visit count.

I use this pattern when later work should not happen after a failed prerequisite.

Validation may need every error

A form, configuration file, or batch import often benefits from reporting several independent problems at once. Fail-fast collection gives a frustrating fix-one-run-again workflow.

For full validation I map every input to an outcome and fold into separate values and errors. partition can split Ok and Err items, after which each side is unwrapped deliberately. A custom error accumulator can retain positions and field names.

I set a maximum number of errors for hostile or huge inputs. Complete validation does not require unbounded memory or messages.

Side effects make short-circuiting important

A lazy map closure after the first Err never runs. If it sends emails, deletes files, or updates external systems, behaviour depends on item order and the first failure. Hiding side effects inside a collect pipeline makes partial completion difficult to recover.

I separate validation from effects: parse and validate the batch first, then execute with an explicit transaction, idempotency, or per-item result policy. When streaming requires interleaving, I record a durable checkpoint and specify whether processing stops or continues on error.

The iterator’s already produced Ok values are dropped when the final Result is Err unless another owner retained them. They are not returned beside the error. If partial values matter for diagnostics or resume, the destination type must represent them.

Error order follows iteration order

The “first” error is the first encountered by this iterator, not necessarily the most important. Sorting, parallelism, or a HashMap source can change which error is reported.

For deterministic user feedback I process stable input order or attach positions and sort accumulated diagnostics. In parallel pipelines, fail-fast cancellation can race with work already started. I define which in-flight tasks are cancelled or drained.

Error context should be added near the item boundary so the returned first error identifies the record and operation. Logging and returning the same error in every layer creates duplicates.

Collecting Option<T> values into Option<Vec<T>> stops at the first None. The same lesson applies: destination type chooses short-circuit behaviour.

try_fold and try_for_each can express fail-fast processing without allocating a Vec. They use the same general residual/short-circuit model while allowing explicit state or effects.

I choose collect when I need all successful values only if every element succeeds. I choose try_for_each when success values need no retained collection. I choose an accumulator when partial outcomes matter.

For parsing, I keep the original input location with each error before collection erases the iterator context. A plain error string from the third record is rarely enough once the caller no longer has the successful prefix. Structured errors containing record number, field path, and safe cause make fail-fast behaviour diagnosable without storing the entire batch.

My Result collection checklist

  • Should processing stop at the first error or inspect every item?
  • Are later iterator closures expected to run after an error?
  • What happens to successful values accumulated before failure?
  • Are there external side effects hidden inside the lazy pipeline?
  • Does error order remain deterministic for the source iterator?
  • Would partition, fold, try_fold, or a custom batch result model the requirement better?
  • Is accumulated error volume bounded for large inputs?
  • Do tests assert visit count, first-error identity, partial work, and empty input?

The core principle is that collecting Result is a transactional shape for values, not for external effects: either all values are returned or the first error is. I use it for fail-fast pipelines and build an explicit accumulator when the product needs comprehensive validation.