Urgent.News

What's breaking now, across thousands of outlets.

Tech

Rusty thoughts on "Parse, don't validate"

The "Parse, don't validate" article by Alexis King intrigued many programmers, as it highlights a familiar, valuable coding approach that has been observed and utilized previously without a formal name. This review focuses on the pattern's application in Rust programming, drawing insights from the original post which employs Haskell.

The concept begins with the Vec type in Rust, whose 'first' method returns an Option<&T>. This is due to vectors potentially having no elements, making it unclear what to do if 'first' is called on an empty vector. To address this, Rust uses the Option type for idiomatic handling, with convenient syntax for dealing with results that may or may not exist. Consider a function that reads configuration paths from an environment variable, enforcing the rule that the list cannot be empty.

The challenge arises when using the 'first' method on the resulting vector. If the function returns a successful result, the vector is guaranteed not to be empty. However, accessing the first element still requires handling a potential empty case. The core issue lies in the lack of a formally enforced check that the vector cannot be empty.

The solution involves leveraging Rust's type system to enforce a new invariant. A separate type, such as NonEmpty, can be used to ensure a vector cannot be empty. This type prevents the possibility of creating an empty vector during construction and provides a first method that returns &T without an Option.

Several Rust crates, like nonempty, already implement NonEmpty, allowing it to behave similarly to Vec while enforcing the non-empty invariant. Using NonEmpty in functions like get_configuration_directories eliminates the need to repeatedly check for an empty list, as the type system enforces this constraint. This approach aligns with the "parse vs. validate" terminology from King's article.

When get_configuration_directories returns a Vec, it validates the input but leaves the responsibility of ensuring the vector is non-empty to the caller. In contrast, returning a NonEmpty type enforces the non-emptiness at the type level, simplifying client code.

The "parse, don't validate" pattern extends beyond Vec. Rust's core POSIX utilities rewrite uses NonEmpty in various scenarios, such as constructing a shell pipeline where a valid Pipeline is returned only if there are commands in the parsed Abstract Syntax Tree (AST). If no commands are present, the Pipeline returns None. This approach eliminates the need to check for a None value in client code after parsing.

Another example comes from rust-analyzer, a project that represents absolute filesystem paths using AbsPathBuf, a type that enforces the path's absoluteness. The AbsPathBuf type wraps Utf8PathBuf, a custom type refinement ensuring valid UTF-8 paths. This gradual parsing and type refinement ensure path validity without the need for constant validation checks in subsequent code.

Rust's NonZero type further illustrates the "parse, don't validate" pattern. Numeric types like NonZeroUsize enforce that their values are never zero. When used as the denominator in division operations, they cannot panic, as zero is an invalid value for the type. This enshrines the non-zero constraint in the type system, eliminating the need for explicit validation checks.

In JSON deserialization, the serde crate exemplifies the "parse, don't validate" idiom. Deserializing data from a JSON string parses the data, and the resulting types inherently carry validated information. For instance, converting a JSON string to a Rust enum eliminates the need for additional validation after parsing. In dynamic languages like Python or JavaScript, similar processes often require manual validation of parsed data, making Rust's approach more streamlined.

Overall, the "parse, don't validate" pattern is a powerful technique for enforcing invariants at the type level, reducing the need for manual validation checks and improving code clarity. This approach, seen across various Rust projects, demonstrates its versatility and effectiveness in managing data integrity and simplifying client code.

Written by urgent.news from Lobsters's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.

Read the original at eli.thegreenplace.net →

More in Tech

From HTML to JavaScript: My Learning Journey

Every developer starts somewhere. This is the story of how I began learning HTML, CSS, and JavaScript, and the lessons I've learned along the way Introduction Technology has always fascinated me…

More from Saturday 26 September →