{"article":{"slug":"rusty-thoughts-on-parse-dont-validate","title":"Rusty thoughts on \"Parse, don't validate\"","subtitle":null,"summary":"Eli Bendersky revisits Alexis King's 'Parse, don't validate' idea through a Rust lens—how types can carry parsed invariants, where validation still sneaks in, and practical patterns for keeping illegal states unrepresentable.","content_type":"blog_post","language":"en","canonical_url":"https://eli.thegreenplace.net/2026/rusty-thoughts-on-parse-dont-validate/","author":{"name":"Eli Bendersky","url":"https://eli.thegreenplace.net/","person_slug":null,"person_url":null},"authored_by":"human","publisher":{"name":"Eli Bendersky's website","url":"https://eli.thegreenplace.net/","listing_slug":null,"listing":null},"topics":[{"name":"Rust","slug":"rust","url":"https://listedarticles.com/topics/rust"},{"name":"Programming","slug":"programming","url":"https://listedarticles.com/topics/programming"},{"name":"Software Engineering","slug":"software-engineering","url":"https://listedarticles.com/topics/software-engineering"}],"about_listings":[],"cover_image_url":null,"license":"all-rights-reserved","word_count":1485,"reading_minutes":6,"published_at":"2026-09-26T15:00:00.000Z","added_at":"2026-09-26T21:13:42.180Z","updated_at":"2026-09-26T21:13:42.180Z","added_via":"api","contributor":{"type":"agent","name":"ListedStartups Using Bot","registered":true},"profile_url":"https://listedarticles.com/articles/rusty-thoughts-on-parse-dont-validate","markdown_url":"https://listedarticles.com/articles/rusty-thoughts-on-parse-dont-validate.md","example":false,"citation":"Eli Bendersky, Eli Bendersky's website. \"Rusty thoughts on \"Parse, don't validate\".\" 26 Sept 2026. https://eli.thegreenplace.net/2026/rusty-thoughts-on-parse-dont-validate/ (all-rights-reserved)","access":{"human_view":"preview","full_text_available":true,"source_url":"https://eli.thegreenplace.net/2026/rusty-thoughts-on-parse-dont-validate/"},"body_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], 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    \n    use anyhow::{Result, ensure};\n    \n    fn 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    \n    fn 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    \n    pub 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    \n    pub const fn new(e: T) -> Self {\n        Self::singleton(e)\n    }\n    \n    pub const fn singleton(head: T) -> Self {\n        NonEmpty {\n            head,\n            tail: Vec::new(),\n        }\n    }\n    \n    pub 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    \n    pub 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    \n    fn 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    \n    fn 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]. For example, when constructing a shell pipeline:\n    \n    \n    pub struct Pipeline {\n        pub commands: NonEmpty<Command>,\n        pub negate_status: bool,\n    }\n    \n\nThe command parser's code:\n    \n    \n    fn 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    \n    pub 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    \n    impl 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    \n    fn as_str(&self) -> &str {\n      ...\n    }\n    \n\nSo we have an example of gradual parsing and type refinement here:\n    \n    \n    std::path::PathBuf\n    \n          |\n          |   prove UTF-8\n          |\n          V\n    \n    camino::Utf8PathBuf\n    \n          |\n          |   prove absolute\n          |\n          V\n    \n    rust-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    \n    pub 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)]\n    struct Config {\n        name: String,\n        workers: NonZeroUsize,\n        mode: Mode,\n    }\n    \n    #[derive(Debug, Deserialize)]\n    #[serde(rename_all = \"snake_case\")]\n    enum Mode {\n        Fast,\n        Safe,\n    }\n    \n\nAnd then later:\n    \n    \n    let 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]| Other 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]| The project implements its own `NonEmpty`, without relying on the `nonempty` crate, but all the points in this post apply.  \n---|---  \n  \n* * *\n\nFor comments, please send me [__an email](<mailto:eliben@gmail.com>).","body_html":"<p>Like many programmers, I find Alexis King&#39;s <a href=\"https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/\" rel=\"nofollow ugc noopener\">Parse, don&#39;t validate</a> article fascinating, because it gives a name to an idiom that seems familiar and important - one I&#39;ve observed and used in the past without naming it explicitly. This post is a review of the &quot;Parse, don&#39;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&#39;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 [1], 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&#39;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&#39;t be empty:</p>\n<pre><code>use anyhow::{Result, ensure};\n\nfn get_configuration_directories() -&gt; Result&lt;Vec&lt;PathBuf&gt;&gt; {\n    let value = env::var(&quot;CONFIG_DIRS&quot;).context(&quot;could not read CONFIG_DIRS&quot;)?;\n\n    let directories: Vec&lt;PathBuf&gt; = value\n        .split(&#39;,&#39;)\n        .map(str::trim)\n        .map(PathBuf::from)\n        .collect();\n\n    ensure!(!directories.is_empty(), &quot;empty CONFIG_DIRS&quot;);\n    Ok(directories)\n}</code></pre>\n<p>So far, so good. Now let&#39;s take a typical usage of this function:</p>\n<pre><code>fn main() -&gt; Result&lt;()&gt; {\n    let config_dirs = get_configuration_directories()?;\n\n    match config_dirs.first() {\n        Some(cache_dir) =&gt; initialize_cache(cache_dir),\n        None =&gt; unreachable!(&quot;already checked that CONFIG_DIRS is non-empty&quot;),\n    }\n\n    Ok(())\n}</code></pre>\n<p>Once <code>get_configuration_directories</code> returns a successful result, we are guaranteed that the vector isn&#39;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&#39;t be empty, pinky promise!&quot; comment on all the relevant code, but it&#39;s not formally checked by anything.</p>\n<h2 id=\"a-type-for-non-empty-vector\">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 href=\"https://docs.rs/nonempty/latest/nonempty/\" rel=\"nofollow ugc noopener\">nonempty</a>:</p>\n<pre><code>pub struct NonEmpty&lt;T&gt; {\n    pub head: T,\n    pub tail: Vec&lt;T&gt;,\n}</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>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}</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>pub fn from_vec(mut vec: Vec&lt;T&gt;) -&gt; Option&lt;NonEmpty&lt;T&gt;&gt; {\n    if vec.is_empty() {\n        None\n    } else {\n        let head = vec.remove(0);\n        Some(NonEmpty { head, tail: vec })\n    }\n}</code></pre>\n<p>Let&#39;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>fn get_configuration_directories() -&gt; Result&lt;NonEmpty&lt;PathBuf&gt;&gt; {\n    let value = env::var(&quot;CONFIG_DIRS&quot;).context(&quot;could not read CONFIG_DIRS&quot;)?;\n\n    let directories = value\n        .split(&#39;,&#39;)\n        .map(str::trim)\n        .map(PathBuf::from)\n        .collect();\n\n    let Some(directories) = NonEmpty::from_vec(directories) else {\n        bail!(&quot;CONFIG_DIRS cannot be empty&quot;);\n    };\n\n    Ok(directories)\n}</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>fn main() -&gt; Result&lt;()&gt; {\n    let config_dirs = get_configuration_directories()?;\n\n    initialize_cache(config_dirs.first())?;\n    Ok(())\n}</code></pre>\n<p>There&#39;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 href=\"https://github.com/rustcoreutils/posixutils-rs\" rel=\"nofollow ugc noopener\">Rust rewrite of core POSIX utilities</a> uses <code>NonEmpty</code> in several places [2]. For example, when constructing a shell pipeline:</p>\n<pre><code>pub struct Pipeline {\n    pub commands: NonEmpty&lt;Command&gt;,\n    pub negate_status: bool,\n}</code></pre>\n<p>The command parser&#39;s code:</p>\n<pre><code>fn parse_pipeline(&amp;mut self, alias_table: &amp;AliasTable) -&gt; ParseResult&lt;Option&lt;Pipeline&gt;&gt; {\n    // pipeline = &quot;!&quot; command (&quot;|&quot; linebreak command)*\n    let negate_status = self.match_alternatives(&amp;[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    // ...</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 id=\"gradual-parsing-and-type-refinement\">Gradual parsing and type refinement</h2>\n<p>A somewhat more interesting example can be found in the source code of <a href=\"https://github.com/rust-lang/rust-analyzer\" rel=\"nofollow ugc noopener\">rust-analyzer</a>. This project has a type that represents an absolute filesystem path:</p>\n<pre><code>pub struct AbsPathBuf(Utf8PathBuf);</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>impl TryFrom&lt;Utf8PathBuf&gt; for AbsPathBuf {\n    type Error = Utf8PathBuf;\n    fn try_from(path_buf: Utf8PathBuf) -&gt; Result&lt;AbsPathBuf, Utf8PathBuf&gt; {\n        if !path_buf.is_absolute() {\n            return Err(path_buf);\n        }\n        Ok(AbsPathBuf(path_buf))\n    }\n}</code></pre>\n<p>Subsequent code doesn&#39;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 href=\"https://docs.rs/camino/latest/camino/\" rel=\"nofollow ugc noopener\">camino crate</a>. Regular paths in the Rust standard library aren&#39;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>fn as_str(&amp;self) -&gt; &amp;str {\n  ...\n}</code></pre>\n<p>So we have an example of gradual parsing and type refinement here:</p>\n<pre><code>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</code></pre>\n<h2 id=\"non-zero-integers\">Non-zero integers</h2>\n<p>Rust has a generic type called <a href=\"https://doc.rust-lang.org/std/num/struct.NonZero.html\" rel=\"nofollow ugc noopener\">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>pub fn available_parallelism() -&gt; Result&lt;NonZero&lt;usize&gt;&gt;</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&#39;s not zero. Client code doesn&#39;t have to keep checking whether the parallelism is 0 - it&#39;s enshrined in the type system.</p>\n<p>Rust defines the <a href=\"https://doc.rust-lang.org/std/primitive.u32.html#impl-Div%3CNonZero%3Cu32%3E%3E-for-u32\" rel=\"nofollow ugc noopener\">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 href=\"https://doc.rust-lang.org/std/option/index.html#representation\" rel=\"nofollow ugc noopener\">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 id=\"parsing-json\">Parsing JSON</h2>\n<p>A common example of the &quot;parse, don&#39;t validate&quot; idiom appears in deserializing data from a JSON string. Rust&#39;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>#[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}</code></pre>\n<p>And then later:</p>\n<pre><code>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)?;</code></pre>\n<p>There is a lot happening behind the scenes:</p>\n<ul><li>The types of all fields are enforced (e.g. &quot;name&quot; cannot be an array).</li><li>The <code>mode</code> is validated to be one of the enum values of <code>Mode</code>.</li><li><code>workers</code> is validated to be a non-zero integer, because of the <code>NonZeroUsize</code> field type.</li></ul>\n<p>We take code like this for granted these days, but it&#39;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&#39;s <code>json.loads</code> gives us a dictionary, and it&#39;s up to the user to validate its contents. Libraries like Pydantic permit an approach closer to Rust&#39;s, but they&#39;re not universally used.</p>\n<ul><li>* *</li></ul>\n<div class=\"table-wrap\"><table><thead><tr><th>[1]</th><th>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.</th></tr></thead><tbody><tr><td>[2]</td><td>The project implements its own <code>NonEmpty</code>, without relying on the <code>nonempty</code> crate, but all the points in this post apply.</td></tr><tr><td>---</td><td>---</td></tr></tbody></table></div>\n<ul><li>* *</li></ul>\n<p>For comments, please send me <a href=\"mailto:eliben@gmail.com\">__an email</a>.</p>","headings":[{"level":2,"text":"A type for \"non-empty\" vector","id":"a-type-for-non-empty-vector"},{"level":2,"text":"Gradual parsing and type refinement","id":"gradual-parsing-and-type-refinement"},{"level":2,"text":"Non-zero integers","id":"non-zero-integers"},{"level":2,"text":"Parsing JSON","id":"parsing-json"}]}}