Software Engineering

Applying 'Parse, don't validate' pattern to Rust development

Key Takeaway

Using specialized types instead of simple validation allows the Rust type system to enforce data invariants at compile time.

Summary

An analysis by Eli Bendersky explores the 'Parse, don't validate' idiom within the context of the Rust programming language. The article demonstrates how replacing standard types like Vec with specialized types like NonEmpty can eliminate redundant runtime checks and prevent logic errors. Real-world examples from the Rust standard library, rust-analyzer, and the nonempty crate illustrate how type refinement improves code safety and clarity.

Type-driven invariant enforcement

  • Validation vs. Parsing: Validation checks if data meets criteria but returns the same type; parsing transforms data into a new type that carries the proof of validity.
  • NonEmpty Pattern: Using a struct like NonEmpty<T> (containing a head: T and tail: Vec<T>) ensures the collection cannot be empty, allowing first() to return &T instead of Option<&T>.
  • Type Refinement: Projects like rust-analyzer use wrapper types (e.g., AbsPathBuf) to encode properties like 'absolute path' into the type itself.
  • Memory Optimization: The NonZero<T> type in Rust allows Option<NonZero<T>> to occupy the same memory space as NonZero<T> by using the zero bit pattern to represent None.

Why it matters

This pattern encourages more robust software design in the Rust ecosystem by moving runtime error handling to compile-time type guarantees.

Original reference: eli.thegreenplace.net →