HN
Today

Parse, Don't Validate and Type-Driven Design in Rust

This post translates the 'Parse, Don't Validate' philosophy from Haskell to Rust, advocating for encoding invariants directly into types rather than relying on runtime checks. It demonstrates how Rust's rich type system can enhance API design by making illegal states unrepresentable and proving data correctness at compile-time. The author makes a compelling case for leveraging type-driven design to create more robust, clear, and resilient code, a concept highly valued by the Hacker News community.

28
Score
3
Comments
#1
Highest Rank
3h
on Front Page
First Seen
Feb 21, 8:00 PM
Last Seen
Feb 21, 10:00 PM
Rank Over Time
122

The Lowdown

The article introduces the 'Parse, Don't Validate' principle, traditionally explained in Haskell, and recontextualizes it for Rust programmers. The core idea is to leverage Rust's type system to define types that inherently guarantee certain properties, thereby eliminating the need for repetitive runtime validation and improving code robustness.

  • The author begins with a simple divide_by_zero example, contrasting runtime panics and Option<f32> (fallible return types) with a type-driven approach using a NonZeroF32 newtype.
  • This NonZeroF32 type ensures that the divisor can never be zero, pushing validation to the point of type construction rather than function execution. This "strengthens" function parameters instead of "weakening" return types.
  • Another example, NonEmptyVec, demonstrates how guaranteeing a collection's non-emptiness at the type level removes redundant checks and makes code more resilient to refactoring errors.
  • Real-world analogies are drawn to String (a Vec<u8> that parses for UTF-8 validity) and serde_json deserialization, where structured types prevent runtime errors common with generic Value parsing.
  • Two key maxims of Type-Driven Design are highlighted: making illegal states unrepresentable (e.g., a NonZeroF32 cannot represent zero) and proving invariants as early as possible to prevent issues like "shotgun parsing" and security vulnerabilities.
  • Practical recommendations include using semantic enums instead of raw bools for clarity and considering parsing functions over simple verify functions when a more structured type can represent validated data.

In conclusion, the article champions the creation of more specific types to encapsulate data invariants, arguing that while it might introduce some verbosity or ergonomic challenges (due to Rust's current features), it ultimately leads to significantly clearer, more robust, and compile-time-verified software. The author encourages full utilization of Rust's powerful type system for better program design.