{
  "@context": "https://schema.org",
  "@id": "https://aggr.aymericbeaumet.com/items/lobste-rs/2026-09-26-rusty-thoughts-on-parse-don-t-validate/#webpage",
  "@type": [
    "WebPage",
    "ArchiveComponent"
  ],
  "articleBody": "Like many programmers, I find Alexis King's [Parse, don't validate](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/) article fascinating, because it gives a name to an idiom that seems familiar and important - one I've observed and used in the past without naming it explicitly. This post is a review of the \"Parse, don't validate\" pattern applied to the Rust programming language (the original post uses Haskell). I was particularly interested in finding educational examples of this pattern in the Rust standard library and other well-known projects.\n\nWithout repeating the original article (please read it first!), here's the gist of it.\n\nConsider the venerable `Vec`; its `first` method returns `Option<&T>`. Why? Because a vector is not guaranteed to have any elements in it, so what to do if `first` is invoked on an empty one? Returning an `Option` in this case is idiomatic in Rust [\\[1\\]](https://eli.thegreenplace.net/2026/rusty-thoughts-on-parse-dont-validate/#footnote-1), with convenient syntax sugar for accepting the result of functions that return `Option` and deciding what to do next.\n\nSo what's the issue?\n\nImagine we have a function to read some configuration paths from an env var, while enforcing the invariant that the list can't be empty:\n\n```\nuse anyhow::{Result, ensure};\n\nfn get_configuration_directories() -> Result<Vec<PathBuf>> {\n    let value = env::var(\"CONFIG_DIRS\").context(\"could not read CONFIG_DIRS\")?;\n\n    let directories: Vec<PathBuf> = value\n        .split(',')\n        .map(str::trim)\n        .map(PathBuf::from)\n        .collect();\n\n    ensure!(!directories.is_empty(), \"empty CONFIG_DIRS\");\n    Ok(directories)\n}\n```\n\nSo far, so good. Now let's take a typical usage of this function:\n\n```\nfn main() -> Result<()> {\n    let config_dirs = get_configuration_directories()?;\n\n    match config_dirs.first() {\n        Some(cache_dir) => initialize_cache(cache_dir),\n        None => unreachable!(\"already checked that CONFIG_DIRS is non-empty\"),\n    }\n\n    Ok(())\n}\n```\n\nOnce `get_configuration_directories` returns a successful result, we are guaranteed that the vector isn't empty. And yet, if we want to get the first element of this vector, we have to use the `first` method that returns `Option<&T>`. We are therefore forced - again - to handle a potentially empty case (where the option is `None`).\n\nAs the original article states, this has a number of problems with code clarity, potential performance implications and a ticking time bomb if the invariant is ever changed in `get_configuration_directories`.\n\nThe core issue is that `Vec` is fundamentally a type that can be empty; we can carry along a \"This one can't be empty, pinky promise!\" comment on all the relevant code, but it's not formally checked by anything.\n\n## A type for \"non-empty\" vector\n\nThe solution is leveraging the type system to enforce a newly established invariant. We can use a separate type for \"a vector that cannot be empty\"; in fact, such types already exist in several Rust crates - for example [nonempty](https://docs.rs/nonempty/latest/nonempty/):\n\n```\npub struct NonEmpty<T> {\n    pub head: T,\n    pub tail: Vec<T>,\n}\n```\n\nThis type has no constructor that permits \"no elements\"; its `new` takes one element, and its `first` method returns `&T` without an `Option`:\n\n```\npub const fn new(e: T) -> Self {\n    Self::singleton(e)\n}\n\npub const fn singleton(head: T) -> Self {\n    NonEmpty {\n        head,\n        tail: Vec::new(),\n    }\n}\n\npub const fn first(&self) -> &T {\n    &self.head\n}\n```\n\nThe rest of the crate deals with making `NonEmpty` behave as close as possible to a normal `Vec`, by implementing many useful traits, as well as conversions like:\n\n```\npub fn from_vec(mut vec: Vec<T>) -> Option<NonEmpty<T>> {\n    if vec.is_empty() {\n        None\n    } else {\n        let head = vec.remove(0);\n        Some(NonEmpty { head, tail: vec })\n    }\n}\n```\n\nLet's see how our `get_configuration_directories` function would look if it returned a `NonEmpty` instead of a plain `Vec`:\n\n```\nfn get_configuration_directories() -> Result<NonEmpty<PathBuf>> {\n    let value = env::var(\"CONFIG_DIRS\").context(\"could not read CONFIG_DIRS\")?;\n\n    let directories = value\n        .split(',')\n        .map(str::trim)\n        .map(PathBuf::from)\n        .collect();\n\n    let Some(directories) = NonEmpty::from_vec(directories) else {\n        bail!(\"CONFIG_DIRS cannot be empty\");\n    };\n\n    Ok(directories)\n}\n```\n\nNote the use of `NonEmpty::from_vec` here - this is where the invariant is established. Now a successful result is `NonEmpty`, not just `Vec`. The client code looks like:\n\n```\nfn main() -> Result<()> {\n    let config_dirs = get_configuration_directories()?;\n\n    initialize_cache(config_dirs.first())?;\n    Ok(())\n}\n```\n\nThere's no need to check if the returned value is empty again; this is enforced by the type system!\n\nThis is where the parse vs. validate terminology of the original article comes from. When `get_configuration_directories` returned a `Vec`, it simply validated it. But when it returns a `NonEmpty` - the vector is transformed into another entity which carries additional meaning. If we treat the concept of parsing in the most generic sense - \"transforming data from one format to another\", this fits.\n\nTo mention a less artificial example, the [Rust rewrite of core POSIX utilities](https://github.com/rustcoreutils/posixutils-rs) uses `NonEmpty` in several places [\\[2\\]](https://eli.thegreenplace.net/2026/rusty-thoughts-on-parse-dont-validate/#footnote-2). For example, when constructing a shell pipeline:\n\n```\npub struct Pipeline {\n    pub commands: NonEmpty<Command>,\n    pub negate_status: bool,\n}\n```\n\nThe command parser's code:\n\n```\nfn parse_pipeline(&mut self, alias_table: &AliasTable) -> ParseResult<Option<Pipeline>> {\n    // pipeline = \"!\" command (\"|\" linebreak command)*\n    let negate_status = self.match_alternatives(&[CommandToken::Bang])?.is_some();\n    let mut commands = if let Some(command) = self.parse_command(alias_table)? {\n        NonEmpty::new(command)\n    } else {\n        return Ok(None);\n    };\n\n    // ...\n```\n\nA valid `Pipeline` is only returned if there are some commands in the parsed AST. Otherwise, it just returns `None`. Once this is done, the client code can use `commands.first()` without having to worry about the possibility of it returning `None`.\n\n## Gradual parsing and type refinement\n\nA somewhat more interesting example can be found in the source code of [rust-analyzer](https://github.com/rust-lang/rust-analyzer). This project has a type that represents an absolute filesystem path:\n\n```\npub struct AbsPathBuf(Utf8PathBuf);\n```\n\nInstead of carrying around a regular path, the absoluteness is recorded in the type once the initial parsing and validation is done:\n\n```\nimpl TryFrom<Utf8PathBuf> for AbsPathBuf {\n    type Error = Utf8PathBuf;\n    fn try_from(path_buf: Utf8PathBuf) -> Result<AbsPathBuf, Utf8PathBuf> {\n        if !path_buf.is_absolute() {\n            return Err(path_buf);\n        }\n        Ok(AbsPathBuf(path_buf))\n    }\n}\n```\n\nSubsequent code doesn't have to validate the the path is absolute. The type enforces it.\n\nNote also that `AbsPathBuf` wraps `Utf8PathBuf`, not `PathBuf`. `Utf8PathBuf` is itself a custom, \"parsed\" type refinement from the [camino crate](https://docs.rs/camino/latest/camino/). Regular paths in the Rust standard library aren't guaranteed to be valid UTF-8, so they cannot be easily converted to a `String` (which has to be valid UTF-8 in Rust); `camino::Utf8PathBuf` establishes validity on construction, and can then be converted to a string with just:\n\n```\nfn as_str(&self) -> &str {\n  ...\n}\n```\n\nSo we have an example of gradual parsing and type refinement here:\n\n```\nstd::path::PathBuf\n\n      |\n      |   prove UTF-8\n      |\n      V\n\ncamino::Utf8PathBuf\n\n      |\n      |   prove absolute\n      |\n      V\n\nrust-analyzer's paths::AbsPathBuf\n```\n\n## Non-zero integers\n\nRust has a generic type called [NonZero](https://doc.rust-lang.org/std/num/struct.NonZero.html), to describe unsigned numeric quantities that are known to be non-zero.\n\nFor example, `thread::available_parallelism` is defined as:\n\n```\npub fn available_parallelism() -> Result<NonZero<usize>>\n```\n\nIf the call is successful, it returns a `NonZero<usize>`, which is like a normal `usize` with the restriction that it's not zero. Client code doesn't have to keep checking whether the parallelism is 0 - it's enshrined in the type system.\n\nRust defines the [division operator](https://doc.rust-lang.org/std/primitive.u32.html#impl-Div%3CNonZero%3Cu32%3E%3E-for-u32) with `NonZero<usize>` in the denominator as an operation that \"cannot panic\".\n\n`NonZero` has an additional advantage: zero is an invalid value for the type, so Rust can use the zero bit pattern to represent `None`. Consequently, `Option<NonZeroUsize>` is [guaranteed to have the same size](https://doc.rust-lang.org/std/option/index.html#representation) and alignment as `NonZeroUsize` itself (and as `usize`). This can avoid the extra storage that an `Option<usize>` would generally require.\n\n## Parsing JSON\n\nA common example of the \"parse, don't validate\" idiom appears in deserializing data from a JSON string. Rust's `serde` crate enables us to do the parsing, with validated decisions encoded into the type system, e.g.:\n\n```\n#[derive(Debug, Deserialize)]\nstruct Config {\n    name: String,\n    workers: NonZeroUsize,\n    mode: Mode,\n}\n\n#[derive(Debug, Deserialize)]\n#[serde(rename_all = \"snake_case\")]\nenum Mode {\n    Fast,\n    Safe,\n}\n```\n\nAnd then later:\n\n```\nlet input = r#\"\n     {\n         \"name\": \"compiler\",\n         \"workers\": 4,\n         \"mode\": \"fast\"\n     }\n \"#;\n\n let config: Config = serde_json::from_str(input)?;\n```\n\nThere is a lot happening behind the scenes:\n\n- The types of all fields are enforced (e.g. \"name\" cannot be an array).\n- The `mode` is validated to be one of the enum values of `Mode`.\n- `workers` is validated to be a non-zero integer, because of the `NonZeroUsize` field type.\n\nWe take code like this for granted these days, but it's still a great example of the pattern discussed in this post. Once the parser converted `mode` into the `Mode` enum, no further validation is required.\n\nIn dynamic languages like Python and JS, the process is usually much more manual. Python's `json.loads` gives us a dictionary, and it's up to the user to validate its contents. Libraries like Pydantic permit an approach closer to Rust's, but they're not universally used.\n\n* * *\n\n[\\[1\\]](https://eli.thegreenplace.net/2026/rusty-thoughts-on-parse-dont-validate/#footnote-reference-1)\n\nOther languages - like Go or Python - have a runtime check that raises some sort of exception or panic when `lst[0]` is accessed on an empty list or slice.\n\n[\\[2\\]](https://eli.thegreenplace.net/2026/rusty-thoughts-on-parse-dont-validate/#footnote-reference-2)\n\nThe project implements its own `NonEmpty`, without relying on the `nonempty` crate, but all the points in this post apply.\n\n* * *\n\nFor comments, please send me [an email](mailto:eliben@gmail.com).\n",
  "authors": [
    "eli.thegreenplace.net via carlana"
  ],
  "captured_at": "2026-09-28T15:21:52.698000543+00:00",
  "category": "news",
  "content_html": "<p>Like many programmers, I find Alexis King's <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/\">Parse, don't validate</a> article fascinating, because it gives a name to an idiom that seems familiar and important - one I've observed and used in the past without naming it explicitly. This post is a review of the &quot;Parse, don't validate&quot; pattern applied to the Rust programming language (the original post uses Haskell). I was particularly interested in finding educational examples of this pattern in the Rust standard library and other well-known projects.</p>\n<p>Without repeating the original article (please read it first!), here's the gist of it.</p>\n<p>Consider the venerable <code>Vec</code>; its <code>first</code> method returns <code>Option&lt;&amp;T&gt;</code>. Why? Because a vector is not guaranteed to have any elements in it, so what to do if <code>first</code> is invoked on an empty one? Returning an <code>Option</code> in this case is idiomatic in Rust <sup class=\"citation-ref\"><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://eli.thegreenplace.net/2026/rusty-thoughts-on-parse-dont-validate/#footnote-1\">[1]</a></sup>, with convenient syntax sugar for accepting the result of functions that return <code>Option</code> and deciding what to do next.</p>\n<p>So what's the issue?</p>\n<p>Imagine we have a function to read some configuration paths from an env var, while enforcing the invariant that the list can't be empty:</p>\n<pre><code><span class=\"code-snippet\" data-language=\"Rust\"><span class=\"syntax-source syntax-rust\"><span class=\"syntax-keyword syntax-other syntax-rust\">use</span> <span class=\"syntax-meta syntax-path syntax-rust\">anyhow<span class=\"syntax-punctuation syntax-accessor syntax-rust\">::</span></span><span class=\"syntax-meta syntax-block syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust\">{</span><span class=\"syntax-support syntax-type syntax-rust\">Result</span><span class=\"syntax-punctuation syntax-separator syntax-rust\">,</span> ensure</span><span class=\"syntax-meta syntax-block syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-block syntax-end syntax-rust\">}</span></span><span class=\"syntax-punctuation syntax-terminator syntax-rust\">;</span>\n\n<span class=\"syntax-meta syntax-function syntax-rust\"><span class=\"syntax-meta syntax-function syntax-rust\"><span class=\"syntax-storage syntax-type syntax-function syntax-rust\">fn</span> </span><span class=\"syntax-entity syntax-name syntax-function syntax-rust\">get_configuration_directories</span></span><span class=\"syntax-meta syntax-function syntax-rust\"><span class=\"syntax-meta syntax-function syntax-parameters syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-parameters syntax-begin syntax-rust\">(</span></span><span class=\"syntax-meta syntax-function syntax-rust\"><span class=\"syntax-meta syntax-function syntax-parameters syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-parameters syntax-end syntax-rust\">)</span></span></span></span><span class=\"syntax-meta syntax-function syntax-rust\"> <span class=\"syntax-meta syntax-function syntax-return-type syntax-rust\"><span class=\"syntax-punctuation syntax-separator syntax-rust\">-&gt;</span> <span class=\"syntax-meta syntax-generic syntax-rust\">Result<span class=\"syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust\">&lt;</span><span class=\"syntax-meta syntax-generic syntax-rust\">Vec<span class=\"syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust\">&lt;</span>PathBuf<span class=\"syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust\">&gt;</span></span><span class=\"syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust\">&gt;</span></span></span> </span><span class=\"syntax-meta syntax-function syntax-rust\"><span class=\"syntax-meta syntax-block syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust\">{</span>\n    <span class=\"syntax-storage syntax-type syntax-rust\">let</span> value <span class=\"syntax-keyword syntax-operator syntax-rust\">=</span> <span class=\"syntax-meta syntax-path syntax-rust\">env<span class=\"syntax-punctuation syntax-accessor syntax-rust\">::</span></span>var<span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust\">(</span><span class=\"syntax-string syntax-quoted syntax-double syntax-rust\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-rust\">&quot;</span>CONFIG_DIRS<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-rust\">&quot;</span></span></span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-rust\">)</span></span>.<span class=\"syntax-support syntax-function syntax-rust\">context</span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust\">(</span><span class=\"syntax-string syntax-quoted syntax-double syntax-rust\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-rust\">&quot;</span>could not read CONFIG_DIRS<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-rust\">&quot;</span></span></span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-rust\">)</span></span><span class=\"syntax-keyword syntax-operator syntax-rust\">?</span><span class=\"syntax-punctuation syntax-terminator syntax-rust\">;</span>\n\n    <span class=\"syntax-storage syntax-type syntax-rust\">let</span> directories<span class=\"syntax-punctuation syntax-separator syntax-rust\">:</span> <span class=\"syntax-meta syntax-generic syntax-rust\">Vec<span class=\"syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust\">&lt;</span>PathBuf<span class=\"syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust\">&gt;</span></span> <span class=\"syntax-keyword syntax-operator syntax-rust\">=</span> value\n        .<span class=\"syntax-support syntax-function syntax-rust\">split</span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust\">(</span><span class=\"syntax-string syntax-quoted syntax-single syntax-rust\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-rust\">&#39;</span>,<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-rust\">&#39;</span></span></span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-rust\">)</span></span>\n        .<span class=\"syntax-support syntax-function syntax-rust\">map</span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust\">(</span><span class=\"syntax-storage syntax-type syntax-rust\">str</span><span class=\"syntax-meta syntax-path syntax-rust\"><span class=\"syntax-punctuation syntax-accessor syntax-rust\">::</span></span>trim</span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-rust\">)</span></span>\n        .<span class=\"syntax-support syntax-function syntax-rust\">map</span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust\">(</span><span class=\"syntax-meta syntax-path syntax-rust\">PathBuf<span class=\"syntax-punctuation syntax-accessor syntax-rust\">::</span></span>from</span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-rust\">)</span></span>\n        .<span class=\"syntax-support syntax-function syntax-rust\">collect</span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust\">(</span></span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-rust\">)</span></span><span class=\"syntax-punctuation syntax-terminator syntax-rust\">;</span>\n\n    <span class=\"syntax-support syntax-macro syntax-rust\">ensure!</span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust\">(</span><span class=\"syntax-keyword syntax-operator syntax-rust\">!</span>directories.<span class=\"syntax-support syntax-function syntax-rust\">is_empty</span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust\">(</span></span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-rust\">)</span></span><span class=\"syntax-punctuation syntax-separator syntax-rust\">,</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-rust\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-rust\">&quot;</span>empty CONFIG_DIRS<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-rust\">&quot;</span></span></span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-rust\">)</span></span><span class=\"syntax-punctuation syntax-terminator syntax-rust\">;</span>\n    <span class=\"syntax-support syntax-type syntax-rust\">Ok</span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust\">(</span>directories</span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-rust\">)</span></span>\n</span><span class=\"syntax-meta syntax-block syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-block syntax-end syntax-rust\">}</span></span></span>\n</span></span></code></pre>\n<p>So far, so good. Now let's take a typical usage of this function:</p>\n<pre><code><span class=\"code-snippet\" data-language=\"CSS\"><span class=\"syntax-source syntax-css\"><span class=\"syntax-meta syntax-selector syntax-css\">fn <span class=\"syntax-entity syntax-name syntax-tag syntax-css\">main</span>(</span>) -&gt;<span class=\"syntax-meta syntax-selector syntax-css\"> Result&lt;(</span>)&gt; <span class=\"syntax-meta syntax-property-list syntax-css\"><span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">{</span>\n    <span class=\"syntax-meta syntax-property-name syntax-css\">let</span> <span class=\"syntax-meta syntax-property-name syntax-css\">config</span>_<span class=\"syntax-meta syntax-property-name syntax-css\">dirs</span> = <span class=\"syntax-meta syntax-property-name syntax-css\">get</span>_<span class=\"syntax-meta syntax-property-name syntax-css\">configuration</span>_<span class=\"syntax-meta syntax-property-name syntax-css\">directories</span>()?;\n\n    <span class=\"syntax-meta syntax-property-name syntax-css\">match</span> <span class=\"syntax-meta syntax-property-name syntax-css\">config</span>_<span class=\"syntax-meta syntax-property-name syntax-css\">dirs</span>.<span class=\"syntax-meta syntax-property-name syntax-css\">first</span>() {\n        S<span class=\"syntax-meta syntax-property-name syntax-css\">ome</span>(<span class=\"syntax-meta syntax-property-name syntax-css\">cache</span>_<span class=\"syntax-meta syntax-property-name syntax-css\">dir</span>) =&gt; <span class=\"syntax-meta syntax-property-name syntax-css\">initialize</span>_<span class=\"syntax-meta syntax-property-name syntax-css\">cache</span>(<span class=\"syntax-meta syntax-property-name syntax-css\">cache</span>_<span class=\"syntax-meta syntax-property-name syntax-css\">dir</span>),\n        N<span class=\"syntax-meta syntax-property-name syntax-css\">one</span> =&gt; <span class=\"syntax-meta syntax-property-name syntax-css\">unreachable</span>!(&quot;<span class=\"syntax-meta syntax-property-name syntax-css\">already</span> <span class=\"syntax-meta syntax-property-name syntax-css\">checked</span> <span class=\"syntax-meta syntax-property-name syntax-css\">that</span> CONFIG_DIRS <span class=\"syntax-meta syntax-property-name syntax-css\">is</span> <span class=\"syntax-meta syntax-property-name syntax-css\">non-empty</span>&quot;),\n</span>    <span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">}</span>\n\n<span class=\"syntax-meta syntax-selector syntax-css\">    Ok((</span>))\n}\n</span></span></code></pre>\n<p>Once <code>get_configuration_directories</code> returns a successful result, we are guaranteed that the vector isn't empty. And yet, if we want to get the first element of this vector, we have to use the <code>first</code> method that returns <code>Option&lt;&amp;T&gt;</code>. We are therefore forced - again - to handle a potentially empty case (where the option is <code>None</code>).</p>\n<p>As the original article states, this has a number of problems with code clarity, potential performance implications and a ticking time bomb if the invariant is ever changed in <code>get_configuration_directories</code>.</p>\n<p>The core issue is that <code>Vec</code> is fundamentally a type that can be empty; we can carry along a &quot;This one can't be empty, pinky promise!&quot; comment on all the relevant code, but it's not formally checked by anything.</p>\n<h2>A type for &quot;non-empty&quot; vector</h2>\n<p>The solution is leveraging the type system to enforce a newly established invariant. We can use a separate type for &quot;a vector that cannot be empty&quot;; in fact, such types already exist in several Rust crates - for example <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://docs.rs/nonempty/latest/nonempty/\">nonempty</a>:</p>\n<pre><code><span class=\"code-snippet\">pub struct NonEmpty&lt;T&gt; {\n    pub head: T,\n    pub tail: Vec&lt;T&gt;,\n}\n</span></code></pre>\n<p>This type has no constructor that permits &quot;no elements&quot;; its <code>new</code> takes one element, and its <code>first</code> method returns <code>&amp;T</code> without an <code>Option</code>:</p>\n<pre><code><span class=\"code-snippet\">pub const fn new(e: T) -&gt; Self {\n    Self::singleton(e)\n}\n\npub const fn singleton(head: T) -&gt; Self {\n    NonEmpty {\n        head,\n        tail: Vec::new(),\n    }\n}\n\npub const fn first(&amp;self) -&gt; &amp;T {\n    &amp;self.head\n}\n</span></code></pre>\n<p>The rest of the crate deals with making <code>NonEmpty</code> behave as close as possible to a normal <code>Vec</code>, by implementing many useful traits, as well as conversions like:</p>\n<pre><code><span class=\"code-snippet\" data-language=\"CSS\"><span class=\"syntax-source syntax-css\"><span class=\"syntax-meta syntax-selector syntax-css\">pub fn from_vec(mut vec: Vec&lt;T<span class=\"syntax-punctuation syntax-separator syntax-combinator syntax-css\">&gt;</span></span>) -&gt;<span class=\"syntax-meta syntax-selector syntax-css\"> Option&lt;NonEmpty&lt;T<span class=\"syntax-punctuation syntax-separator syntax-combinator syntax-css\">&gt;&gt;</span> </span><span class=\"syntax-meta syntax-property-list syntax-css\"><span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">{</span>\n    <span class=\"syntax-meta syntax-property-name syntax-css\">if</span> <span class=\"syntax-meta syntax-property-name syntax-css\">vec</span>.<span class=\"syntax-meta syntax-property-name syntax-css\">is</span>_<span class=\"syntax-meta syntax-property-name syntax-css\">empty</span>() {\n        N<span class=\"syntax-meta syntax-property-name syntax-css\">one</span>\n</span>    <span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">}</span><span class=\"syntax-meta syntax-selector syntax-css\"> else </span><span class=\"syntax-meta syntax-property-list syntax-css\"><span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">{</span>\n        <span class=\"syntax-meta syntax-property-name syntax-css\">let</span> <span class=\"syntax-meta syntax-property-name syntax-css\">head</span> = <span class=\"syntax-meta syntax-property-name syntax-css\">vec</span>.<span class=\"syntax-meta syntax-property-name syntax-css\">remove</span>(0);\n        S<span class=\"syntax-meta syntax-property-name syntax-css\">ome</span>(N<span class=\"syntax-meta syntax-property-name syntax-css\">on</span>E<span class=\"syntax-meta syntax-property-name syntax-css\">mpty</span> { <span class=\"syntax-meta syntax-property-name syntax-css\">head</span>, <span class=\"syntax-meta syntax-property-name syntax-css\">tail</span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-css\">:</span><span class=\"syntax-meta syntax-property-value syntax-css\"> </span><span class=\"syntax-meta syntax-property-value syntax-css\">vec </span></span><span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">}</span>)\n    }\n}\n</span></span></code></pre>\n<p>Let's see how our <code>get_configuration_directories</code> function would look if it returned a <code>NonEmpty</code> instead of a plain <code>Vec</code>:</p>\n<pre><code><span class=\"code-snippet\" data-language=\"CSS\"><span class=\"syntax-source syntax-css\"><span class=\"syntax-meta syntax-selector syntax-css\">fn get_configuration_directories(</span>) -&gt;<span class=\"syntax-meta syntax-selector syntax-css\"> Result&lt;NonEmpty&lt;PathBuf<span class=\"syntax-punctuation syntax-separator syntax-combinator syntax-css\">&gt;&gt;</span> </span><span class=\"syntax-meta syntax-property-list syntax-css\"><span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">{</span>\n    <span class=\"syntax-meta syntax-property-name syntax-css\">let</span> <span class=\"syntax-meta syntax-property-name syntax-css\">value</span> = <span class=\"syntax-meta syntax-property-name syntax-css\">env</span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-css\">:</span><span class=\"syntax-meta syntax-property-value syntax-css\">:<span class=\"syntax-meta syntax-function-call syntax-css\"><span class=\"syntax-support syntax-function syntax-var syntax-css\">var</span><span class=\"syntax-meta syntax-group syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-group syntax-begin syntax-css\">(</span>&quot;CONFIG_DIRS&quot;</span><span class=\"syntax-meta syntax-group syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-group syntax-end syntax-css\">)</span></span></span>.context(<span class=\"syntax-string syntax-quoted syntax-double syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-css\">&quot;</span>could not read CONFIG_DIRS<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-css\">&quot;</span></span></span>)?;\n\n    <span class=\"syntax-meta syntax-property-name syntax-css\">let</span> <span class=\"syntax-meta syntax-property-name syntax-css\">directories</span> = <span class=\"syntax-meta syntax-property-name syntax-css\">value</span>\n        .<span class=\"syntax-meta syntax-property-name syntax-css\">split</span>(&#39;,&#39;)\n        .<span class=\"syntax-meta syntax-property-name syntax-css\">map</span>(<span class=\"syntax-meta syntax-property-name syntax-css\">str</span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-css\">:</span><span class=\"syntax-meta syntax-property-value syntax-css\">:trim</span>)\n        .<span class=\"syntax-meta syntax-property-name syntax-css\">map</span>(P<span class=\"syntax-meta syntax-property-name syntax-css\">ath</span>B<span class=\"syntax-meta syntax-property-name syntax-css\">uf</span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-css\">:</span><span class=\"syntax-meta syntax-property-value syntax-css\">:<span class=\"syntax-support syntax-constant syntax-property-value syntax-css\">from</span></span>)\n        .<span class=\"syntax-meta syntax-property-name syntax-css\">collect</span>();\n\n    <span class=\"syntax-meta syntax-property-name syntax-css\">let</span> S<span class=\"syntax-meta syntax-property-name syntax-css\">ome</span>(<span class=\"syntax-meta syntax-property-name syntax-css\">directories</span>) = N<span class=\"syntax-meta syntax-property-name syntax-css\">on</span>E<span class=\"syntax-meta syntax-property-name syntax-css\">mpty</span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-css\">:</span><span class=\"syntax-meta syntax-property-value syntax-css\">:from_vec(directories</span>) <span class=\"syntax-meta syntax-property-name syntax-css\">else</span> {\n        <span class=\"syntax-meta syntax-property-name syntax-css\">bail</span>!(&quot;CONFIG_DIRS <span class=\"syntax-meta syntax-property-name syntax-css\">cannot</span> <span class=\"syntax-meta syntax-property-name syntax-css\">be</span> <span class=\"syntax-meta syntax-property-name syntax-css\">empty</span>&quot;);\n</span>    <span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">}</span>;\n\n<span class=\"syntax-meta syntax-selector syntax-css\">    Ok(directories</span>)\n}\n</span></span></code></pre>\n<p>Note the use of <code>NonEmpty::from_vec</code> here - this is where the invariant is established. Now a successful result is <code>NonEmpty</code>, not just <code>Vec</code>. The client code looks like:</p>\n<pre><code><span class=\"code-snippet\" data-language=\"Rust\"><span class=\"syntax-source syntax-rust\"><span class=\"syntax-meta syntax-function syntax-rust\"><span class=\"syntax-meta syntax-function syntax-rust\"><span class=\"syntax-storage syntax-type syntax-function syntax-rust\">fn</span> </span><span class=\"syntax-entity syntax-name syntax-function syntax-rust\">main</span></span><span class=\"syntax-meta syntax-function syntax-rust\"><span class=\"syntax-meta syntax-function syntax-parameters syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-parameters syntax-begin syntax-rust\">(</span></span><span class=\"syntax-meta syntax-function syntax-rust\"><span class=\"syntax-meta syntax-function syntax-parameters syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-parameters syntax-end syntax-rust\">)</span></span></span></span><span class=\"syntax-meta syntax-function syntax-rust\"> <span class=\"syntax-meta syntax-function syntax-return-type syntax-rust\"><span class=\"syntax-punctuation syntax-separator syntax-rust\">-&gt;</span> <span class=\"syntax-meta syntax-generic syntax-rust\">Result<span class=\"syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust\">&lt;</span><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust\">(</span><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-rust\">)</span><span class=\"syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust\">&gt;</span></span></span> </span><span class=\"syntax-meta syntax-function syntax-rust\"><span class=\"syntax-meta syntax-block syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust\">{</span>\n    <span class=\"syntax-storage syntax-type syntax-rust\">let</span> config_dirs <span class=\"syntax-keyword syntax-operator syntax-rust\">=</span> <span class=\"syntax-support syntax-function syntax-rust\">get_configuration_directories</span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust\">(</span></span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-rust\">)</span></span><span class=\"syntax-keyword syntax-operator syntax-rust\">?</span><span class=\"syntax-punctuation syntax-terminator syntax-rust\">;</span>\n\n    <span class=\"syntax-support syntax-function syntax-rust\">initialize_cache</span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust\">(</span>config_dirs.<span class=\"syntax-support syntax-function syntax-rust\">first</span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust\">(</span></span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-rust\">)</span></span></span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-rust\">)</span></span><span class=\"syntax-keyword syntax-operator syntax-rust\">?</span><span class=\"syntax-punctuation syntax-terminator syntax-rust\">;</span>\n    <span class=\"syntax-support syntax-type syntax-rust\">Ok</span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust\">(</span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust\">(</span></span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-rust\">)</span></span></span><span class=\"syntax-meta syntax-group syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-rust\">)</span></span>\n</span><span class=\"syntax-meta syntax-block syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-block syntax-end syntax-rust\">}</span></span></span>\n</span></span></code></pre>\n<p>There's no need to check if the returned value is empty again; this is enforced by the type system!</p>\n<p>This is where the parse vs. validate terminology of the original article comes from. When <code>get_configuration_directories</code> returned a <code>Vec</code>, it simply validated it. But when it returns a <code>NonEmpty</code> - the vector is transformed into another entity which carries additional meaning. If we treat the concept of parsing in the most generic sense - &quot;transforming data from one format to another&quot;, this fits.</p>\n<p>To mention a less artificial example, the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/rustcoreutils/posixutils-rs\">Rust rewrite of core POSIX utilities</a> uses <code>NonEmpty</code> in several places <sup class=\"citation-ref\"><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://eli.thegreenplace.net/2026/rusty-thoughts-on-parse-dont-validate/#footnote-2\">[2]</a></sup>. For example, when constructing a shell pipeline:</p>\n<pre><code><span class=\"code-snippet\">pub struct Pipeline {\n    pub commands: NonEmpty&lt;Command&gt;,\n    pub negate_status: bool,\n}\n</span></code></pre>\n<p>The command parser's code:</p>\n<pre><code><span class=\"code-snippet\" data-language=\"CSS\"><span class=\"syntax-source syntax-css\"><span class=\"syntax-meta syntax-selector syntax-css\">fn parse_pipeline(&amp;mut self, alias_table: &amp;AliasTable</span>) -&gt;<span class=\"syntax-meta syntax-selector syntax-css\"> ParseResult&lt;Option&lt;Pipeline<span class=\"syntax-punctuation syntax-separator syntax-combinator syntax-css\">&gt;&gt;</span> </span><span class=\"syntax-meta syntax-property-list syntax-css\"><span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">{</span>\n    // <span class=\"syntax-meta syntax-property-name syntax-css\">pipeline</span> = &quot;!&quot; <span class=\"syntax-meta syntax-property-name syntax-css\">command</span> (&quot;|&quot; <span class=\"syntax-meta syntax-property-name syntax-css\">linebreak</span> <span class=\"syntax-meta syntax-property-name syntax-css\">command</span>)*\n    <span class=\"syntax-meta syntax-property-name syntax-css\">let</span> <span class=\"syntax-meta syntax-property-name syntax-css\">negate</span>_<span class=\"syntax-meta syntax-property-name syntax-css\">status</span> = <span class=\"syntax-meta syntax-property-name syntax-css\">self</span>.<span class=\"syntax-meta syntax-property-name syntax-css\">match</span>_<span class=\"syntax-meta syntax-property-name syntax-css\">alternatives</span>(&amp;[C<span class=\"syntax-meta syntax-property-name syntax-css\">ommand</span>T<span class=\"syntax-meta syntax-property-name syntax-css\">oken</span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-css\">:</span><span class=\"syntax-meta syntax-property-value syntax-css\">:Bang]</span>)?.<span class=\"syntax-meta syntax-property-name syntax-css\">is</span>_<span class=\"syntax-meta syntax-property-name syntax-css\">some</span>();\n    <span class=\"syntax-meta syntax-property-name syntax-css\">let</span> <span class=\"syntax-meta syntax-property-name syntax-css\">mut</span> <span class=\"syntax-meta syntax-property-name syntax-css\">commands</span> = <span class=\"syntax-meta syntax-property-name syntax-css\">if</span> <span class=\"syntax-meta syntax-property-name syntax-css\">let</span> S<span class=\"syntax-meta syntax-property-name syntax-css\">ome</span>(<span class=\"syntax-meta syntax-property-name syntax-css\">command</span>) = <span class=\"syntax-meta syntax-property-name syntax-css\">self</span>.<span class=\"syntax-meta syntax-property-name syntax-css\">parse</span>_<span class=\"syntax-meta syntax-property-name syntax-css\">command</span>(<span class=\"syntax-meta syntax-property-name syntax-css\">alias</span>_<span class=\"syntax-meta syntax-property-name syntax-css\">table</span>)? {\n        N<span class=\"syntax-meta syntax-property-name syntax-css\">on</span>E<span class=\"syntax-meta syntax-property-name syntax-css\">mpty</span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-css\">:</span><span class=\"syntax-meta syntax-property-value syntax-css\">:new(command</span>)\n</span>    <span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">}</span><span class=\"syntax-meta syntax-selector syntax-css\"> else </span><span class=\"syntax-meta syntax-property-list syntax-css\"><span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">{</span>\n        <span class=\"syntax-meta syntax-property-name syntax-css\">return</span> O<span class=\"syntax-meta syntax-property-name syntax-css\">k</span>(N<span class=\"syntax-meta syntax-property-name syntax-css\">one</span>);\n</span>    <span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">}</span>;\n\n    //<span class=\"syntax-meta syntax-selector syntax-css\"> ...\n</span></span></span></code></pre>\n<p>A valid <code>Pipeline</code> is only returned if there are some commands in the parsed AST. Otherwise, it just returns <code>None</code>. Once this is done, the client code can use <code>commands.first()</code> without having to worry about the possibility of it returning <code>None</code>.</p>\n<h2>Gradual parsing and type refinement</h2>\n<p>A somewhat more interesting example can be found in the source code of <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/rust-lang/rust-analyzer\">rust-analyzer</a>. This project has a type that represents an absolute filesystem path:</p>\n<pre><code><span class=\"code-snippet\">pub struct AbsPathBuf(Utf8PathBuf);\n</span></code></pre>\n<p>Instead of carrying around a regular path, the absoluteness is recorded in the type once the initial parsing and validation is done:</p>\n<pre><code><span class=\"code-snippet\" data-language=\"CSS\"><span class=\"syntax-source syntax-css\"><span class=\"syntax-meta syntax-selector syntax-css\">impl TryFrom&lt;Utf8PathBuf<span class=\"syntax-punctuation syntax-separator syntax-combinator syntax-css\">&gt;</span> for AbsPathBuf </span><span class=\"syntax-meta syntax-property-list syntax-css\"><span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">{</span>\n    <span class=\"syntax-meta syntax-property-name syntax-css\">type</span> E<span class=\"syntax-meta syntax-property-name syntax-css\">rror</span> = U<span class=\"syntax-meta syntax-property-name syntax-css\">tf</span>8P<span class=\"syntax-meta syntax-property-name syntax-css\">ath</span>B<span class=\"syntax-meta syntax-property-name syntax-css\">uf</span>;\n    <span class=\"syntax-meta syntax-property-name syntax-css\">fn</span> <span class=\"syntax-meta syntax-property-name syntax-css\">try</span>_<span class=\"syntax-meta syntax-property-name syntax-css\">from</span>(<span class=\"syntax-meta syntax-property-name syntax-css\">path</span>_<span class=\"syntax-meta syntax-property-name syntax-css\">buf</span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-css\">:</span><span class=\"syntax-meta syntax-property-value syntax-css\"> </span><span class=\"syntax-meta syntax-property-value syntax-css\">Utf<span class=\"syntax-constant syntax-numeric syntax-css\">8</span>PathBuf</span>) <span class=\"syntax-meta syntax-property-name syntax-css\">-</span>&gt; R<span class=\"syntax-meta syntax-property-name syntax-css\">esult</span>&lt;A<span class=\"syntax-meta syntax-property-name syntax-css\">bs</span>P<span class=\"syntax-meta syntax-property-name syntax-css\">ath</span>B<span class=\"syntax-meta syntax-property-name syntax-css\">uf</span>, U<span class=\"syntax-meta syntax-property-name syntax-css\">tf</span>8P<span class=\"syntax-meta syntax-property-name syntax-css\">ath</span>B<span class=\"syntax-meta syntax-property-name syntax-css\">uf</span>&gt; {\n        <span class=\"syntax-meta syntax-property-name syntax-css\">if</span> !<span class=\"syntax-meta syntax-property-name syntax-css\">path</span>_<span class=\"syntax-meta syntax-property-name syntax-css\">buf</span>.<span class=\"syntax-meta syntax-property-name syntax-css\">is</span>_<span class=\"syntax-meta syntax-property-name syntax-css\">absolute</span>() {\n            <span class=\"syntax-meta syntax-property-name syntax-css\">return</span> E<span class=\"syntax-meta syntax-property-name syntax-css\">rr</span>(<span class=\"syntax-meta syntax-property-name syntax-css\">path</span>_<span class=\"syntax-meta syntax-property-name syntax-css\">buf</span>);\n</span>        <span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">}</span>\n<span class=\"syntax-meta syntax-selector syntax-css\">        Ok(AbsPathBuf(path_buf</span>))\n    }\n}\n</span></span></code></pre>\n<p>Subsequent code doesn't have to validate the the path is absolute. The type enforces it.</p>\n<p>Note also that <code>AbsPathBuf</code> wraps <code>Utf8PathBuf</code>, not <code>PathBuf</code>. <code>Utf8PathBuf</code> is itself a custom, &quot;parsed&quot; type refinement from the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://docs.rs/camino/latest/camino/\">camino crate</a>. Regular paths in the Rust standard library aren't guaranteed to be valid UTF-8, so they cannot be easily converted to a <code>String</code> (which has to be valid UTF-8 in Rust); <code>camino::Utf8PathBuf</code> establishes validity on construction, and can then be converted to a string with just:</p>\n<pre><code><span class=\"code-snippet\" data-language=\"Rust\"><span class=\"syntax-source syntax-rust\"><span class=\"syntax-meta syntax-function syntax-rust\"><span class=\"syntax-meta syntax-function syntax-rust\"><span class=\"syntax-storage syntax-type syntax-function syntax-rust\">fn</span> </span><span class=\"syntax-entity syntax-name syntax-function syntax-rust\">as_str</span></span><span class=\"syntax-meta syntax-function syntax-rust\"><span class=\"syntax-meta syntax-function syntax-parameters syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-parameters syntax-begin syntax-rust\">(</span><span class=\"syntax-keyword syntax-operator syntax-rust\">&amp;</span><span class=\"syntax-variable syntax-parameter syntax-rust\">self</span></span><span class=\"syntax-meta syntax-function syntax-rust\"><span class=\"syntax-meta syntax-function syntax-parameters syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-parameters syntax-end syntax-rust\">)</span></span></span></span><span class=\"syntax-meta syntax-function syntax-rust\"> <span class=\"syntax-meta syntax-function syntax-return-type syntax-rust\"><span class=\"syntax-punctuation syntax-separator syntax-rust\">-&gt;</span> <span class=\"syntax-keyword syntax-operator syntax-rust\">&amp;</span><span class=\"syntax-storage syntax-type syntax-rust\">str</span></span> </span><span class=\"syntax-meta syntax-function syntax-rust\"><span class=\"syntax-meta syntax-block syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust\">{</span>\n  <span class=\"syntax-keyword syntax-operator syntax-rust\">...</span>\n</span><span class=\"syntax-meta syntax-block syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-block syntax-end syntax-rust\">}</span></span></span>\n</span></span></code></pre>\n<p>So we have an example of gradual parsing and type refinement here:</p>\n<pre><code><span class=\"code-snippet\">std::path::PathBuf\n\n      |\n      |   prove UTF-8\n      |\n      V\n\ncamino::Utf8PathBuf\n\n      |\n      |   prove absolute\n      |\n      V\n\nrust-analyzer&#39;s paths::AbsPathBuf\n</span></code></pre>\n<h2>Non-zero integers</h2>\n<p>Rust has a generic type called <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://doc.rust-lang.org/std/num/struct.NonZero.html\">NonZero</a>, to describe unsigned numeric quantities that are known to be non-zero.</p>\n<p>For example, <code>thread::available_parallelism</code> is defined as:</p>\n<pre><code><span class=\"code-snippet\" data-language=\"Rust\"><span class=\"syntax-source syntax-rust\"><span class=\"syntax-meta syntax-function syntax-rust\"><span class=\"syntax-meta syntax-function syntax-rust\"><span class=\"syntax-storage syntax-modifier syntax-rust\">pub</span> <span class=\"syntax-storage syntax-type syntax-function syntax-rust\">fn</span> </span><span class=\"syntax-entity syntax-name syntax-function syntax-rust\">available_parallelism</span></span><span class=\"syntax-meta syntax-function syntax-rust\"><span class=\"syntax-meta syntax-function syntax-parameters syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-parameters syntax-begin syntax-rust\">(</span></span><span class=\"syntax-meta syntax-function syntax-rust\"><span class=\"syntax-meta syntax-function syntax-parameters syntax-rust\"><span class=\"syntax-punctuation syntax-section syntax-parameters syntax-end syntax-rust\">)</span></span></span></span><span class=\"syntax-meta syntax-function syntax-rust\"> <span class=\"syntax-meta syntax-function syntax-return-type syntax-rust\"><span class=\"syntax-punctuation syntax-separator syntax-rust\">-&gt;</span> <span class=\"syntax-meta syntax-generic syntax-rust\">Result<span class=\"syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust\">&lt;</span><span class=\"syntax-meta syntax-generic syntax-rust\">NonZero<span class=\"syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust\">&lt;</span><span class=\"syntax-storage syntax-type syntax-rust\">usize</span><span class=\"syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust\">&gt;</span></span><span class=\"syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust\">&gt;</span></span>\n</span></span></span></span></code></pre>\n<p>If the call is successful, it returns a <code>NonZero&lt;usize&gt;</code>, which is like a normal <code>usize</code> with the restriction that it's not zero. Client code doesn't have to keep checking whether the parallelism is 0 - it's enshrined in the type system.</p>\n<p>Rust defines the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://doc.rust-lang.org/std/primitive.u32.html#impl-Div%3CNonZero%3Cu32%3E%3E-for-u32\">division operator</a> with <code>NonZero&lt;usize&gt;</code> in the denominator as an operation that &quot;cannot panic&quot;.</p>\n<p><code>NonZero</code> has an additional advantage: zero is an invalid value for the type, so Rust can use the zero bit pattern to represent <code>None</code>. Consequently, <code>Option&lt;NonZeroUsize&gt;</code> is <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://doc.rust-lang.org/std/option/index.html#representation\">guaranteed to have the same size</a> and alignment as <code>NonZeroUsize</code> itself (and as <code>usize</code>). This can avoid the extra storage that an <code>Option&lt;usize&gt;</code> would generally require.</p>\n<h2>Parsing JSON</h2>\n<p>A common example of the &quot;parse, don't validate&quot; idiom appears in deserializing data from a JSON string. Rust's <code>serde</code> crate enables us to do the parsing, with validated decisions encoded into the type system, e.g.:</p>\n<pre><code><span class=\"code-snippet\">#[derive(Debug, Deserialize)]\nstruct Config {\n    name: String,\n    workers: NonZeroUsize,\n    mode: Mode,\n}\n\n#[derive(Debug, Deserialize)]\n#[serde(rename_all = &quot;snake_case&quot;)]\nenum Mode {\n    Fast,\n    Safe,\n}\n</span></code></pre>\n<p>And then later:</p>\n<pre><code><span class=\"code-snippet\">let input = r#&quot;\n     {\n         &quot;name&quot;: &quot;compiler&quot;,\n         &quot;workers&quot;: 4,\n         &quot;mode&quot;: &quot;fast&quot;\n     }\n &quot;#;\n\n let config: Config = serde_json::from_str(input)?;\n</span></code></pre>\n<p>There is a lot happening behind the scenes:</p>\n<ul>\n<li>The types of all fields are enforced (e.g. &quot;name&quot; cannot be an array).</li>\n<li>The <code>mode</code> is validated to be one of the enum values of <code>Mode</code>.</li>\n<li><code>workers</code> is validated to be a non-zero integer, because of the <code>NonZeroUsize</code> field type.</li>\n</ul>\n<p>We take code like this for granted these days, but it's still a great example of the pattern discussed in this post. Once the parser converted <code>mode</code> into the <code>Mode</code> enum, no further validation is required.</p>\n<p>In dynamic languages like Python and JS, the process is usually much more manual. Python's <code>json.loads</code> gives us a dictionary, and it's up to the user to validate its contents. Libraries like Pydantic permit an approach closer to Rust's, but they're not universally used.</p>\n<hr />\n<p><sup class=\"citation-ref\"><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://eli.thegreenplace.net/2026/rusty-thoughts-on-parse-dont-validate/#footnote-reference-1\">[1]</a></sup></p>\n<p>Other languages - like Go or Python - have a runtime check that raises some sort of exception or panic when <code>lst[0]</code> is accessed on an empty list or slice.</p>\n<p><sup class=\"citation-ref\"><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://eli.thegreenplace.net/2026/rusty-thoughts-on-parse-dont-validate/#footnote-reference-2\">[2]</a></sup></p>\n<p>The project implements its own <code>NonEmpty</code>, without relying on the <code>nonempty</code> crate, but all the points in this post apply.</p>\n<hr />\n<p>For comments, please send me <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"mailto:eliben@gmail.com\">an email</a>.</p>\n",
  "content_markdown": "Like many programmers, I find Alexis King's [Parse, don't validate](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/) article fascinating, because it gives a name to an idiom that seems familiar and important - one I've observed and used in the past without naming it explicitly. This post is a review of the \"Parse, don't validate\" pattern applied to the Rust programming language (the original post uses Haskell). I was particularly interested in finding educational examples of this pattern in the Rust standard library and other well-known projects.\n\nWithout repeating the original article (please read it first!), here's the gist of it.\n\nConsider the venerable `Vec`; its `first` method returns `Option<&T>`. Why? Because a vector is not guaranteed to have any elements in it, so what to do if `first` is invoked on an empty one? Returning an `Option` in this case is idiomatic in Rust [\\[1\\]](https://eli.thegreenplace.net/2026/rusty-thoughts-on-parse-dont-validate/#footnote-1), with convenient syntax sugar for accepting the result of functions that return `Option` and deciding what to do next.\n\nSo what's the issue?\n\nImagine we have a function to read some configuration paths from an env var, while enforcing the invariant that the list can't be empty:\n\n```\nuse anyhow::{Result, ensure};\n\nfn get_configuration_directories() -> Result<Vec<PathBuf>> {\n    let value = env::var(\"CONFIG_DIRS\").context(\"could not read CONFIG_DIRS\")?;\n\n    let directories: Vec<PathBuf> = value\n        .split(',')\n        .map(str::trim)\n        .map(PathBuf::from)\n        .collect();\n\n    ensure!(!directories.is_empty(), \"empty CONFIG_DIRS\");\n    Ok(directories)\n}\n```\n\nSo far, so good. Now let's take a typical usage of this function:\n\n```\nfn main() -> Result<()> {\n    let config_dirs = get_configuration_directories()?;\n\n    match config_dirs.first() {\n        Some(cache_dir) => initialize_cache(cache_dir),\n        None => unreachable!(\"already checked that CONFIG_DIRS is non-empty\"),\n    }\n\n    Ok(())\n}\n```\n\nOnce `get_configuration_directories` returns a successful result, we are guaranteed that the vector isn't empty. And yet, if we want to get the first element of this vector, we have to use the `first` method that returns `Option<&T>`. We are therefore forced - again - to handle a potentially empty case (where the option is `None`).\n\nAs the original article states, this has a number of problems with code clarity, potential performance implications and a ticking time bomb if the invariant is ever changed in `get_configuration_directories`.\n\nThe core issue is that `Vec` is fundamentally a type that can be empty; we can carry along a \"This one can't be empty, pinky promise!\" comment on all the relevant code, but it's not formally checked by anything.\n\n## A type for \"non-empty\" vector\n\nThe solution is leveraging the type system to enforce a newly established invariant. We can use a separate type for \"a vector that cannot be empty\"; in fact, such types already exist in several Rust crates - for example [nonempty](https://docs.rs/nonempty/latest/nonempty/):\n\n```\npub struct NonEmpty<T> {\n    pub head: T,\n    pub tail: Vec<T>,\n}\n```\n\nThis type has no constructor that permits \"no elements\"; its `new` takes one element, and its `first` method returns `&T` without an `Option`:\n\n```\npub const fn new(e: T) -> Self {\n    Self::singleton(e)\n}\n\npub const fn singleton(head: T) -> Self {\n    NonEmpty {\n        head,\n        tail: Vec::new(),\n    }\n}\n\npub const fn first(&self) -> &T {\n    &self.head\n}\n```\n\nThe rest of the crate deals with making `NonEmpty` behave as close as possible to a normal `Vec`, by implementing many useful traits, as well as conversions like:\n\n```\npub fn from_vec(mut vec: Vec<T>) -> Option<NonEmpty<T>> {\n    if vec.is_empty() {\n        None\n    } else {\n        let head = vec.remove(0);\n        Some(NonEmpty { head, tail: vec })\n    }\n}\n```\n\nLet's see how our `get_configuration_directories` function would look if it returned a `NonEmpty` instead of a plain `Vec`:\n\n```\nfn get_configuration_directories() -> Result<NonEmpty<PathBuf>> {\n    let value = env::var(\"CONFIG_DIRS\").context(\"could not read CONFIG_DIRS\")?;\n\n    let directories = value\n        .split(',')\n        .map(str::trim)\n        .map(PathBuf::from)\n        .collect();\n\n    let Some(directories) = NonEmpty::from_vec(directories) else {\n        bail!(\"CONFIG_DIRS cannot be empty\");\n    };\n\n    Ok(directories)\n}\n```\n\nNote the use of `NonEmpty::from_vec` here - this is where the invariant is established. Now a successful result is `NonEmpty`, not just `Vec`. The client code looks like:\n\n```\nfn main() -> Result<()> {\n    let config_dirs = get_configuration_directories()?;\n\n    initialize_cache(config_dirs.first())?;\n    Ok(())\n}\n```\n\nThere's no need to check if the returned value is empty again; this is enforced by the type system!\n\nThis is where the parse vs. validate terminology of the original article comes from. When `get_configuration_directories` returned a `Vec`, it simply validated it. But when it returns a `NonEmpty` - the vector is transformed into another entity which carries additional meaning. If we treat the concept of parsing in the most generic sense - \"transforming data from one format to another\", this fits.\n\nTo mention a less artificial example, the [Rust rewrite of core POSIX utilities](https://github.com/rustcoreutils/posixutils-rs) uses `NonEmpty` in several places [\\[2\\]](https://eli.thegreenplace.net/2026/rusty-thoughts-on-parse-dont-validate/#footnote-2). For example, when constructing a shell pipeline:\n\n```\npub struct Pipeline {\n    pub commands: NonEmpty<Command>,\n    pub negate_status: bool,\n}\n```\n\nThe command parser's code:\n\n```\nfn parse_pipeline(&mut self, alias_table: &AliasTable) -> ParseResult<Option<Pipeline>> {\n    // pipeline = \"!\" command (\"|\" linebreak command)*\n    let negate_status = self.match_alternatives(&[CommandToken::Bang])?.is_some();\n    let mut commands = if let Some(command) = self.parse_command(alias_table)? {\n        NonEmpty::new(command)\n    } else {\n        return Ok(None);\n    };\n\n    // ...\n```\n\nA valid `Pipeline` is only returned if there are some commands in the parsed AST. Otherwise, it just returns `None`. Once this is done, the client code can use `commands.first()` without having to worry about the possibility of it returning `None`.\n\n## Gradual parsing and type refinement\n\nA somewhat more interesting example can be found in the source code of [rust-analyzer](https://github.com/rust-lang/rust-analyzer). This project has a type that represents an absolute filesystem path:\n\n```\npub struct AbsPathBuf(Utf8PathBuf);\n```\n\nInstead of carrying around a regular path, the absoluteness is recorded in the type once the initial parsing and validation is done:\n\n```\nimpl TryFrom<Utf8PathBuf> for AbsPathBuf {\n    type Error = Utf8PathBuf;\n    fn try_from(path_buf: Utf8PathBuf) -> Result<AbsPathBuf, Utf8PathBuf> {\n        if !path_buf.is_absolute() {\n            return Err(path_buf);\n        }\n        Ok(AbsPathBuf(path_buf))\n    }\n}\n```\n\nSubsequent code doesn't have to validate the the path is absolute. The type enforces it.\n\nNote also that `AbsPathBuf` wraps `Utf8PathBuf`, not `PathBuf`. `Utf8PathBuf` is itself a custom, \"parsed\" type refinement from the [camino crate](https://docs.rs/camino/latest/camino/). Regular paths in the Rust standard library aren't guaranteed to be valid UTF-8, so they cannot be easily converted to a `String` (which has to be valid UTF-8 in Rust); `camino::Utf8PathBuf` establishes validity on construction, and can then be converted to a string with just:\n\n```\nfn as_str(&self) -> &str {\n  ...\n}\n```\n\nSo we have an example of gradual parsing and type refinement here:\n\n```\nstd::path::PathBuf\n\n      |\n      |   prove UTF-8\n      |\n      V\n\ncamino::Utf8PathBuf\n\n      |\n      |   prove absolute\n      |\n      V\n\nrust-analyzer's paths::AbsPathBuf\n```\n\n## Non-zero integers\n\nRust has a generic type called [NonZero](https://doc.rust-lang.org/std/num/struct.NonZero.html), to describe unsigned numeric quantities that are known to be non-zero.\n\nFor example, `thread::available_parallelism` is defined as:\n\n```\npub fn available_parallelism() -> Result<NonZero<usize>>\n```\n\nIf the call is successful, it returns a `NonZero<usize>`, which is like a normal `usize` with the restriction that it's not zero. Client code doesn't have to keep checking whether the parallelism is 0 - it's enshrined in the type system.\n\nRust defines the [division operator](https://doc.rust-lang.org/std/primitive.u32.html#impl-Div%3CNonZero%3Cu32%3E%3E-for-u32) with `NonZero<usize>` in the denominator as an operation that \"cannot panic\".\n\n`NonZero` has an additional advantage: zero is an invalid value for the type, so Rust can use the zero bit pattern to represent `None`. Consequently, `Option<NonZeroUsize>` is [guaranteed to have the same size](https://doc.rust-lang.org/std/option/index.html#representation) and alignment as `NonZeroUsize` itself (and as `usize`). This can avoid the extra storage that an `Option<usize>` would generally require.\n\n## Parsing JSON\n\nA common example of the \"parse, don't validate\" idiom appears in deserializing data from a JSON string. Rust's `serde` crate enables us to do the parsing, with validated decisions encoded into the type system, e.g.:\n\n```\n#[derive(Debug, Deserialize)]\nstruct Config {\n    name: String,\n    workers: NonZeroUsize,\n    mode: Mode,\n}\n\n#[derive(Debug, Deserialize)]\n#[serde(rename_all = \"snake_case\")]\nenum Mode {\n    Fast,\n    Safe,\n}\n```\n\nAnd then later:\n\n```\nlet input = r#\"\n     {\n         \"name\": \"compiler\",\n         \"workers\": 4,\n         \"mode\": \"fast\"\n     }\n \"#;\n\n let config: Config = serde_json::from_str(input)?;\n```\n\nThere is a lot happening behind the scenes:\n\n- The types of all fields are enforced (e.g. \"name\" cannot be an array).\n- The `mode` is validated to be one of the enum values of `Mode`.\n- `workers` is validated to be a non-zero integer, because of the `NonZeroUsize` field type.\n\nWe take code like this for granted these days, but it's still a great example of the pattern discussed in this post. Once the parser converted `mode` into the `Mode` enum, no further validation is required.\n\nIn dynamic languages like Python and JS, the process is usually much more manual. Python's `json.loads` gives us a dictionary, and it's up to the user to validate its contents. Libraries like Pydantic permit an approach closer to Rust's, but they're not universally used.\n\n* * *\n\n[\\[1\\]](https://eli.thegreenplace.net/2026/rusty-thoughts-on-parse-dont-validate/#footnote-reference-1)\n\nOther languages - like Go or Python - have a runtime check that raises some sort of exception or panic when `lst[0]` is accessed on an empty list or slice.\n\n[\\[2\\]](https://eli.thegreenplace.net/2026/rusty-thoughts-on-parse-dont-validate/#footnote-reference-2)\n\nThe project implements its own `NonEmpty`, without relying on the `nonempty` crate, but all the points in this post apply.\n\n* * *\n\nFor comments, please send me [an email](mailto:eliben@gmail.com).\n",
  "dateCreated": "2026-09-28T15:21:52.698000543+00:00",
  "date_created": "2026-09-26T20:45:24+00:00",
  "date_modified": "2026-09-26T20:45:24+00:00",
  "date_published": "2026-09-26T20:45:24+00:00",
  "description": "Comments",
  "external_url": "https://eli.thegreenplace.net/2026/rusty-thoughts-on-parse-dont-validate/",
  "git": {
    "commit": "ebb0941c5c06a2ab2377342d607535262a3e969e",
    "path": "items/lobste-rs/2026/09/2026-09-26-rusty-thoughts-on-parse-don-t-validate.md",
    "permalink": "https://github.com/aymericbeaumet/aggr-instance/blob/ebb0941c5c06a2ab2377342d607535262a3e969e/items/lobste-rs/2026/09/2026-09-26-rusty-thoughts-on-parse-don-t-validate.md"
  },
  "id": "urn:aggr:item:613fcee8cf038ec3c68e5096f3394d23357290b1",
  "isBasedOn": {
    "@id": "https://eli.thegreenplace.net/2026/rusty-thoughts-on-parse-dont-validate/"
  },
  "mainEntity": {
    "@id": "https://eli.thegreenplace.net/2026/rusty-thoughts-on-parse-dont-validate/",
    "@type": "CreativeWork",
    "archivedAt": {
      "@id": "https://aggr.aymericbeaumet.com/items/lobste-rs/2026-09-26-rusty-thoughts-on-parse-don-t-validate/#webpage"
    },
    "author": [
      {
        "@type": "Person",
        "name": "eli.thegreenplace.net via carlana"
      }
    ],
    "dateModified": "2026-09-26T20:45:24+00:00",
    "datePublished": "2026-09-26T20:45:24+00:00",
    "headline": "Rusty thoughts on \"Parse, don't validate\"",
    "timeRequired": "PT7M",
    "url": "https://eli.thegreenplace.net/2026/rusty-thoughts-on-parse-dont-validate/",
    "wordCount": 1439
  },
  "name": "Archived snapshot: Rusty thoughts on \"Parse, don't validate\"",
  "preview": null,
  "reading_minutes": 7,
  "replicated_at": null,
  "source": "lobste-rs",
  "source_name": "Lobsters: Top Stories of the Past Week",
  "summary": "Comments",
  "tags": [
    "programming",
    "rust"
  ],
  "temporalCoverage": "2026-09-28T15:21:52.698000543+00:00",
  "title": "Rusty thoughts on \"Parse, don't validate\"",
  "url": "https://aggr.aymericbeaumet.com/items/lobste-rs/2026-09-26-rusty-thoughts-on-parse-don-t-validate/",
  "word_count": 1439
}