<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <link href="https://matklad.github.io/feed.xml" rel="self"
    type="application/atom+xml" />
  <link href="https://matklad.github.io" rel="alternate" type="text/html" />
  <updated>2026-10-18T00:52:27.634Z</updated>
  <id>https://matklad.github.io/feed.xml</id>
  <title type="html">matklad</title>
  <subtitle>matklad&#039;s Arts&amp;Crafts</subtitle>
  <author>
    <name>Alex Kladov</name>
  </author>
  <entry>
    <title type="text">On Git Refs</title>
    <link href="https://matklad.github.io/2026/10/07/git-ref.html"
      rel="alternate" type="text/html" title="On Git Refs" />
    <published>2026-10-07T00:00:00+00:00</published>
    <updated>2026-10-07T00:00:00+00:00</updated>
    <id>https://matklad.github.io/2026/10/07/git-ref</id>
    <author>
      <name>Alex Kladov</name>
    </author>
    <summary type="html">
      <![CDATA[I have recently improved my mental model of Git. Consider these two git commands:]]>
    </summary>
    <content type="html"
      xml:base="https://matklad.github.io/2026/10/07/git-ref.html">
      <![CDATA[
<header>
  <h1>On Git Refs</h1>
  <time class="meta" datetime="2026-10-07">Oct 7, 2026</time>
</header>
<p>I have recently improved my mental model of Git. Consider these two git commands:</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-title function_">$</span> git fetch origin master</span>
<span class="line"><span class="hl-title function_">$</span> git switch -c my-feature origin/master</span></code></pre>

</figure>
<p>Do you understand why is it <code>origin master</code> in one command, and <code>origin/master</code>
in the other? I didn’t, until a few weeks ago!</p>
<p>My understanding was that git is a content-addressable database. Git stores
commits, a commit is identified by the hash of its content, and the content
of a commit is, primarily:</p>
<ul>
<li>
a memory-less snapshot of a state of the codebase at a given point in time,
</li>
<li>
a list of (hashes of) parent commits.
</li>
</ul>
<p>That was enough git for me to understand <code>git log</code> output and get me out of any
botched rebase without having to re-clone the repo (For roughly half of my
career, I <em>was</em> re-cloning the repo. No shame in that! Learning git is useful,
but it’s not the <em>highest</em> priority thing to learn when you start).</p>
<hr>
<p>I now understand that git not only comes with an append-only (“immutable”)
content-addressable database, but is also a boring mutable key-value store.</p>
<p>Git has a mutable map whose keys are strings, and whose values are
content-addressed objects. The keys are conventionally formatted as file system
paths, and you can usually inspect the state of the mapping by listing
<code>.git/refs</code> directory:</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-title function_">$</span> eza -T .git/refs</span>
<span class="line"><span class="hl-output">.git/refs</span></span>
<span class="line"><span class="hl-output">├── heads</span></span>
<span class="line"><span class="hl-output">│   ├── make</span></span>
<span class="line"><span class="hl-output">│   ├── master</span></span>
<span class="line"><span class="hl-output">│   ├── my-feature</span></span>
<span class="line"><span class="hl-output">│   └── pbd-adt</span></span>
<span class="line"><span class="hl-output">├── origin</span></span>
<span class="line"><span class="hl-output">├── remotes</span></span>
<span class="line"><span class="hl-output">│   └── origin</span></span>
<span class="line"><span class="hl-output">│       ├── context-switches</span></span>
<span class="line"><span class="hl-output">│       ├── gh-pages</span></span>
<span class="line"><span class="hl-output">│       ├── HEAD</span></span>
<span class="line"><span class="hl-output">│       ├── make</span></span>
<span class="line"><span class="hl-output">│       └── master</span></span>
<span class="line"><span class="hl-output">└── tags</span></span>
<span class="line"><span class="hl-output"></span></span>
<span class="line"><span class="hl-title function_">$</span> cat .git/refs/heads/master</span>
<span class="line"><span class="hl-output">b59148228e52f7c615ead7fdd4e91001994ad50f</span></span>
<span class="line"><span class="hl-output"></span></span>
<span class="line"><span class="hl-title function_">$</span> git show-ref refs/heads/master</span>
<span class="line"><span class="hl-output">b59148228e52f7c615ead7fdd4e91001994ad50f refs/heads/master</span></span></code></pre>

</figure>
<p>What makes this <code>refs</code> KV infrastructure confusing is that:</p>
<ul>
<li>
It powers many distinct user-visible git features, but refs themselves are an
implementation detail.
</li>
<li>
It is a bit of a leaky abstraction, refs are <em>almost</em> invisible in the
day-to-day usage.
</li>
<li>
Git CLI uses shorthand notation for refs and many default arguments, which
makes it not obvious that a particular CLI argument is a ref.
</li>
<li>
And, as usual, git likes to give several names to one thing, and re-uses the
same name for distinct things.
</li>
</ul>
<p>Branches, tags, and git notes are all just refs!</p>
<hr>
<p>The structure becomes much more obvious once you elaborate all CLI shortcuts. The original command</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-title function_">$</span> git fetch origin master</span></code></pre>

</figure>
<p>then becomes</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-title function_">$</span> git fetch \</span>
<span class="line">    https://github.com/matklad/matklad.github.io \</span>
<span class="line">    refs/heads/master:refs/remotes/origin/master</span></code></pre>

</figure>
<p>The first argument of <code>fetch</code> (<code>https://...</code>) is a location of a remote
repository. Git will “dial” that address, and will transfer some data from that
computer locally over the network.</p>
<p>The second argument is a <code>source:target</code> pair of string keys (refs). The
<code>source</code> is a key on the remote repo, the <code>target</code> is the name of a local key,
and fetch as a whole asks git to read a value from a remote repository and save
it locally under a different name.</p>
<p>To avoid typing repository URLs all the time, git assigns them symbolic names,
with <code>origin</code> being the conventional name for the primary remote repository:</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-title function_">$</span> git fetch origin \</span>
<span class="line">    refs/heads/master:refs/remotes/origin/master</span></code></pre>

</figure>
<p><code>refs/heads/master</code> is a fully elaborated name of a branch on the remote repo.
That is, branch <code>my-feature</code> is just a
<code class="display">refs/heads/my-feature</code>
ref. It could
have been
<span class="display"><code>refs/branch/my-feature</code>,</span>
but it isn’t :)</p>
<p>I don’t know the specific shorthand rules, but, generally, git allows you to
spell only the suffix of a ref:</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-title function_">$</span> git fetch origin \</span>
<span class="line">    master:refs/remotes/origin/master</span></code></pre>

</figure>
<p><code>refs/remotes/origin/master</code> is the name of the local ref we’ll use to store the
result. It would seem natural to just use the same name locally as the one on
the remote, but this only works if there’s a single remote. If there are two
upstream repositories (for example, your fork, and the original repo you forked
from), their ref names will collide. That’s why we want to namespace the refs
for remote called <code>foo</code> under <code>refs/remotes/foo</code>. And <code>origin</code> is just a
conventional name for <em>the</em> remote in simple setups.</p>
<p>Again, it would be more natural to <em>directly</em> mirror remote ref structure
locally:</p>

<figure class="code-block">


<pre><code><span class="line">refs/ heads/my-branch -&gt; refs/ remotes/origin/ heads/my-branch</span></code></pre>

</figure>
<p>but git strips the redundant heads component. And this <code>-heads</code>,
<code>+remotes/$remote</code> mapping is built in, which compresses the command to</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-title function_">$</span> git fetch origin master</span></code></pre>

</figure>
<p>It’s worth reflecting <em>why</em> it works this way. Git model is offline first.
What’s more, it assumes <em>explicit</em> synchronization points. Rather than
synchronizing with the remote repository in background when there’s
connectivity, git requires explicit <code>fetch</code> and <code>push</code> operations to transfer
bytes over the wire. In this paradigm, it is useful to model the state of the
remote party at the moment when we talked to them the last time. Theory of mind!</p>
<p>This <em>hopefully</em> deconfuses git’s concept of local and remote branches. Consider
the <code>main</code> branch. It exists on the remote named <code>origin</code> as <code>refs/heads/main</code>.
When you synchronize your local repository with <code>origin</code>, you get
<span class="display"><code>refs/remotes/origin/main</code> —</span>
you current best knowledge about the the state of
<code>main</code> on the <code>origin</code>.</p>
<p>And then there’s your local <code>refs/heads/main</code>. It typically starts pointing at
the same commit as
<span class="display"><code>refs/remotes/origin/main</code>.</span>
But, when you make a commit, <code>refs/heads/main</code> advances, but
<code class="display">refs/remotes/origin/main</code>
stays the same.</p>
<p>When you try to push your local commit to origin, you will get a conflict, if
the <code>main</code> branch on the <code>origin</code> advanced in the meanwhile. In that case, git
automatically updates
<code class="display">refs/remotes/origin/main</code>
(as that’s just a local mirror of the remote state), but then it’s on you to
update <code>refs/heads/main</code> and push it again.</p>
<p>Revisiting the full example:</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-title function_">$</span> git fetch origin master</span>
<span class="line"><span class="hl-title function_">$</span> git switch -c my-feature origin/master</span></code></pre>

</figure>
<p>The first command looks up the URL for the <code>origin</code> remote in <code>.git/config</code> and
makes a network request to that machine. As a result, the local
<code>refs/remotes/origin/master</code> gets updated to the same commit as
<code>refs/heads/master</code> remotely (the commit and its ancestors are transferred
locally as a result).</p>
<p>The second command creates a <code>refs/heads/my-feature</code> ref (a branch), whose
starting point is <code>refs/remotes/origin/master</code>. It is an example of a leaky
abstraction.</p>
<p>The second argument there is a (shorthand of a) ref, so you can do</p>

<figure class="code-block">


<pre><code><span class="line">$ git switch -c my-feature \</span>
<span class="line">    refs/remotes/origin/master</span></code></pre>

</figure>
<p>But, although the first argument <em>creates</em> a ref, it isn’t a ref itself. In
other words, if you try to elaborate it as well</p>

<figure class="code-block">


<pre><code><span class="line">$ git switch -c refs/heads/my-feature \</span>
<span class="line">    refs/remotes/origin/master</span></code></pre>

</figure>
<p>you’ll get</p>

<figure class="code-block">


<pre><code><span class="line">refs/heads/refs/heads/my-feature</span></code></pre>

</figure>
<p>That’s all! I am pretty sure this isn’t particularly useful, but maybe it is
interesting!</p>
]]>
    </content>
  </entry>
  <entry>
    <title type="text">Benchmark In Milliseconds</title>
    <link
      href="https://matklad.github.io/2026/10/05/benchmark-milliseconds.html"
      rel="alternate" type="text/html" title="Benchmark In Milliseconds" />
    <published>2026-10-05T00:00:00+00:00</published>
    <updated>2026-10-05T00:00:00+00:00</updated>
    <id>https://matklad.github.io/2026/10/05/benchmark-milliseconds</id>
    <author>
      <name>Alex Kladov</name>
    </author>
    <summary type="html">
      <![CDATA[How long should a micro benchmark run? My rule of thumb is to tweak the input size until the benchmark takes about 300ms, for the following reasons:]]>
    </summary>
    <content type="html"
      xml:base="https://matklad.github.io/2026/10/05/benchmark-milliseconds.html">
      <![CDATA[
<header>
  <h1>Benchmark In Milliseconds</h1>
  <time class="meta" datetime="2026-10-05">Oct 5, 2026</time>
</header>
<p>How long should a micro benchmark run? My rule of thumb is to tweak the input
size until the benchmark takes about 300ms, for the following reasons:</p>
<ul>
<li>
Milliseconds are integers ranging from 1 to 999. Enough precision to notice
even a small improvement, and easy to scan visually. No need for different
units or floating points (compare <code>1.31s</code> with <code>239ms</code>).
</li>
<li>
Anything faster than, say, <code>10ms</code> risks being skewed by fixed costs (e.g,
interpreter startup). Hundreds of milliseconds is an eternity for a computer,
usually enough to make one-off overheads irrelevant without using fancier (=
less robust) techniques to explicitly account for them.
</li>
<li>
For a human, hundreds of milliseconds is fast, but noticeable. Pushing numbers
into human-perceptible range allows me to use my intuitive sense of time and
speed, it doesn’t rely exclusively on numeracy. It’s plain fun to see, as a
result of optimization work, how a previously lagging CLI command becomes
“instant”.
</li>
<li>
But anything longer than a second makes <em>iterating</em> on the benchmark slower
than it needs to be. Running a benchmark 10 times in a row to eyeball variance
should be fast!
</li>
</ul>
<p>The imminently-to-be-stated assumption here is that the purpose of benchmarking
isn’t so much a precise measurement of performance, but rather providing the
author with enough intuition to make a correct decision.</p>
]]>
    </content>
  </entry>
  <entry>
    <title type="text">Finding Bugs</title>
    <link href="https://matklad.github.io/2026/09/19/finding-bugs.html"
      rel="alternate" type="text/html" title="Finding Bugs" />
    <published>2026-09-19T00:00:00+00:00</published>
    <updated>2026-09-19T00:00:00+00:00</updated>
    <id>https://matklad.github.io/2026/09/19/finding-bugs</id>
    <author>
      <name>Alex Kladov</name>
    </author>
    <summary type="html">
      <![CDATA[Are generative (randomized) tests significantly more effective than example-based unit-tests at discovering bugs? There's an interesting discussion about this on lobste.rs. One argument in favor of unit tests is, paraphrasing]]>
    </summary>
    <content type="html"
      xml:base="https://matklad.github.io/2026/09/19/finding-bugs.html">
      <![CDATA[
<header>
  <h1>Finding Bugs</h1>
  <time class="meta" datetime="2026-09-19">Sep 19, 2026</time>
</header>
<p>Are generative (randomized) tests significantly more effective than example-based
unit-tests at discovering bugs? There’s
<a href="https://lobste.rs/s/mkv2pl/unit_tests_mark_territory_more_than#c_5nimhl">an interesting discussion about this on lobste.rs</a>.
One argument in favor of unit tests is, paraphrasing</p>

<figure class="blockquote">
<blockquote><p>My generic fuzzer wasn’t able to find
<a href="https://github.com/rust-lang/regex/issues/1354">this tricky bug</a>
in Rust <code>regex</code> crate.</p>
</blockquote>

</figure>
<p>To me, it seems that generative testing should shake out that particular
creature, so I wrote <a href="https://github.com/matklad/regex-fuzz">a lil fuzzer</a>
of my own, and it indeed discovered <em>another</em> bug in that version of <code>regex</code>,
and then the one I was after. I didn’t find anything in the latest version. I
like to do a write up about the process, as it is a good case study for how one
approaches a problem like this.</p>
<p>I want to be extra clear that my argument is very weak here, as I know exactly
the bug I am after, and I even know that fuzzers can find it. My primary goal is
to teach you the techniques, leaving it to your judgment just how effective they
are. That being said, I think finding a <em>second</em> bug validates the approach
somewhat.</p>
<p>I also want to emphasize that writing fuzzers to find known bugs is far from an
idle amusement. While I believe that generative testing is very powerful,
relative to its cost, it’s always a question whether a particular test is
thorough enough. And it never is, you <em>will</em> find more bugs elsewhere (that’s
why defense in depth and <em>runtime</em> mitigations are critical). And, whenever you
have a pest that dodged your fuzzers, your first order of business is to treat
this event as a bug in the <em>fuzzer</em>, and change it so that it can find this and
related bugs. Only then you are allowed to add a fix and a unit test!</p>
<section id="The-Bug">

<h2><a href="#The-Bug">The Bug</a></h2>
<p>For <code>".abb|b"</code> regex and <code>"zabb"</code> input, an older version of <code>regex</code> crate
returned <code>b</code> as the first match, which is incorrect, because the entire <code>zabb</code>
matches:</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-keyword">use</span> regex;</span>
<span class="line"></span>
<span class="line"><span class="hl-keyword">fn</span> <span class="hl-title function_">main</span>() {</span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-variable">r</span> = regex::Regex::<span class="hl-title function_ invoke__">new</span>(<span class="hl-string">&quot;.abb|b&quot;</span>).<span class="hl-title function_ invoke__">unwrap</span>();</span>
<span class="line"></span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-variable">m</span> = r.<span class="hl-title function_ invoke__">find</span>(<span class="hl-string">&quot;zabb&quot;</span>).<span class="hl-title function_ invoke__">unwrap</span>();</span>
<span class="line"></span>
<span class="line">    <span class="hl-comment">// Fails with regex-automata=0.4.15:</span></span>
<span class="line">    <span class="hl-built_in">assert_eq!</span>(m.<span class="hl-title function_ invoke__">as_str</span>(), <span class="hl-string">&quot;zabb&quot;</span>)</span>
<span class="line">}</span></code></pre>

</figure>
<p>How do we find this, or something <em>like</em> this?</p>
<p>Regular expression engines are one of the easiest things to apply generative
testing to, they are pure algorithms. While few large systems are <em>just</em> an
algorithm, algorithms are everywhere inside components of interesting systems,
so this is a hands-on knowledge.</p>
<p>And by far the most important technique for testing algorithms is to compare
with the known right answer, with an oracle. Implement both <code>O(N log N)</code> and
<code>O(N^2)</code> versions of the algorithm, and match the answers.</p>
<p>To be fair, the original comment mentioned that the their fuzzer didn’t find the
issue because they didn’t have access to an oracle. However, if you are
designing a reliable system, it’s part of your job to ensure it has an oracle!
One of the first things we did for our
<a href="https://jepsen.io/analyses/tigerbeetle-0.16.11">Jepsen test</a> at TigerBeetle was to
<a href="https://github.com/tigerbeetle/tigerbeetle/pull/2481">expose internal timestamps</a>
via API, to make it easier for Jepsen to find bugs (TigerBeetle is
<a href="https://tigerbeetle.com/blog/2026-08-20-protocol-aware-dst/">co-designed</a> with
its internal simulator
<a href="https://github.com/tigerbeetle/tigerbeetle/blob/47aeb2212a255273dda508288412e537d11e4b7c/docs/internals/vopr.md">VOPR</a>
which naturally has access to timestamps and anything else). And for, a regex
engine, coming up with an oracle shouldn’t be hard, as they typically already
come with multiple specialized implementations under a single facade, and the
implementations can be cross-checked against each other.</p>
<p>But the <code>regex</code> case is even simpler (which makes it an excellent case study).
There’s <code>regex_lite</code> crate that provides the same API.</p>
<p>So here’s a plan: generate a regular expression, an input text, and check that
<code>regex</code> and <code>regex_lite</code> give identical answers.</p>
</section>
<section id="Generating-a-String">

<h2><a href="#Generating-a-String">Generating a String</a></h2>
<p>I’ll start with code that generates a random string, as it is simpler, but still
shows some non-trivial ideas. First, we’ll need a random number generator:</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-keyword">use</span> fastrand::Rng;</span></code></pre>

</figure>
<p>There are fancier techniques, which can give you
<a href="https://matklad.github.io/2026/04/20/test-case-minimization.html">test-case minimization</a>,
<a href="https://matklad.github.io/2021/11/07/generate-all-the-things.html">exhaustive search</a>, or
<a href="https://llvm.org/docs/LibFuzzer.html">coverage guided exploration</a>, but the
insight is that even a humble PRNG is brutally effective, if you put it to good use.</p>
<p>When you start with randomized testing, the instinct is to generate something
big, no, HUGE! Surely regex will choke on 5 GiBs of input? This is usually a
wrong call. Bugs <em>usually</em> involve small, but tricky examples, weaponizing
interactions between a few features. A string where all characters are the same
is more likely to trigger a bug than a purely random string where every
character is unique.</p>
<p>So my default approach to generating strings is this. <em>First</em>, I fix the
alphabet of possible characters. A nice way to get one is to <code>sort | unique</code> all
the unit tests. Then, for each particular string, I pick a <em>subset</em> of that
alphabet. I want strings that use all the characters, but I also want long
strings with only <code>a</code> and <code>b</code>! Then I generate a string using the given subset
of the alphabet, where the length of the string is also picked at random.</p>
<p>To make fuzzing efficient, I want to keep each iteration as fast as possible, so
I make sure to re-use the memory across iterations,
<a href="https://github.com/tigerbeetle/tigerbeetle/blob/47aeb2212a255273dda508288412e537d11e4b7c/docs/ARCHITECTURE.md#static-memory-allocation">static allocation</a>
in the small:</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-keyword">use</span> fastrand::Rng;</span>
<span class="line"></span>
<span class="line"><span class="hl-keyword">fn</span> <span class="hl-title function_">main</span>() {</span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-keyword">mut </span><span class="hl-variable">rng</span> = Rng::<span class="hl-title function_ invoke__">new</span>();</span>
<span class="line"></span>
<span class="line">    <span class="hl-comment">// Re-use the same memory for all tests.</span></span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-keyword">mut </span><span class="hl-variable">text_alphabet</span>: <span class="hl-type">Vec</span>&lt;<span class="hl-type">u8</span>&gt; = <span class="hl-built_in">vec!</span>[];</span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-keyword">mut </span><span class="hl-variable">text</span>: <span class="hl-type">Vec</span>&lt;<span class="hl-type">u8</span>&gt; = <span class="hl-built_in">vec!</span>[];</span>
<span class="line"></span>
<span class="line"></span>
<span class="line">    <span class="hl-keyword">for</span> <span class="hl-variable">_</span> <span class="hl-keyword">in</span> <span class="hl-number">0</span>..<span class="hl-number">1_000_000</span> {</span>
<span class="line">        <span class="hl-comment">// It&#x27;s unlikely that a counter example with</span></span>
<span class="line">        <span class="hl-comment">// 7 different letters exists, while there</span></span>
<span class="line">        <span class="hl-comment">// isn&#x27;t one with just 6.</span></span>
<span class="line">        <span class="hl-title function_ invoke__">alphabet_swarm</span>(&amp;<span class="hl-keyword">mut</span> rng, <span class="hl-string">b&quot;abcdef&quot;</span>, &amp;<span class="hl-keyword">mut</span> text_alphabet);</span>
<span class="line">        <span class="hl-keyword">let</span> <span class="hl-variable">text</span> =</span>
<span class="line">            <span class="hl-title function_ invoke__">gen_string</span>(&amp;<span class="hl-keyword">mut</span> rng, &amp;text_alphabet, &amp;<span class="hl-keyword">mut</span> text);</span>
<span class="line">    }</span>
<span class="line">}</span>
<span class="line"></span>
<span class="line"><span class="hl-keyword">fn</span> <span class="hl-title function_">alphabet_swarm</span>&lt;<span class="hl-symbol">&#x27;a</span>&gt;(</span>
<span class="line">    rng: &amp;<span class="hl-keyword">mut</span> Rng,</span>
<span class="line">    all: &amp;[<span class="hl-type">u8</span>],</span>
<span class="line">    pick: &amp;<span class="hl-symbol">&#x27;a</span> <span class="hl-keyword">mut</span> <span class="hl-type">Vec</span>&lt;<span class="hl-type">u8</span>&gt;,</span>
<span class="line">) {</span>
<span class="line">    pick.<span class="hl-title function_ invoke__">clear</span>();</span>
<span class="line">    pick.<span class="hl-title function_ invoke__">extend</span>(all);</span>
<span class="line">    rng.<span class="hl-title function_ invoke__">shuffle</span>(pick);</span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-variable">count</span> = rng.<span class="hl-title function_ invoke__">usize</span>(<span class="hl-number">1</span>..=pick.<span class="hl-title function_ invoke__">len</span>());</span>
<span class="line">    pick.<span class="hl-title function_ invoke__">truncate</span>(count);</span>
<span class="line">}</span>
<span class="line"></span>
<span class="line"><span class="hl-keyword">fn</span> <span class="hl-title function_">gen_string</span>&lt;<span class="hl-symbol">&#x27;a</span>&gt;(</span>
<span class="line">    rng: &amp;<span class="hl-keyword">mut</span> Rng,</span>
<span class="line">    alphabet: &amp;[<span class="hl-type">u8</span>],</span>
<span class="line">    result: &amp;<span class="hl-symbol">&#x27;a</span> <span class="hl-keyword">mut</span> <span class="hl-type">Vec</span>&lt;<span class="hl-type">u8</span>&gt;,</span>
<span class="line">) <span class="hl-punctuation">-&gt;</span> &amp;<span class="hl-symbol">&#x27;a</span> <span class="hl-type">str</span> {</span>
<span class="line">    result.<span class="hl-title function_ invoke__">clear</span>();</span>
<span class="line">    <span class="hl-comment">// Again, this is a short string.</span></span>
<span class="line">    <span class="hl-comment">// Longer failures are not likely.</span></span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-variable">count</span> = rng.<span class="hl-title function_ invoke__">usize</span>(<span class="hl-number">0</span>..<span class="hl-number">8</span>);</span>
<span class="line">    <span class="hl-keyword">for</span> <span class="hl-variable">_</span> <span class="hl-keyword">in</span> <span class="hl-number">0</span>..count {</span>
<span class="line">        result.<span class="hl-title function_ invoke__">push</span>(alphabet[rng.<span class="hl-title function_ invoke__">usize</span>(<span class="hl-number">0</span>..alphabet.<span class="hl-title function_ invoke__">len</span>())]);</span>
<span class="line">    }</span>
<span class="line">    <span class="hl-type">str</span>::<span class="hl-title function_ invoke__">from_utf8</span>(result).<span class="hl-title function_ invoke__">unwrap</span>()</span>
<span class="line">}</span></code></pre>

</figure>
<p>There’s a nice way to think about this two step process, generating alphabet
first, and then generating a string. To generate a string, you need a
distribution of characters. You <em>can</em> use the same distribution for each of the
million iterations. But an easy way to spice things up is to make the
distribution <em>itself</em> random. I file this “randomize distributions themselves” idea under
<a href="https://tigerbeetle.com/blog/2025-04-23-swarm-testing-data-structures/">swarm testing</a>.</p>
</section>
<section id="Generating-a-Regex-Distribution">

<h2><a href="#Generating-a-Regex-Distribution">Generating a Regex Distribution</a></h2>
<p>Let’s apply the same tricks when generating a regex:</p>
<ul>
<li>
pick a subset of active regex features,
</li>
<li>
pick size at random,
</li>
<li>
re-use memory.
</li>
</ul>
<p>Let’s start with the first one:</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-meta">#[derive(Default, Debug)]</span></span>
<span class="line"><span class="hl-keyword">struct</span> <span class="hl-title class_">ReOptions</span> {</span>
<span class="line">    alt: <span class="hl-type">u16</span>, <span class="hl-comment">// |</span></span>
<span class="line">    rep: <span class="hl-type">u16</span>, <span class="hl-comment">// *</span></span>
<span class="line">    any: <span class="hl-type">u16</span>, <span class="hl-comment">// .</span></span>
<span class="line">    lit: <span class="hl-type">u16</span>, <span class="hl-comment">// &#x27;a&#x27;</span></span>
<span class="line">    sum: <span class="hl-type">u16</span>,</span>
<span class="line">    alphabet: <span class="hl-type">Vec</span>&lt;<span class="hl-type">u8</span>&gt;,</span>
<span class="line">}</span></code></pre>

</figure>
<p>Regexes have alternation <code>r1|r2</code>, repetition <code>r*</code>, wildcard <code>.</code>, and literals
<code>a</code>. Rather then binary enabling or disabling a particular feature, I assign
each feature a weight between 0 and 100, which is a bit more general. The <code>sum</code>
is the total of all weights. To select a feature at random, we need to generate
a number in <code>0..sum</code> and find which segment it falls into.</p>
<p>In anything more serious, I’d introduce explicit types for probabilities and
distributions, but just a two-digit number is perfectly serviceable in the
small.</p>
<p>This is how I generate <code>ReOptions</code>, making sure that literals always have
non-zero weight, and also selecting an alphabet for them:</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-keyword">impl</span> <span class="hl-title class_">ReOptions</span> {</span>
<span class="line">    <span class="hl-keyword">fn</span> <span class="hl-title function_">swarm</span>(&amp;<span class="hl-keyword">mut</span> <span class="hl-keyword">self</span>, rng: &amp;<span class="hl-keyword">mut</span> Rng, alphabet_full: &amp;[<span class="hl-type">u8</span>]) {</span>
<span class="line">        <span class="hl-comment">// We _still_ want to enable a few features at a time.</span></span>
<span class="line">        <span class="hl-keyword">self</span>.alt = <span class="hl-keyword">if</span> rng.<span class="hl-title function_ invoke__">bool</span>() { <span class="hl-number">0</span> } <span class="hl-keyword">else</span> { rng.<span class="hl-title function_ invoke__">u16</span>(<span class="hl-number">0</span>..<span class="hl-number">100</span>) };</span>
<span class="line">        <span class="hl-keyword">self</span>.rep = <span class="hl-keyword">if</span> rng.<span class="hl-title function_ invoke__">bool</span>() { <span class="hl-number">0</span> } <span class="hl-keyword">else</span> { rng.<span class="hl-title function_ invoke__">u16</span>(<span class="hl-number">0</span>..<span class="hl-number">100</span>) };</span>
<span class="line">        <span class="hl-keyword">self</span>.any = <span class="hl-keyword">if</span> rng.<span class="hl-title function_ invoke__">bool</span>() { <span class="hl-number">0</span> } <span class="hl-keyword">else</span> { rng.<span class="hl-title function_ invoke__">u16</span>(<span class="hl-number">0</span>..<span class="hl-number">100</span>) };</span>
<span class="line">        <span class="hl-keyword">self</span>.lit = rng.<span class="hl-title function_ invoke__">u16</span>(<span class="hl-number">1</span>..<span class="hl-number">100</span>);</span>
<span class="line">        <span class="hl-keyword">self</span>.sum = <span class="hl-keyword">self</span>.alt + <span class="hl-keyword">self</span>.rep + <span class="hl-keyword">self</span>.any + <span class="hl-keyword">self</span>.lit;</span>
<span class="line">        <span class="hl-built_in">assert!</span>(<span class="hl-keyword">self</span>.sum &gt; <span class="hl-number">0</span>);</span>
<span class="line">        <span class="hl-title function_ invoke__">alphabet_swarm</span>(rng, alphabet_full, &amp;<span class="hl-keyword">mut</span> <span class="hl-keyword">self</span>.alphabet);</span>
<span class="line">    }</span>
<span class="line">}</span></code></pre>

</figure>
</section>
<section id="Generating-a-Regex">

<h2><a href="#Generating-a-Regex">Generating a Regex</a></h2>
<p>So now we can generate a regular expression. This is convenient to do
recursively. To avoid allocations, an output buffer is passed through. To
control regex length, a <code>size</code> parameter is also threaded, and “branching”
recursive invocations divide the <code>size</code> between the children:</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-keyword">fn</span> <span class="hl-title function_">gen_re</span>(</span>
<span class="line">    rng: &amp;<span class="hl-keyword">mut</span> Rng,</span>
<span class="line">    options: &amp;ReOptions,</span>
<span class="line">    result: &amp;<span class="hl-keyword">mut</span> <span class="hl-type">Vec</span>&lt;<span class="hl-type">u8</span>&gt;,</span>
<span class="line">) {</span>
<span class="line">    result.<span class="hl-title function_ invoke__">clear</span>();</span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-variable">size</span> = rng.<span class="hl-title function_ invoke__">u8</span>(<span class="hl-number">0</span>..<span class="hl-number">8</span>);</span>
<span class="line">    <span class="hl-title function_ invoke__">gen_re_rec</span>(rng, options, result, size);</span>
<span class="line"></span>
<span class="line">}</span>
<span class="line"></span>
<span class="line"><span class="hl-keyword">fn</span> <span class="hl-title function_">gen_re_rec</span>(</span>
<span class="line">    rng: &amp;<span class="hl-keyword">mut</span> Rng,</span>
<span class="line">    options: &amp;ReOptions,</span>
<span class="line">    result: &amp;<span class="hl-keyword">mut</span> <span class="hl-type">Vec</span>&lt;<span class="hl-type">u8</span>&gt;,</span>
<span class="line">    size: <span class="hl-type">u8</span>,</span>
<span class="line">) {</span>
<span class="line">    <span class="hl-keyword">if</span> size == <span class="hl-number">0</span> {</span>
<span class="line">        <span class="hl-keyword">return</span>; <span class="hl-comment">// Base case, empty regex.</span></span>
<span class="line">    }</span>
<span class="line"></span>
<span class="line">    <span class="hl-comment">// Pick one of the features, according to weights.</span></span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-keyword">mut </span><span class="hl-variable">p</span> = rng.<span class="hl-title function_ invoke__">u16</span>(<span class="hl-number">0</span>..options.sum);</span>
<span class="line">    <span class="hl-keyword">if</span> p &lt; options.alt {</span>
<span class="line">        <span class="hl-comment">// Alternation distributes the size</span></span>
<span class="line">        <span class="hl-comment">// among the two children.</span></span>
<span class="line">        <span class="hl-keyword">let</span> <span class="hl-variable">size_left</span> = rng.<span class="hl-title function_ invoke__">u8</span>(<span class="hl-number">0</span>..=size - <span class="hl-number">1</span>);</span>
<span class="line">        <span class="hl-keyword">let</span> <span class="hl-variable">size_right</span> = size - size_left - <span class="hl-number">1</span>;</span>
<span class="line">        <span class="hl-built_in">assert!</span>(size == size_left + <span class="hl-number">1</span> + size_right);</span>
<span class="line"></span>
<span class="line">        result.<span class="hl-title function_ invoke__">push</span>(<span class="hl-string">b&#x27;(&#x27;</span>);</span>
<span class="line">        <span class="hl-title function_ invoke__">gen_re_rec</span>(rng, options, result, size_left);</span>
<span class="line">        result.<span class="hl-title function_ invoke__">extend</span>(<span class="hl-string">b&quot;)|(&quot;</span>);</span>
<span class="line">        <span class="hl-title function_ invoke__">gen_re_rec</span>(rng, options, result, size_right);</span>
<span class="line">        result.<span class="hl-title function_ invoke__">push</span>(<span class="hl-string">b&#x27;)&#x27;</span>);</span>
<span class="line">        <span class="hl-keyword">return</span>;</span>
<span class="line">    }</span>
<span class="line">    p -= options.alt;</span>
<span class="line"></span>
<span class="line">    <span class="hl-keyword">if</span> p &lt; options.rep {</span>
<span class="line">        result.<span class="hl-title function_ invoke__">push</span>(<span class="hl-string">b&#x27;(&#x27;</span>);</span>
<span class="line">        <span class="hl-title function_ invoke__">gen_re_rec</span>(rng, options, result, size - <span class="hl-number">1</span>);</span>
<span class="line">        result.<span class="hl-title function_ invoke__">extend</span>(<span class="hl-string">b&quot;)*&quot;</span>);</span>
<span class="line">        <span class="hl-keyword">return</span>;</span>
<span class="line">    }</span>
<span class="line">    p -= options.rep;</span>
<span class="line"></span>
<span class="line">    <span class="hl-keyword">if</span> p &lt; options.any {</span>
<span class="line">        <span class="hl-title function_ invoke__">gen_re_rec</span>(rng, options, result, size - <span class="hl-number">1</span>);</span>
<span class="line">        result.<span class="hl-title function_ invoke__">push</span>(<span class="hl-string">b&#x27;.&#x27;</span>);</span>
<span class="line">        <span class="hl-keyword">return</span>;</span>
<span class="line">    }</span>
<span class="line">    p -= options.any;</span>
<span class="line"></span>
<span class="line">    <span class="hl-keyword">if</span> p &lt; options.lit {</span>
<span class="line">        <span class="hl-title function_ invoke__">gen_re_rec</span>(rng, options, result, size - <span class="hl-number">1</span>);</span>
<span class="line">        <span class="hl-keyword">let</span> <span class="hl-variable">index</span> = rng.<span class="hl-title function_ invoke__">usize</span>(<span class="hl-number">0</span>..options.alphabet.<span class="hl-title function_ invoke__">len</span>());</span>
<span class="line">        <span class="hl-keyword">let</span> <span class="hl-variable">lit</span> = options.alphabet[index];</span>
<span class="line">        result.<span class="hl-title function_ invoke__">push</span>(lit);</span>
<span class="line">        <span class="hl-keyword">return</span>;</span>
<span class="line">    }</span>
<span class="line">    <span class="hl-built_in">unreachable!</span>();</span>
<span class="line">}</span></code></pre>

</figure>
</section>
<section id="Search-Loop">

<h2><a href="#Search-Loop">Search Loop</a></h2>
<p>Given that compiling regular expressions is somewhat slow, it seems like a good
idea to try multiple strings for the same pair of regular expressions, which
gives the following code:</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-keyword">fn</span> <span class="hl-title function_">main</span>() {</span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-keyword">mut </span><span class="hl-variable">rng</span> = Rng::<span class="hl-title function_ invoke__">new</span>();</span>
<span class="line"></span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-keyword">mut </span><span class="hl-variable">options</span> = ReOptions::<span class="hl-title function_ invoke__">default</span>();</span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-keyword">mut </span><span class="hl-variable">text_alphabet</span>: <span class="hl-type">Vec</span>&lt;<span class="hl-type">u8</span>&gt; = <span class="hl-built_in">vec!</span>[];</span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-keyword">mut </span><span class="hl-variable">text</span>: <span class="hl-type">Vec</span>&lt;<span class="hl-type">u8</span>&gt; = <span class="hl-built_in">vec!</span>[];</span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-keyword">mut </span><span class="hl-variable">re</span>: <span class="hl-type">Vec</span>&lt;<span class="hl-type">u8</span>&gt; = <span class="hl-built_in">vec!</span>[];</span>
<span class="line"></span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-keyword">mut </span><span class="hl-variable">test_count</span>: <span class="hl-type">u32</span> = <span class="hl-number">0</span>;</span>
<span class="line">    <span class="hl-keyword">for</span> <span class="hl-variable">_</span> <span class="hl-keyword">in</span> <span class="hl-number">0</span>..<span class="hl-number">1_000_000</span> {</span>
<span class="line">        options.<span class="hl-title function_ invoke__">swarm</span>(&amp;<span class="hl-keyword">mut</span> rng, <span class="hl-string">b&quot;abcdef&quot;</span>);</span>
<span class="line">        <span class="hl-title function_ invoke__">alphabet_swarm</span>(&amp;<span class="hl-keyword">mut</span> rng, <span class="hl-string">b&quot;abcdefx&quot;</span>, &amp;<span class="hl-keyword">mut</span> text_alphabet);</span>
<span class="line"></span>
<span class="line">        <span class="hl-title function_ invoke__">gen_re</span>(&amp;<span class="hl-keyword">mut</span> rng, &amp;options, &amp;<span class="hl-keyword">mut</span> re);</span>
<span class="line"></span>
<span class="line">        <span class="hl-keyword">let</span> <span class="hl-variable">re</span> = <span class="hl-type">str</span>::<span class="hl-title function_ invoke__">from_utf8</span>(&amp;re).<span class="hl-title function_ invoke__">unwrap</span>();</span>
<span class="line">        <span class="hl-keyword">let</span> <span class="hl-variable">r1</span> = regex::Regex::<span class="hl-title function_ invoke__">new</span>(re).<span class="hl-title function_ invoke__">unwrap</span>();</span>
<span class="line">        <span class="hl-keyword">let</span> <span class="hl-variable">r2</span> = regex_lite::Regex::<span class="hl-title function_ invoke__">new</span>(re).<span class="hl-title function_ invoke__">unwrap</span>();</span>
<span class="line"></span>
<span class="line">        <span class="hl-keyword">for</span> <span class="hl-variable">_</span> <span class="hl-keyword">in</span> <span class="hl-number">0</span>..<span class="hl-number">1000</span> {</span>
<span class="line">            test_count += <span class="hl-number">1</span>;</span>
<span class="line">            <span class="hl-keyword">let</span> <span class="hl-variable">text</span> =</span>
<span class="line">                <span class="hl-title function_ invoke__">gen_string</span>(&amp;<span class="hl-keyword">mut</span> rng, &amp;text_alphabet, &amp;<span class="hl-keyword">mut</span> text);</span>
<span class="line"></span>
<span class="line">            <span class="hl-keyword">let</span> <span class="hl-variable">m1</span> = r1.<span class="hl-title function_ invoke__">find</span>(text)</span>
<span class="line">                .<span class="hl-title function_ invoke__">map_or</span>(<span class="hl-string">&quot;not found&quot;</span>, |it| it.<span class="hl-title function_ invoke__">as_str</span>());</span>
<span class="line">            <span class="hl-keyword">let</span> <span class="hl-variable">m2</span> = r2.<span class="hl-title function_ invoke__">find</span>(text)</span>
<span class="line">                .<span class="hl-title function_ invoke__">map_or</span>(<span class="hl-string">&quot;not found&quot;</span>, |it| it.<span class="hl-title function_ invoke__">as_str</span>());</span>
<span class="line"></span>
<span class="line">            <span class="hl-keyword">if</span> m1 != m2 {</span>
<span class="line">                eprintln!(<span class="hl-string">&quot;err re={re} text={text} m1={m1} m2={m2}&quot;</span>);</span>
<span class="line">                <span class="hl-keyword">return</span>;</span>
<span class="line">            }</span>
<span class="line"></span>
<span class="line">            <span class="hl-keyword">if</span> test_count % <span class="hl-number">500_000</span> == <span class="hl-number">0</span> {</span>
<span class="line">                eprintln!(<span class="hl-string">&quot;ok  re={re} text={text}&quot;</span>);</span>
<span class="line">            }</span>
<span class="line">        }</span>
<span class="line">    }</span>
<span class="line">}</span></code></pre>

</figure>
<p>It produces examples similar to those in the issue, with a common suffix:</p>

<figure class="code-block">


<pre><code><span class="line">err re=(e)|(fee) text=xxfee</span></code></pre>

</figure>
<p>but also examples which somewhat different, without the shared suffix:</p>

<figure class="code-block">


<pre><code><span class="line">err re=(f..)*.d text=xfcbdd</span></code></pre>

</figure>
<p>All together:</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-keyword">use</span> fastrand::Rng;</span>
<span class="line"></span>
<span class="line"><span class="hl-keyword">fn</span> <span class="hl-title function_">main</span>() {</span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-keyword">mut </span><span class="hl-variable">rng</span> = Rng::<span class="hl-title function_ invoke__">new</span>();</span>
<span class="line"></span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-keyword">mut </span><span class="hl-variable">options</span> = ReOptions::<span class="hl-title function_ invoke__">default</span>();</span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-keyword">mut </span><span class="hl-variable">text_alphabet</span>: <span class="hl-type">Vec</span>&lt;<span class="hl-type">u8</span>&gt; = <span class="hl-built_in">vec!</span>[];</span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-keyword">mut </span><span class="hl-variable">text</span>: <span class="hl-type">Vec</span>&lt;<span class="hl-type">u8</span>&gt; = <span class="hl-built_in">vec!</span>[];</span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-keyword">mut </span><span class="hl-variable">re</span>: <span class="hl-type">Vec</span>&lt;<span class="hl-type">u8</span>&gt; = <span class="hl-built_in">vec!</span>[];</span>
<span class="line"></span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-keyword">mut </span><span class="hl-variable">test_count</span>: <span class="hl-type">u32</span> = <span class="hl-number">0</span>;</span>
<span class="line">    <span class="hl-keyword">for</span> <span class="hl-variable">_</span> <span class="hl-keyword">in</span> <span class="hl-number">0</span>..<span class="hl-number">1_000_000</span> {</span>
<span class="line">        options.<span class="hl-title function_ invoke__">swarm</span>(&amp;<span class="hl-keyword">mut</span> rng, <span class="hl-string">b&quot;abcdef&quot;</span>);</span>
<span class="line">        <span class="hl-title function_ invoke__">alphabet_swarm</span>(&amp;<span class="hl-keyword">mut</span> rng, <span class="hl-string">b&quot;abcdefx&quot;</span>, &amp;<span class="hl-keyword">mut</span> text_alphabet);</span>
<span class="line"></span>
<span class="line">        <span class="hl-title function_ invoke__">gen_re</span>(&amp;<span class="hl-keyword">mut</span> rng, &amp;options, &amp;<span class="hl-keyword">mut</span> re);</span>
<span class="line"></span>
<span class="line">        <span class="hl-keyword">let</span> <span class="hl-variable">re</span> = <span class="hl-type">str</span>::<span class="hl-title function_ invoke__">from_utf8</span>(&amp;re).<span class="hl-title function_ invoke__">unwrap</span>();</span>
<span class="line">        <span class="hl-keyword">let</span> <span class="hl-variable">r1</span> = regex::Regex::<span class="hl-title function_ invoke__">new</span>(re).<span class="hl-title function_ invoke__">unwrap</span>();</span>
<span class="line">        <span class="hl-keyword">let</span> <span class="hl-variable">r2</span> = regex_lite::Regex::<span class="hl-title function_ invoke__">new</span>(re).<span class="hl-title function_ invoke__">unwrap</span>();</span>
<span class="line"></span>
<span class="line">        <span class="hl-keyword">for</span> <span class="hl-variable">_</span> <span class="hl-keyword">in</span> <span class="hl-number">0</span>..<span class="hl-number">1000</span> {</span>
<span class="line">            test_count += <span class="hl-number">1</span>;</span>
<span class="line">            <span class="hl-keyword">let</span> <span class="hl-variable">text</span> =</span>
<span class="line">                <span class="hl-title function_ invoke__">gen_string</span>(&amp;<span class="hl-keyword">mut</span> rng, &amp;text_alphabet, &amp;<span class="hl-keyword">mut</span> text);</span>
<span class="line"></span>
<span class="line">            <span class="hl-keyword">let</span> <span class="hl-variable">m1</span> = r1.<span class="hl-title function_ invoke__">find</span>(text)</span>
<span class="line">                .<span class="hl-title function_ invoke__">map_or</span>(<span class="hl-string">&quot;not found&quot;</span>, |it| it.<span class="hl-title function_ invoke__">as_str</span>());</span>
<span class="line">            <span class="hl-keyword">let</span> <span class="hl-variable">m2</span> = r2.<span class="hl-title function_ invoke__">find</span>(text)</span>
<span class="line">                .<span class="hl-title function_ invoke__">map_or</span>(<span class="hl-string">&quot;not found&quot;</span>, |it| it.<span class="hl-title function_ invoke__">as_str</span>());</span>
<span class="line"></span>
<span class="line">            <span class="hl-keyword">if</span> m1 != m2 {</span>
<span class="line">                eprintln!(<span class="hl-string">&quot;err re={re} text={text} m1={m1} m2={m2}&quot;</span>);</span>
<span class="line">                <span class="hl-keyword">return</span>;</span>
<span class="line">            }</span>
<span class="line"></span>
<span class="line">            <span class="hl-keyword">if</span> test_count % <span class="hl-number">500_000</span> == <span class="hl-number">0</span> {</span>
<span class="line">                eprintln!(<span class="hl-string">&quot;ok  re={re} text={text}&quot;</span>);</span>
<span class="line">            }</span>
<span class="line">        }</span>
<span class="line">    }</span>
<span class="line">}</span>
<span class="line"></span>
<span class="line"><span class="hl-keyword">fn</span> <span class="hl-title function_">alphabet_swarm</span>&lt;<span class="hl-symbol">&#x27;a</span>&gt;(</span>
<span class="line">    rng: &amp;<span class="hl-keyword">mut</span> Rng,</span>
<span class="line">    all: &amp;[<span class="hl-type">u8</span>],</span>
<span class="line">    pick: &amp;<span class="hl-symbol">&#x27;a</span> <span class="hl-keyword">mut</span> <span class="hl-type">Vec</span>&lt;<span class="hl-type">u8</span>&gt;,</span>
<span class="line">) {</span>
<span class="line">    pick.<span class="hl-title function_ invoke__">clear</span>();</span>
<span class="line">    pick.<span class="hl-title function_ invoke__">extend</span>(all);</span>
<span class="line">    rng.<span class="hl-title function_ invoke__">shuffle</span>(pick);</span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-variable">count</span> = rng.<span class="hl-title function_ invoke__">usize</span>(<span class="hl-number">1</span>..=pick.<span class="hl-title function_ invoke__">len</span>());</span>
<span class="line">    pick.<span class="hl-title function_ invoke__">truncate</span>(count);</span>
<span class="line">}</span>
<span class="line"></span>
<span class="line"><span class="hl-keyword">fn</span> <span class="hl-title function_">gen_string</span>&lt;<span class="hl-symbol">&#x27;a</span>&gt;(</span>
<span class="line">    rng: &amp;<span class="hl-keyword">mut</span> Rng,</span>
<span class="line">    alphabet: &amp;[<span class="hl-type">u8</span>],</span>
<span class="line">    result: &amp;<span class="hl-symbol">&#x27;a</span> <span class="hl-keyword">mut</span> <span class="hl-type">Vec</span>&lt;<span class="hl-type">u8</span>&gt;,</span>
<span class="line">) <span class="hl-punctuation">-&gt;</span> &amp;<span class="hl-symbol">&#x27;a</span> <span class="hl-type">str</span> {</span>
<span class="line">    result.<span class="hl-title function_ invoke__">clear</span>();</span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-variable">count</span> = rng.<span class="hl-title function_ invoke__">usize</span>(<span class="hl-number">0</span>..<span class="hl-number">8</span>);</span>
<span class="line">    <span class="hl-keyword">for</span> <span class="hl-variable">_</span> <span class="hl-keyword">in</span> <span class="hl-number">0</span>..count {</span>
<span class="line">        result.<span class="hl-title function_ invoke__">push</span>(alphabet[rng.<span class="hl-title function_ invoke__">usize</span>(<span class="hl-number">0</span>..alphabet.<span class="hl-title function_ invoke__">len</span>())]);</span>
<span class="line">    }</span>
<span class="line">    <span class="hl-type">str</span>::<span class="hl-title function_ invoke__">from_utf8</span>(result).<span class="hl-title function_ invoke__">unwrap</span>()</span>
<span class="line">}</span>
<span class="line"></span>
<span class="line"><span class="hl-meta">#[derive(Default, Debug)]</span></span>
<span class="line"><span class="hl-keyword">struct</span> <span class="hl-title class_">ReOptions</span> {</span>
<span class="line">    alt: <span class="hl-type">u16</span>, <span class="hl-comment">// |</span></span>
<span class="line">    rep: <span class="hl-type">u16</span>, <span class="hl-comment">// *</span></span>
<span class="line">    any: <span class="hl-type">u16</span>, <span class="hl-comment">// .</span></span>
<span class="line">    lit: <span class="hl-type">u16</span>, <span class="hl-comment">// &#x27;a&#x27;</span></span>
<span class="line">    sum: <span class="hl-type">u16</span>,</span>
<span class="line">    alphabet: <span class="hl-type">Vec</span>&lt;<span class="hl-type">u8</span>&gt;,</span>
<span class="line">}</span>
<span class="line"></span>
<span class="line"><span class="hl-keyword">impl</span> <span class="hl-title class_">ReOptions</span> {</span>
<span class="line">    <span class="hl-keyword">fn</span> <span class="hl-title function_">swarm</span>(&amp;<span class="hl-keyword">mut</span> <span class="hl-keyword">self</span>, rng: &amp;<span class="hl-keyword">mut</span> Rng, alphabet_full: &amp;[<span class="hl-type">u8</span>]) {</span>
<span class="line">        <span class="hl-keyword">self</span>.alt = <span class="hl-keyword">if</span> rng.<span class="hl-title function_ invoke__">bool</span>() { <span class="hl-number">0</span> } <span class="hl-keyword">else</span> { rng.<span class="hl-title function_ invoke__">u16</span>(<span class="hl-number">0</span>..<span class="hl-number">100</span>) };</span>
<span class="line">        <span class="hl-keyword">self</span>.rep = <span class="hl-keyword">if</span> rng.<span class="hl-title function_ invoke__">bool</span>() { <span class="hl-number">0</span> } <span class="hl-keyword">else</span> { rng.<span class="hl-title function_ invoke__">u16</span>(<span class="hl-number">0</span>..<span class="hl-number">100</span>) };</span>
<span class="line">        <span class="hl-keyword">self</span>.any = <span class="hl-keyword">if</span> rng.<span class="hl-title function_ invoke__">bool</span>() { <span class="hl-number">0</span> } <span class="hl-keyword">else</span> { rng.<span class="hl-title function_ invoke__">u16</span>(<span class="hl-number">0</span>..<span class="hl-number">100</span>) };</span>
<span class="line">        <span class="hl-keyword">self</span>.lit = rng.<span class="hl-title function_ invoke__">u16</span>(<span class="hl-number">1</span>..<span class="hl-number">100</span>);</span>
<span class="line">        <span class="hl-keyword">self</span>.sum = <span class="hl-keyword">self</span>.alt + <span class="hl-keyword">self</span>.rep + <span class="hl-keyword">self</span>.any + <span class="hl-keyword">self</span>.lit;</span>
<span class="line">        <span class="hl-built_in">assert!</span>(<span class="hl-keyword">self</span>.sum &gt; <span class="hl-number">0</span>);</span>
<span class="line">        <span class="hl-title function_ invoke__">alphabet_swarm</span>(rng, alphabet_full, &amp;<span class="hl-keyword">mut</span> <span class="hl-keyword">self</span>.alphabet);</span>
<span class="line"></span>
<span class="line">    }</span>
<span class="line">}</span>
<span class="line"></span>
<span class="line"><span class="hl-keyword">fn</span> <span class="hl-title function_">gen_re</span>(</span>
<span class="line">    rng: &amp;<span class="hl-keyword">mut</span> Rng,</span>
<span class="line">    options: &amp;ReOptions,</span>
<span class="line">    result: &amp;<span class="hl-keyword">mut</span> <span class="hl-type">Vec</span>&lt;<span class="hl-type">u8</span>&gt;,</span>
<span class="line">) {</span>
<span class="line">    result.<span class="hl-title function_ invoke__">clear</span>();</span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-variable">size</span> = rng.<span class="hl-title function_ invoke__">u8</span>(<span class="hl-number">0</span>..<span class="hl-number">8</span>);</span>
<span class="line">    <span class="hl-title function_ invoke__">gen_re_rec</span>(rng, options, result, size);</span>
<span class="line"></span>
<span class="line">}</span>
<span class="line"></span>
<span class="line"><span class="hl-keyword">fn</span> <span class="hl-title function_">gen_re_rec</span>(</span>
<span class="line">    rng: &amp;<span class="hl-keyword">mut</span> Rng,</span>
<span class="line">    options: &amp;ReOptions,</span>
<span class="line">    result: &amp;<span class="hl-keyword">mut</span> <span class="hl-type">Vec</span>&lt;<span class="hl-type">u8</span>&gt;,</span>
<span class="line">    size: <span class="hl-type">u8</span>,</span>
<span class="line">) {</span>
<span class="line">    <span class="hl-keyword">if</span> size == <span class="hl-number">0</span> {</span>
<span class="line">        <span class="hl-keyword">return</span>; <span class="hl-comment">// Base case, empty regex.</span></span>
<span class="line">    }</span>
<span class="line"></span>
<span class="line">    <span class="hl-comment">// Pick one of the features, according to weights.</span></span>
<span class="line">    <span class="hl-keyword">let</span> <span class="hl-keyword">mut </span><span class="hl-variable">p</span> = rng.<span class="hl-title function_ invoke__">u16</span>(<span class="hl-number">0</span>..options.sum);</span>
<span class="line">    <span class="hl-keyword">if</span> p &lt; options.alt {</span>
<span class="line">        <span class="hl-comment">// Alternation distributes the size</span></span>
<span class="line">        <span class="hl-comment">// among the two children.</span></span>
<span class="line">        <span class="hl-keyword">let</span> <span class="hl-variable">size_left</span> = rng.<span class="hl-title function_ invoke__">u8</span>(<span class="hl-number">0</span>..=size - <span class="hl-number">1</span>);</span>
<span class="line">        <span class="hl-keyword">let</span> <span class="hl-variable">size_right</span> = size - size_left - <span class="hl-number">1</span>;</span>
<span class="line">        <span class="hl-built_in">assert!</span>(size == size_left + <span class="hl-number">1</span> + size_right);</span>
<span class="line"></span>
<span class="line">        result.<span class="hl-title function_ invoke__">push</span>(<span class="hl-string">b&#x27;(&#x27;</span>);</span>
<span class="line">        <span class="hl-title function_ invoke__">gen_re_rec</span>(rng, options, result, size_left);</span>
<span class="line">        result.<span class="hl-title function_ invoke__">extend</span>(<span class="hl-string">b&quot;)|(&quot;</span>);</span>
<span class="line">        <span class="hl-title function_ invoke__">gen_re_rec</span>(rng, options, result, size_right);</span>
<span class="line">        result.<span class="hl-title function_ invoke__">push</span>(<span class="hl-string">b&#x27;)&#x27;</span>);</span>
<span class="line">        <span class="hl-keyword">return</span>;</span>
<span class="line">    }</span>
<span class="line">    p -= options.alt;</span>
<span class="line"></span>
<span class="line">    <span class="hl-keyword">if</span> p &lt; options.rep {</span>
<span class="line">        result.<span class="hl-title function_ invoke__">push</span>(<span class="hl-string">b&#x27;(&#x27;</span>);</span>
<span class="line">        <span class="hl-title function_ invoke__">gen_re_rec</span>(rng, options, result, size - <span class="hl-number">1</span>);</span>
<span class="line">        result.<span class="hl-title function_ invoke__">extend</span>(<span class="hl-string">b&quot;)*&quot;</span>);</span>
<span class="line">        <span class="hl-keyword">return</span>;</span>
<span class="line">    }</span>
<span class="line">    p -= options.rep;</span>
<span class="line"></span>
<span class="line">    <span class="hl-keyword">if</span> p &lt; options.any {</span>
<span class="line">        <span class="hl-title function_ invoke__">gen_re_rec</span>(rng, options, result, size - <span class="hl-number">1</span>);</span>
<span class="line">        result.<span class="hl-title function_ invoke__">push</span>(<span class="hl-string">b&#x27;.&#x27;</span>);</span>
<span class="line">        <span class="hl-keyword">return</span>;</span>
<span class="line">    }</span>
<span class="line">    p -= options.any;</span>
<span class="line"></span>
<span class="line">    <span class="hl-keyword">if</span> p &lt; options.lit {</span>
<span class="line">        <span class="hl-title function_ invoke__">gen_re_rec</span>(rng, options, result, size - <span class="hl-number">1</span>);</span>
<span class="line">        <span class="hl-keyword">let</span> <span class="hl-variable">index</span> = rng.<span class="hl-title function_ invoke__">usize</span>(<span class="hl-number">0</span>..options.alphabet.<span class="hl-title function_ invoke__">len</span>());</span>
<span class="line">        <span class="hl-keyword">let</span> <span class="hl-variable">lit</span> = options.alphabet[index];</span>
<span class="line">        result.<span class="hl-title function_ invoke__">push</span>(lit);</span>
<span class="line">        <span class="hl-keyword">return</span>;</span>
<span class="line">    }</span>
<span class="line">    <span class="hl-built_in">unreachable!</span>();</span>
<span class="line">}</span></code></pre>

</figure>
<p><a href="https://github.com/matklad/regex-fuzz" class="display url">https://github.com/matklad/regex-fuzz</a></p>
<p>Takeaways:</p>
<ul>
<li>
Fuzzing against an oracle is effective, which is a strong motivation to build
an oracle!
</li>
<li>
Go for small, tricky examples, rather than large uniform ones.
</li>
<li>
Real fuzzers are cool, but, if you know something, even xoroshiro can be dangerous.
</li>
<li>
Black box testing is cool, but co-designing system and its testing harness is
a point of leverage (build an oracle!).
</li>
<li>
This stuff is not rocket science, you don’t need a Haskell PhD to apply these
ideas.
</li>
</ul>
</section>
]]>
    </content>
  </entry>
  <entry>
    <title type="text">Static Allocation, Constant Work</title>
    <link
      href="https://matklad.github.io/2026/09/02/static-allocation-constant-work.html"
      rel="alternate" type="text/html"
      title="Static Allocation, Constant Work" />
    <published>2026-09-02T00:00:00+00:00</published>
    <updated>2026-09-02T00:00:00+00:00</updated>
    <id>https://matklad.github.io/2026/09/02/static-allocation-constant-work</id>
    <author>
      <name>Alex Kladov</name>
    </author>
    <summary type="html">
      <![CDATA[In reply to this email:]]>
    </summary>
    <content type="html"
      xml:base="https://matklad.github.io/2026/09/02/static-allocation-constant-work.html">
      <![CDATA[
<header>
  <h1>Static Allocation, Constant Work</h1>
  <time class="meta" datetime="2026-09-02">Sep 2, 2026</time>
</header>
<p>In reply to this email:</p>

<figure class="blockquote">
<blockquote><p><a href="https://matklad.github.io/2026/07/20/memory-safety-hardest-problem.html"><em>Memory Safety’s Hardest Problem</em></a>
named something I’d hit but couldn’t articulate.
Your case is a pointer into one union variant surviving a write of a different
variant, so live typed pointers end up reading bytes that belong to something
else now.</p>
<p>Last year I wrote a limit-order matching engine and shipped a use-after-free: a
cancelled order was released back to the pool while it was still linked into its
price level, so the next allocation handed that memory to a new order and the
stale link kept resolving. I’d filed it under “I was careless with lifetimes.”</p>
<p>After your post I’m not sure that’s what it was. A recycling pool looks like a
tagged union where the tag is “which generation of object currently lives in
this slot,” and nothing in the type system tracks it. Is that a fair reading, or
does the pool case stay genuinely easier because generational indices actually
solve it and the union case has no equivalent?</p>
</blockquote>

</figure>
<p>Yes, object pools are an interesting case to think about, as they clarify the
relation between <em>memory</em> safety and more general correctness.</p>
<p><em>First</em>, consider the case where no object pool is used, and we <code>malloc</code> and
<code>free</code> order objects. In this case, the logical error of use-after-free turns
into physical type confusion, and can easily lead to arbitrary code execution
and the like. If you have two objects of different <em>types</em> sharing the same
memory location, a user-controlled integer in one object might be a function
pointer in the other: an exploitable <code>goto</code> primitive</p>
<p>Now, what happens if we introduce an object pool which stores a list of “dead”
objects of type <code>T</code>? Logical use-after-free is still possible, but its physical
effect is now different — we still get aliasing of memory, but there’s no type
confusion. You can’t necessarily fiddle with an integer and change a function
pointer, unless you additionally hit the hard case, where the object in question
stores an inline enum. Assuming that doesn’t hapen, you get a perfectly defined,
deterministic behavior, even if you are not happy about the result.</p>
<p>This suggests an interesting solution for hardening code, which I’ve learned
from <a href="https://fil-c.org/meet_fil">Fil</a>. <em>If</em> your allocation function is typed
(it takes a <code>T</code> comptime parameter or runtime type witness, rather than a
runtime type-erased size and alignment), you can write an allocator that uses
type-segregated pools internally. This will be somewhat less memory efficient,
as the allocator won’t be able to re-use freed memory of objects of type <code>U</code> for
objects of type <code>T</code>, but the memory overhead will probably be small (rare object
types do not matter, popular object types will have a lot of intra-type re-use),
you might actually gain in memory locality, <em>and</em> solve most of type confusions.
Again, inline enums break this, but, curiously, if you always heap allocate enum
variants, then this works again. Fil-C can’t use this, because C allocator’s
interface is untyped, but someone else could :P</p>
<p>But this is academic. How do we avoid the bugs? Generational indexes are a
popular remedy, but I have never used them, so I don’t have any non-common
knowledge insights about this pattern. Instead, I will share another pair of tricks
from <a href="https://github.com/tigerbeetle/tigerbeetle/blob/main/docs/TIGER_STYLE.md">TigerStyle</a>.
I have only a vague understanding of what an order matching engine is, but I
suspect these tricks might help there</p>
<section id="Static-Allocation">

<h2><a href="#Static-Allocation">Static Allocation</a></h2>
<p>The first one is:</p>

<figure class="blockquote">
<blockquote><p>No dynamic memory allocation after initialization</p>
</blockquote>

</figure>
<p><a href="https://www.youtube.com/watch?v=GRJtYwneG2Q&amp;t=1823s" class="display url">https://www.youtube.com/watch?v=GRJtYwneG2Q&amp;t=1823s</a></p>
<p>This is the pool idea, taken to its logical conclusion. We specify the maximum
number of orders we are willing to work with at startup, and never go beyond
that. So, you might start the program as</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-title function_">$</span> order-engine --orders-max=1_000_000</span></code></pre>

</figure>
<p>and then one of the first lines in its <code>main</code> function would be :</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-keyword">const</span> orders: []Order = <span class="hl-keyword">try</span> gpa.alloc(Order, cli_args.orders_max);</span></code></pre>

</figure>
<p>If, at runtime, more than <code>orders_max</code> requests come in, the surplus requests
are rejected. Someone might object: “But what if I actually have some spare
memory for one more order? Wouldn’t it be a good idea to at least try to handle
it?”</p>
<p>My rejoinder would be “Well, what if you don’t?”. Systems operating at capacity
<em>without</em> strict limits fail catastrophically. Attempting to allocate just one
more <code>Order</code> could cause kernel’s OOM killer to terminate the entire order
matching engine, losing the other million orders, or, better yet, to kill the
supervisor process so that you can’t even restart.</p>
<p>Static allocation gives you peace of mind. The system might fail to start if you
don’t have enough memory, but, if it did start, you can rest assured that it
would handle overload gracefully, continuing to render the service while you are
provisioning a beefier machine.</p>
</section>
<section id="Constant-Work">

<h2><a href="#Constant-Work">Constant Work</a></h2>
<p>What would you do with the slice of orders? One approach is to
<code class="display">@memset(orders, undefined)</code>
and hand the slice over to a pool which tracks spare objects with a bit set:</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-keyword">const</span> OrderPool = <span class="hl-keyword">struct</span> {</span>
<span class="line">    orders: []Order,</span>
<span class="line">    free: DynamicBitSet,</span>
<span class="line"></span>
<span class="line">    <span class="hl-keyword">fn</span><span class="hl-function"> acquire</span>(pool: <span class="hl-operator">*</span>OrderPool) ?<span class="hl-operator">*</span>Order { ... }</span>
<span class="line">    <span class="hl-keyword">fn</span><span class="hl-function"> release</span>(pool: <span class="hl-operator">*</span>OrderPool, order: <span class="hl-operator">*</span>Order) { ... }</span>
<span class="line">};</span></code></pre>

</figure>
<p>or with a free list:</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-keyword">const</span> OrderPool = <span class="hl-keyword">struct</span> {</span>
<span class="line">    orders: []<span class="hl-keyword">union</span> {</span>
<span class="line">        order: Order,</span>
<span class="line">        next_free: ?<span class="hl-type">u32</span>,</span>
<span class="line">    },</span>
<span class="line">    first_free: ?<span class="hl-type">u32</span>,</span>
<span class="line">};</span></code></pre>

</figure>
<p>But there’s an alterative approach. Instead of thinking about a limit on the
number of orders, you could instead design the system to <em>always</em> have a fixed
amount of orders, by introducing a no-op, neutral order:</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-keyword">const</span> Order = {</span>
<span class="line">    id: <span class="hl-type">u128</span>,</span>
<span class="line">    price: <span class="hl-type">u32</span>,</span>
<span class="line">    count: <span class="hl-type">u32</span>,</span>
<span class="line"></span>
<span class="line">    tag: <span class="hl-keyword">enum</span> { bid, ask, reserved },</span>
<span class="line"></span>
<span class="line">    <span class="hl-keyword">pub</span> <span class="hl-keyword">const</span> reserved: Order = .{</span>
<span class="line">        .id = <span class="hl-numbers">0</span>,</span>
<span class="line">        .price = <span class="hl-numbers">0</span>,</span>
<span class="line">        .count = <span class="hl-numbers">0</span>,</span>
<span class="line">        .tag = .reserved,</span>
<span class="line">    };</span>
<span class="line">};</span></code></pre>

</figure>
<p>Your initialization then becomes <span class="display"><code>@memset(orders, .reserved)</code>.</span></p>
<p>One benefit here is cognitive, you no longer think in terms of creating and
destroying orders. Instead, the orders merely circulate in the system according
to the law of the conservation of the number of orders. It becomes harder to
lose track of an order if you must always pay attention not only to where the
order goes, but also to where it came from. You explicitly write state
transition functions for each <em>pair</em> of states, and that makes it easier to
exhaustively enumerate all the cases. And you double-check that with asserting,
at every point, that the state is what you expect it to be (and then you DST the
asserts) .</p>
<p>Another benefit is code-simplification and predictability. You no longer need to
track a separate collection of “live” orders. Instead, you always iterate the
full set, doing no-ops for reserved. This feels wasteful: should we make the
code run faster when there are few orders? But consider this: by specifying the
limit of orders up-front, you commit to be able to serve that amount. <em>If</em> the
maximum amount of orders is active, does the system have acceptable performance?
If not, that is a bug! Gray failure (system becoming unusably slow) is another
way to break when reaching the limit.</p>
<p>Avoiding indexes improves performance for the maximal load case. This</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-keyword">for</span> (orders) <span class="hl-operator">|</span>order<span class="hl-operator">|</span> {</span>
<span class="line">    process(order)</span>
<span class="line">}</span></code></pre>

</figure>
<p>is much easier for a compiler to vectorize, and for a CPU Cache to prefetch, than
this:</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-keyword">for</span> (orders_active) <span class="hl-operator">|</span>order_index<span class="hl-operator">|</span> {</span>
<span class="line">    <span class="hl-keyword">const</span> order = orders[order_index];</span>
<span class="line">    process(order);</span>
<span class="line">}</span></code></pre>

</figure>
<p>Similarly to static allocation, the <em>Constant Work</em> principle gives you peace of
mind with respect to performance. P100 latency stays flat regardless of the
load. Insufficient performance is discovered when you roll out the system, not
during Black Friday on-call.</p>
<p>At TigerBeetle, we apply this pattern in the small. Rather than writing a
search loop with an early return:</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-keyword">const</span> item = <span class="hl-keyword">for</span> (items) <span class="hl-operator">|</span>item<span class="hl-operator">|</span> {</span>
<span class="line">    <span class="hl-keyword">if</span> (predicate(item)) <span class="hl-keyword">break</span> item;</span>
<span class="line">} <span class="hl-keyword">else</span> <span class="hl-literal">null</span>;</span></code></pre>

</figure>
<p>we sometimes let the loop to run its full natural course, additionally asserting
that theres a <em>unique</em> matching item:</p>
<p><a href="https://github.com/tigerbeetle/tigerbeetle/blob/0.17.9/src/vsr/grid.zig#L715-L725" class="url">https://github.com/tigerbeetle/tigerbeetle/blob/0.17.9/src/vsr/grid.zig#L715-L725</a></p>
<hr>
<p>As usual, this is <em>a</em> trick that is useful to have in your arsenal, but it
isn’t a universal solution to all programming’s problems.</p>
</section>
]]>
    </content>
  </entry>
  <entry>
    <title type="text">Cancelation Terminology</title>
    <link
      href="https://matklad.github.io/2026/08/31/cancelation-terminology.html"
      rel="alternate" type="text/html" title="Cancelation Terminology" />
    <published>2026-08-31T00:00:00+00:00</published>
    <updated>2026-08-31T00:00:00+00:00</updated>
    <id>https://matklad.github.io/2026/08/31/cancelation-terminology</id>
    <author>
      <name>Alex Kladov</name>
    </author>
    <summary type="html">
      <![CDATA[A short note explaining the difference between synchronous cancelation, asynchronous cancelation, and graceful shutdown. I am not too attached to these specific three terms, but I want to call your attention to the three things behind them, which are important not to confuse with each other.]]>
    </summary>
    <content type="html"
      xml:base="https://matklad.github.io/2026/08/31/cancelation-terminology.html">
      <![CDATA[
<header>
  <h1>Cancelation Terminology</h1>
  <time class="meta" datetime="2026-08-31">Aug 31, 2026</time>
</header>
<p>A short note explaining the difference between synchronous cancelation,
asynchronous cancelation, and graceful shutdown. I am not too attached to these
specific three <em>terms</em>, but I want to call your attention to the three
<em>things</em> behind them, which are important not to confuse with each other.</p>
<p><dfn>synchronous cancelation</dfn> is an (often implicit) control flow structure.
It unwinds the stack and looks like this:</p>

<figure class="code-block">


<pre><code><span class="line">task.<span class="hl-title function_ invoke__">cancel</span>();</span>
<span class="line"><span class="hl-comment">// The task will have finished by this point.</span></span></code></pre>

</figure>
<p>Synchronous cancelation is a bit like Molière’s prose — we do it all the time,
but not necessarily in full consciousness. The primary source of synchronous
cancelation is error handling — every time an <code>Exception</code> is thrown or an
<code>error</code> returned, the code promptly breaks out of all the loops, ifs, and
blocks, invoking the necessary cleanup actions via RAII, <code>finally</code>, <code>with</code>/<code>try</code>
with resources or <code>defer</code>.</p>
<p><dfn>asynchronous cancelation</dfn> is a communication protocol between two
parties. One party requests cancelation (synchronously), but then it has to wait
until the other party acknowledges it and winds down. It looks like this:</p>

<figure class="code-block">


<pre><code><span class="line">task.<span class="hl-title function_ invoke__">request_cancelation</span>();</span>
<span class="line"><span class="hl-comment">// The task could still be running here.</span></span>
<span class="line">task.<span class="hl-title function_ invoke__">join</span>().<span class="hl-keyword">await</span>;</span>
<span class="line"><span class="hl-comment">// After the requisite wait, the task is finished.</span></span></code></pre>

</figure>
<p>Like synchronous cancelation, this is a relatively low-level concern when
implementing a concurrent program in a way that doesn’t crash or hang. I know
two central example where an asynchronous cancelation is required.</p>
<p>First is the CPU thread pool. Imagine you have offloaded encrypting a buffer to
a separate thread as a part of handling user’s request. Some time later, you
learn that the request must be canceled (perhaps the user had left). You can’t
just abandon the encrypting thread. First, it would be smart not to waste CPU
cycles for useless work, but, more importantly, the underlying <em>buffer</em> must
remain tied up. If it were to be freed as a result of request cancelation,
something else might re-use that memory, leading to data races.</p>
<p>But you also can’t just cancel that thread synchronously! It’s in the middle of a
hyper-optimized SIMD loop, and you really don’t want it to check the cancelation
flag before reading every byte. What you’d want is to split the buffer into
reasonably-sized chunks, and check the cancelation status after every chunk. But
that means that the party that requested the cancelation must wait for at least
one chunk’s worth of work!</p>

<aside class="admn note">
<svg class="icon"><use href="/assets/icons.svg#info"/></svg>
<div><p>For the curious, there’s actually a bit more leeway with canceling CPU work
non-cooperatively, see</p>
<p><a href="https://go.dev/src/runtime/preempt.go" class="display url">https://go.dev/src/runtime/preempt.go</a></p>
</div>
</aside><p>Another example here is <code>io_uring</code>. It has exactly the same shape: if you submit
a write with a buffer to the kernel, that buffer must remain tied up until the
write finishes (and you can cancel the write to make it finish faster). While
<code>io_uring</code> is still at least a somewhat exotic technology (though, arguably,
it’s the interfaces we have had before which are byzantine), the thread pool
example demonstrates that the phenomenon of asynchronous cancelation itself is
rather mundane.</p>
<p>Asynchronous cancelation comes up all the time when writing concurrent software.
Because it affects the overall shape of the code, it’s useful to identify it
early. Conversely, it is useful to ask yourself whether you need asynchronous
cancelation at all, and whether synchronous one can be made to work. This is
especially important in Rust, which makes synchronous cancelation too easy, and
doesn’t provide great mechanisms for asynchronous one.</p>
<p>Finally, <dfn>graceful shutdown</dfn> is an application programming pattern for
handling connections. It lives on a higher level of abstraction than the two
cancelations. If you are implementing a web service, you can implement shutdown
by stopping your <code>accept</code> loop (rejecting new connections), but continuing to
serve all existing connections until their respective clients disconnect. If the
load balancer is configured to route new connection requests to different
instances of the service, this pattern allows you to do rolling upgrades without
service disruptions.</p>
<p>As a bonus point, a related idea is that of
<a href="https://www.usenix.org/legacy/events/hotos03/tech/full_papers/candea/candea_html/index.html">crash-only software</a>.
Cancelation is all good, but your entire program can get SIGKILLed arbitrarily
by an OOM killer, and the entire computer might get rebooted on powerloss.
Reliable software has to handle ungraceful shutdown without losing data. But, if
you can survive powerloss, you might as well implement the <kbd><kbd>Quit</kbd></kbd> button
by SIGKILLing yourself, simultaneously simplifying the implementation and
increasing testing coverage for powerloss scenarios.</p>
<hr>
<p>To give some examples from TigerBeetle,
<a href="https://github.com/tigerbeetle/tigerbeetle/blob/47aeb2212a255273dda508288412e537d11e4b7c/src/vsr/grid.zig#L589"><code>Grid.cancel</code></a>
is an asynchronous cancelation. It takes a callback to notify the caller when
the cancelation is done. This API is used during state sync. When a replica
determines that that cluster is so far ahead that event based transfer doesn’t
work, and that a state transfer is required to catch up, it must cancel all
outstanding grid read operations. A read can be backed either by replica’s local
disk, or by transparent fetch of the data from a neighboring replica. In the
first case, we have to wait until the read is done. In the second case, we need
to abandon the read — remote read getting stuck is probably <em>the</em> reason for
us to state sync in the first place.</p>
<p><a href="https://github.com/tigerbeetle/tigerbeetle/blob/47aeb2212a255273dda508288412e537d11e4b7c/src/state_machine.zig#L942"><code>StateMachine.reset</code></a>
is an example of a synchronous cancelation. This is the part of the same flow as
<code>Grid.cancel</code>, and is an example of how you can simplify the code if you think
clearly about asynchronous vs synchronous cancelation. Ultimately,
<code>StateMachine</code> sits on top of the <code>Grid</code>, but there’s a bunch of intermediate
layers (<code>Forest</code>, <code>Tree</code>, <code>Compaction</code>, <code>Scan</code>, etc). A naive approach would be
to notice that <code>Grid</code> requires asynchronous cancelation and propagate asynchrony
throughout the stack. What we do instead is asynchronously canceling <em>just</em> the
<code>Grid</code> directly, and then synchronously <code>reset</code>ing everything else.</p>
<p>Another example of asynchronous cancelation is
<a href="https://github.com/tigerbeetle/tigerbeetle/blob/47aeb2212a255273dda508288412e537d11e4b7c/src/vsr/client.zig#L194-L203"><code>Client.shutdown</code></a>.
When an application using TigerBeetle “drops” the <code>Client</code> object, we need to
free all OS resources. Our client also uses io_uring, so we must first wait for
all outstanding syscalls to complete. In the comment, we call it “graceful
shutdown”, but I think this is wrong, and this is the motivation for writing
down this article. We don’t do graceful shutdown at TigerBeetle — it’s crash
only all the way. Tail latency tolerance (asking several nodes for an answer and
picking the fastest one) is a more general solution, as it handles not only
crash faults, but also
<a href="https://www.microsoft.com/en-us/research/wp-content/uploads/2017/06/paper-1.pdf">gray failures</a>.
In a distributed system, a very slow node looks exactly the same as a crashed
one. A crash is just a degree of slowness.</p>
<hr>
<p>Take aways:</p>
<ul>
<li>
Synchronous cancelation is control flow operator
</li>
<li>
Asynchronous cancelation is a communication protocol
</li>
<li>
Graceful shutdown is an application-level design pattern
</li>
</ul>
]]>
    </content>
  </entry>
  <entry>
    <title type="text">Rust Glancer</title>
    <link href="https://matklad.github.io/2026/08/21/rust-glancer.html"
      rel="alternate" type="text/html" title="Rust Glancer" />
    <published>2026-08-21T00:00:00+00:00</published>
    <updated>2026-08-21T00:00:00+00:00</updated>
    <id>https://matklad.github.io/2026/08/21/rust-glancer</id>
    <author>
      <name>Alex Kladov</name>
    </author>
    <summary type="html">
      <![CDATA[Rust Glancer, a functional LSP server for Rust which uses two orders of magnitude less RAM, is incredibly cool. Go check it out! This post started as a comment on lobste.rs, but I figured it out that it's better to publish it somewhat more prominently. Don't expect polished writing though!]]>
    </summary>
    <content type="html"
      xml:base="https://matklad.github.io/2026/08/21/rust-glancer.html">
      <![CDATA[
<header>
  <h1>Rust Glancer</h1>
  <time class="meta" datetime="2026-08-21">Aug 21, 2026</time>
</header>
<p><a href="https://rust-glancer.github.io/blog/hello-world/">Rust Glancer</a>, a functional
LSP server for Rust which uses two orders of magnitude less RAM, is incredibly
cool. Go check it out! This post started as a comment on lobste.rs, but I
figured it out that it’s better to publish it somewhat more prominently. Don’t
expect polished writing though!</p>
<p>Some thoughts:</p>

<figure class="blockquote">
<blockquote><p>rust-analyzer uses rowan for syntax tree representation</p>
</blockquote>

</figure>
<p>Yeah, rowan is garbage :P I was really thinking about</p>
<ul>
<li>
incremental parsing,
</li>
<li>
incremental, DOM-mutation style refactorings,
</li>
</ul>
<p>And Rowan is pretty good for that. But that’s 1% use case. The 99% use case is
all the code in your 6666 dependencies which you won’t ever look at, but which
needs to be at least shallowly analyzed. Even for incremental tool whose main
goal is refactoring, the primary AST structure should be just a list of arrays.
There might be a real post about that at some point, see
<a href="https://youtu.be/G93oYL1ry70" class="url">https://youtu.be/G93oYL1ry70</a> as a teaser.</p>

<figure class="blockquote">
<blockquote><p>Rust workspaces genuinely have a lot of information that must be indexed:
thousands of functions, structures, traits, relationships between these,
function bodies and statements in them, etc. Each of these needs to be
analyzed and remembered, and you can’t really cheat if you want to have things
like “find all references to this structure”.</p>
</blockquote>

</figure>
<p>If I understand correctly, Rust Glancer wants to process each function body. I
think <em>that</em> part can perhaps be made lazy (but not incremental!) with little
overhead? Index all items, but, for functions, do only the currently opened
file? This might combine some of the better parts of both worlds.</p>
<p>Would be interesting to compare memory usage with Rust Rover. Net of the IDE
GUI itself, I would expect RR to be more compact.</p>

<figure class="blockquote">
<blockquote><p>Some features are unlikely to be supported though, such as build scripts /
proc macros support via proc macro invocation</p>
</blockquote>

</figure>
<p>I might be rationalizing/misremembering things, but IIRC it’s exactly around
adding proc macros that the thing began to feel unreasonably bulky. Expanding
proc macros is slow as we are running real code, we can’t really do normal IDE
cheats. And proc macros generate a lot of code. At one point I measured, it was
like 30% of rust-analyzer binary size was attributed to JSON parsing code. If no
one sees the code, it can’t harm anybody, right?</p>
<p>One potential approach here is to pull the Sorbet trick, where you don’t run
meta programming at all, and instead have a plugin interface to “explain” the
<em>effects</em> of what that would have done. Instead of running serde, we just add a
shim that injects <code class="display">imp Serialize for T {}</code> with an empty body.</p>

<figure class="blockquote">
<blockquote><p>I’m not sure why, but in rust-analyzer I’ve observed that when agents edit the
code, inlay hints can get out of place</p>
</blockquote>

</figure>
<p>Rust analyzer’s core data model is <em>very</em> pedantic about always observing
consistent snapshots of the code, and does its best to ensure that the language
client and server have a shared, strictly serializable view of the world. It’s a
shame that
<a href="https://github.com/Microsoft/language-server-protocol/issues/584">LSP doesn’t allow that to be <em>correct</em>, only heuristically right</a>,
unlike the older Dart Analyzer protocol, which has sound data synchronization.</p>
<p>However our implementation of file watching is sketchy! First, there are two
backends: we can ask the editor to do watching for us, or we can use server side
watching. Try changing
<a href="https://rust-analyzer.github.io/book/configuration.html#files.watcher">this option</a>
and see if it helps? But then, yeah, my recollection is that our native watcher’s API was
fundamentally racy, and I didn’t do the messy platform-specific work of making it correct.</p>
<hr>
<p>But the main thing I want to write, and why I moved from the cozy lobste.rs text
area to the luxurious comforts of an Emacs buffer, is that right now rust-analyzer is a bit
like that half-drawn horse meme, except that it’s only the head half of the
horse.</p>
<p>One Big Idea of IntelliJ is that its PSI API (essentially AST with resolved
types) is really an interface, and there are multiple providers. And in a typical
usage, there’re at least three backends in play:</p>
<ul>
<li>
For the files opened in the editor, actively modified by the user, the PSI is
backed by the concrete syntax trees.
</li>
<li>
For the rest of the project files, the PSI is backed by the so called Stub
Tree, a compact on disk representation storing only the “externally visible”
parts of the file (so, without function bodies). If the user navigates to a
new file, its PSI transparently switches from stubs to syntax tree.
</li>
<li>
For dependencies, the PSI is often backed by the compiled .class files,
produced by javac. If you navigate there, the IDE just decompiles stuff for
you! Super cool!
</li>
</ul>
<p><em>This</em> is how I think such things should work. rust analyzer <em>shouldn’t</em> use
salsa for all those 6666 dependencies you still haven’t looked at. It should
just use rustc’s .rmeta files, switching to salsa, transparently, only when the
user starts messing around their <code class="display">~/.cargo/registry/src</code> folder.</p>
<p>The prerequisite for that is defining the abstract API for accessing Rust code.
That was always the plan, and we did start on that at some point:</p>
<p><a href="https://hackmd.io/ytd82QNiT_Ku2XFr1EAtiQ" class="url">https://hackmd.io/ytd82QNiT_Ku2XFr1EAtiQ</a></p>

<figure class="blockquote">
<blockquote><p>rmeta-transparent – source code might not be available for some crates, the API
should support pre-compiled rmeta files as inputs.</p>
</blockquote>

</figure>
<p>But I don’t think that work was ever completed.</p>
<p>This still seems to me to be the lowest-hanging watermelon here — split the
world into arcy-pointy incremental tip of the iceberg, and mostly read-only,
on disk, compact, dark, moist breeding ground for supply chain attacks.</p>
<p>Such glance analyzer architecture would be great, imo!</p>
]]>
    </content>
  </entry>
  <entry>
    <title type="text">Better Batteries</title>
    <link href="https://matklad.github.io/2026/08/20/better-batteries.html"
      rel="alternate" type="text/html" title="Better Batteries" />
    <published>2026-08-20T00:00:00+00:00</published>
    <updated>2026-08-20T00:00:00+00:00</updated>
    <id>https://matklad.github.io/2026/08/20/better-batteries</id>
    <author>
      <name>Alex Kladov</name>
    </author>
    <summary type="html">
      <![CDATA[One of the eternal schisms in programming is over the question of whether the standard library should be minimal or encompassing. This is the wrong question to ask. The right one is:]]>
    </summary>
    <content type="html"
      xml:base="https://matklad.github.io/2026/08/20/better-batteries.html">
      <![CDATA[
<header>
  <h1>Better Batteries</h1>
  <time class="meta" datetime="2026-08-20">Aug 20, 2026</time>
</header>
<p>One of the eternal schisms in programming is over the question of whether the
standard library should be minimal or encompassing. This is the wrong question
to ask. The right one is:</p>

<figure class="blockquote">
<blockquote><p>Which social architecture creates a high-quality standard library?</p>
</blockquote>

</figure>
<p>Python is always brought up as example of leaky batteries exploding in slow
motion, but this has nothing to do with <em>size</em>. The problem with Python’s stdlib
is its, ahem, uneven quality. Some standard library modules don’t follow
language naming conventions! You know which <code>unittest</code> module I am talking about
:-)</p>
<p>But even that is not a mistake. It’s actually Python core’s advantage — that
it makes functionality available early, not thinking about the future too much.
That’s how we ended up with ossified cAPI which makes CPython the language, but
that is <em>also</em> how we ended up with Python powering data scientific revolution.</p>
<p>The Go standard library is similarly encompassing, but it is held in a high
regard. Go team has institutional capacity to deliver well-designed API for the
standard library, and then some: <a href="https://pkg.go.dev/golang.org/x" class="display url">https://pkg.go.dev/golang.org/x</a></p>
<p>Rust is an interesting case. The 1.0 standard library APIs are brilliant.
Collections and iterators are a work of art. But it also feels that, while the
current team has the capacity to preserve existing APIs and fill in <em>some</em> gaps,
the capacity to execute design decisions is limited. While <code>golang.org/x</code>
captures excess capacity, <code>rust-lang-nursery</code> is a graveyard. Maybe I am
over-indexing on
<a href="https://matklad.github.io/2023/01/04/on-random-numbers.html#True-Randomness">my favorite hobby-horse</a>,
but it seems that the reason for Rust not having an API to get a stream of
random bytes from the OS in 2026 is that, while it is an easy technical problem,
it requires tricky organization architecture (including getting money in
peoples’ pockets, of course) to actually get solved in the high-stakes
environment of a world-wide coordination problem called a programming language.</p>
]]>
    </content>
  </entry>
  <entry>
    <title type="text">Printing Lists</title>
    <link href="https://matklad.github.io/2026/08/14/printing-lists.html"
      rel="alternate" type="text/html" title="Printing Lists" />
    <published>2026-08-14T00:00:00+00:00</published>
    <updated>2026-08-14T00:00:00+00:00</updated>
    <id>https://matklad.github.io/2026/08/14/printing-lists</id>
    <author>
      <name>Alex Kladov</name>
    </author>
    <summary type="html">
      <![CDATA[To print a comma-separated list, a concise idiom is to optionally print the comma first, before the element:]]>
    </summary>
    <content type="html"
      xml:base="https://matklad.github.io/2026/08/14/printing-lists.html">
      <![CDATA[
<header>
  <h1>Printing Lists</h1>
  <time class="meta" datetime="2026-08-14">Aug 14, 2026</time>
</header>
<p>To print a comma-separated list, a concise idiom is to optionally print the comma first, before the element:</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-keyword">for</span> (items, <span class="hl-numbers">0</span>..) <span class="hl-operator">|</span>item, item_index<span class="hl-operator">|</span> {</span>
<span class="line">    <span class="hl-keyword">if</span> (item_index &gt; <span class="hl-numbers">0</span>) std.debug.print(<span class="hl-string">&quot;, &quot;</span>);</span>
<span class="line">    std.debug.print(<span class="hl-string">&quot;{}&quot;</span>, .{item});</span>
<span class="line">}</span></code></pre>

</figure>
]]>
    </content>
  </entry>
  <entry>
    <title type="text">Zig&#039;s Io.Threaded is Neat</title>
    <link href="https://matklad.github.io/2026/08/06/neat-io-threaded.html"
      rel="alternate" type="text/html" title="Zig&#039;s Io.Threaded is Neat" />
    <published>2026-08-06T00:00:00+00:00</published>
    <updated>2026-08-06T00:00:00+00:00</updated>
    <id>https://matklad.github.io/2026/08/06/neat-io-threaded</id>
    <author>
      <name>Alex Kladov</name>
    </author>
    <summary type="html">
      <![CDATA[std.Io.Threaded is one of the implementations of Zig's new Io interface that enables concurrency. This is a boring just use threads impl. I personally find it neat though --- it does this weird thing that I wanted to do for ages, that to my knowledge no one else is doing properly, and implements it better than I thought to be possible.]]>
    </summary>
    <content type="html"
      xml:base="https://matklad.github.io/2026/08/06/neat-io-threaded.html">
      <![CDATA[
<header>
  <h1>Zig’s Io.Threaded is Neat</h1>
  <time class="meta" datetime="2026-08-06">Aug 6, 2026</time>
</header>
<p><a href="https://codeberg.org/ziglang/zig/src/commit/16b42da3fd359bf5ae602aa0153e3ec1d6d14822/lib/std/Io/Threaded.zig">std.Io.Threaded</a>
is one of the implementations of Zig’s new <code>Io</code> interface that enables concurrency. This is a boring
“just use threads” impl. I personally find it neat though — it does this weird thing that
<a href="https://github.com/matklad/jthread-rs#jthread">I wanted to do for ages</a>, that to my knowledge no
one else is doing properly, and implements it better than I thought to be possible.</p>
<p><code>Io.Threaded</code> uses blocking syscalls and fully supports cancelation.</p>
<section id="Concurrency-vs-Parallelism">

<h2><a href="#Concurrency-vs-Parallelism">Concurrency vs Parallelism</a></h2>
<p>Quoting <a href="https://www.tedinski.com/2018/10/16/concurrency-vs-parallelism.html">@tedinski</a>,</p>
<ul>
<li>
Concurrency is about handling (asynchronous, nondeterministic) events.
</li>
<li>
Parallelism is about using hardware resources to do more at the same time.
</li>
</ul>
<p>I think this definition is correct, but doesn’t provide useful intuition directly. Concurrency is
the same thing as state transducers? Yes, obviously, but not really illuminating as to how you’d
program the thing.</p>
<p>For intuition, I like these two litmus tests. <em>First</em>, parallelism is deterministic or
“declarative”:</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-keyword">use</span> rayon::prelude::*;</span>
<span class="line"><span class="hl-keyword">fn</span> <span class="hl-title function_">sum_of_squares</span>(input: &amp;[<span class="hl-type">i32</span>]) <span class="hl-punctuation">-&gt;</span> <span class="hl-type">i32</span> {</span>
<span class="line">    input.<span class="hl-title function_ invoke__">par_iter</span>()</span>
<span class="line">         .<span class="hl-title function_ invoke__">map</span>(|i| i * i)</span>
<span class="line">         .<span class="hl-title function_ invoke__">sum</span>()</span>
<span class="line">}</span></code></pre>

</figure>
<p>You describe how to split the problem into independent partitions, and implement a function to
process one partition at a time . It’s platform’s job to verify the partitioning to be correct
(non-racy), process all partitions, and yield control back once that is done.</p>
<p><em>Second</em>, concurrency invariably involves cancelation. Whenever you have two asynchronous
computations happening at the same time, there comes a moment when one computation becomes aware
that the second computation is no longer necessary, and must be canceled, actively. In general, it
is not possible to just wait until the other computation completes: often, the reason why you want
to cancel it in the first place is precisely because you’ve learned that it <em>can’t</em> complete (e.g.,
it is waiting for a message it will never receive).</p>
<p>And that is the problem with</p>
</section>
<section id="Just-Use-Threads">

<h2><a href="#Just-Use-Threads">Just Use Threads</a></h2>
<p>Well, there are more, the chief being that, while you totally <em>can</em> spawn many threads, this often
requires system-wide configuration change, which is a non-starter for most application. But absence
of cancelation really makes you hit a wall sooner or later. The problem are syscalls. It’s easy
enough, in any loopy code, to do something like</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-keyword">while</span> (<span class="hl-literal">true</span>) {</span>
<span class="line">    <span class="hl-keyword">if</span> (is_canceled()) <span class="hl-keyword">return</span> <span class="hl-keyword">error</span>.Canceled; <span class="hl-comment">/// Easy!</span></span>
<span class="line">    ...</span>
<span class="line">}</span></code></pre>

</figure>
<p>But, the thread is instead blocked inside the syscall in the kernel, programming language APIs
generally doesn’t give any way to unblock it:</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-keyword">const</span> read_size = <span class="hl-keyword">try</span> read(fd, buffer); <span class="hl-comment">// ???</span></span></code></pre>

</figure>
<p>Wouldn’t it be cool if we could just use standard OS threads, blocking APIs, avoid new shinies like
io_uring, but still get to cancel any work reliably? That’s exactly what Zig’s <code>std.Io.Threaded</code>
provides.</p>
</section>
<section id="SIGIO">

<h2><a href="#SIGIO">SIGIO</a></h2>
<p>The way this works on POSIX is a bit cursed. Turns out, the kernel actually provides a roundabout
way to cancel a blocking syscall — signals. When a thread is blocked in the kernel, and a signal
is delivered to the thread, the thread is woken up and the syscall returns <code>EINTR</code>. It is customary
to just
<a href="https://github.com/rust-lang/rust/blob/f73951df0a5566d94d13b7954acd9f4ab1fa3734/library/alloc/src/io/copy/generic.rs#L240-L244">loop re-try the syscall</a>
in such cases, but one doesn’t have to.</p>
<p>By itself, signals are not a cancelation mechanism — signaling a thread is inherently racy, the
signal might get delivered before the relevant syscall starts, or after it finishes. Conversely, a
syscall might get interrupted by signal unrelated to cancelation.</p>
<p>The actual protocol is that the canceling thread sets a flag in shared memory to request
cancelation, and then signals the cancelee, in a loop, until the cancelation is acknowledged (a
different value for a flag in the shared memory). Upon receiving <code>EINTR</code> from a syscall, the thread
potentially being canceled checks the value of the flag and either retries the syscall, or
acknowledges the cancelation and begins unwinding. See
<a href="https://codeberg.org/ziglang/zig/src/commit/16b42da3fd359bf5ae602aa0153e3ec1d6d14822/lib/std/Io/Threaded.zig#L1266" class="display"><code>signalCanceledSyscall</code></a>
and, eg
<a href="https://codeberg.org/ziglang/zig/src/commit/16b42da3fd359bf5ae602aa0153e3ec1d6d14822/lib/std/Io/Threaded.zig#L10000-L10014" class="display"><code>fileReadPositionalPosix</code></a>
for the two halves of the protocol.</p>
<p>On the user-side, cancelation request is materialized as <code>error.Canceled</code>. Error management as a
feature is a combination of cancelation,
<a href="https://matklad.github.io/2025/11/06/error-codes-for-control-flow.html">branching, and reporting</a>,
and Zig implements the first two. Cancelation isn’t an error <em>not</em> because it is
<a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1677r2.pdf">serendipitous success</a>, but
because, vice versa, an error is a cancelation plus a payload.</p>
<p>On Windows, there’s a much more direct
<code class="display">NtCancelSynchronousIoFile</code>
Love the name!. In general, between fibers, IO Completion Ports, Job objects, and this, it seems
that NT has a better thought through concurrency story than Unix.</p>
</section>
<section id="Prior-Art">

<h2><a href="#Prior-Art">Prior Art</a></h2>
<p>In Java, there’s a similarly looking thread interruption mechanism. Critically, it doesn’t support
interrupting syscalls: <code>IOException</code> and <code>InterruptedException</code> are both checked and unrelated,
meaning that IOing functions are not interruptible. In Zig, reader and writer interfaces completely
type erase errors and therefore support cancelation, though this requires some extra care to handle
correctly, on top of the usual <a href="https://www.youtube.com/watch?v=f30PceqQWko">don’t forget to flush</a>.</p>
<p><code>pthread_cancel</code> implements a similar signal+flag machinery. However, it doesn’t integrate with
language-level cancelation (<code>try</code>, <code>defer</code>) which makes post-cancelation cleanup cumbersome and
slow. More generally, a lot of angst around concurrency stems from a fact that it falls exactly
into the twilight zone between the kernel, the runtime, and the language. There’s almost (interrupts
excepted) no concurrency on the CPU, it’s an illusion with a mixed authorship. The language is
usually the better equipped one to tackle the problem, but, traditionally, it is handled by the
kernel and libc, with adverse effects on language design.</p>
<p>Another problem with <code>pthread_cancel</code> is that it tears down the entire thread, which would be an OK
thing to do if threads were cheap. However, creating threads is still slow, and the configured
system limit for a number of threads is typically low, so its usually a good idea to pool OS
threads. Zig’s <code>Io</code> solves this problem ingeniously, separating, at the interface level,
“may run concurrently” from “must run concurrently”:</p>
<p><a href="https://kristoff.it/blog/asynchrony-is-not-concurrency/" class="display url">https://kristoff.it/blog/asynchrony-is-not-concurrency/</a></p>
<p>This achieves an effect similar to that of <a href="https://en.cppreference.com/cpp/thread/async#Launch_policies"><code>std::launch</code>
<code>policy</code></a> (item 36 in effective modern
C++, if you have that around). By naming what is happening (<code>io.async</code> vs <code>io.concurrent</code>), Zig
makes it easier to understand what is actually going on, and also gets more precise signatures
(<code>concurrent</code> is always fallible, <code>async</code> never is). Of course <code>concurrent</code> is backed by a thread
pool, falling back on spawning a fresh thread only when the pool is exhausted.</p>
</section>
]]>
    </content>
  </entry>
  <entry>
    <title type="text">Memory Safety&#039;s Hardest Problem</title>
    <link
      href="https://matklad.github.io/2026/07/20/memory-safety-hardest-problem.html"
      rel="alternate" type="text/html"
      title="Memory Safety&#039;s Hardest Problem" />
    <published>2026-07-20T00:00:00+00:00</published>
    <updated>2026-07-20T00:00:00+00:00</updated>
    <id>https://matklad.github.io/2026/07/20/memory-safety-hardest-problem</id>
    <author>
      <name>Alex Kladov</name>
    </author>
    <summary type="html">
      <![CDATA[Uplifting a lobsters comment for easier reference.]]>
    </summary>
    <content type="html"
      xml:base="https://matklad.github.io/2026/07/20/memory-safety-hardest-problem.html">
      <![CDATA[
<header>
  <h1>Memory Safety’s Hardest Problem</h1>
  <time class="meta" datetime="2026-07-20">Jul 20, 2026</time>
</header>
<p>Uplifting a
<a href="https://lobste.rs/s/vzkmtj/forget_borrow_checkers_c3_solved_memory#c_uuhbpy">lobsters comment</a>
for easier reference.</p>
<p>The central memory safety counter example, the hardest case to solve, doesn’t have anything to do
with destructors or heap:</p>

<figure class="code-block">


<pre><code><span class="line"><span class="hl-keyword">const</span> std = <span class="hl-built_in">@import</span>(<span class="hl-string">&quot;std&quot;</span>);</span>
<span class="line"></span>
<span class="line"><span class="hl-keyword">const</span> E = <span class="hl-keyword">union</span>(<span class="hl-keyword">enum</span>) {</span>
<span class="line">    a: <span class="hl-type">u128</span>,</span>
<span class="line">    b: []<span class="hl-keyword">const</span> <span class="hl-type">u8</span>,</span>
<span class="line">};</span>
<span class="line"></span>
<span class="line"><span class="hl-keyword">pub</span> <span class="hl-keyword">fn</span><span class="hl-function"> main</span>() <span class="hl-type">void</span> {</span>
<span class="line">    <span class="hl-keyword">const</span> bad_addr: <span class="hl-type">u128</span> = <span class="hl-built_in">@intFromPtr</span>(<span class="hl-operator">&amp;</span>main);</span>
<span class="line"></span>
<span class="line">    <span class="hl-keyword">var</span> e: E = .{ .b = <span class="hl-string">&quot;hello&quot;</span> };</span>
<span class="line">    <span class="hl-keyword">const</span> oh_no_pointer: <span class="hl-operator">*</span><span class="hl-keyword">const</span> []<span class="hl-keyword">const</span> <span class="hl-type">u8</span> = <span class="hl-keyword">switch</span> (e) {</span>
<span class="line">        .a =&gt; <span class="hl-keyword">unreachable</span>,</span>
<span class="line">        .b =&gt; <span class="hl-operator">|</span><span class="hl-operator">*</span>p<span class="hl-operator">|</span> p,</span>
<span class="line">    };</span>
<span class="line">    e = .{ .a = (<span class="hl-numbers">16</span> <span class="hl-operator">&lt;&lt;</span> <span class="hl-numbers">64</span>) <span class="hl-operator">+</span> bad_addr };</span>
<span class="line">    <span class="hl-keyword">const</span> oh_no: []<span class="hl-keyword">const</span> <span class="hl-type">u8</span> = oh_no_pointer.<span class="hl-operator">*</span>;</span>
<span class="line">    std.debug.print(<span class="hl-string">&quot;{s}<span class="hl-string">\n</span>&quot;</span>, .{oh_no});</span>
<span class="line">}</span></code></pre>

</figure>

<figure class="code-block">


<pre><code><span class="line">$ zig run main.zig</span>
<span class="line">��C�� �</span></code></pre>

</figure>
<p>This sort of example also breaks Ada:</p>
<p><a href="https://www.enyo.de/fw/notes/ada-type-safety.html" class="url">https://www.enyo.de/fw/notes/ada-type-safety.html</a></p>
<p>We have a tagged union, which can hold either <code>A</code> or <code>B</code>. We initialize the union as
<code>A</code>, take a pointer to its internals, overwrite the original with <code>B</code>, and then use the pointer. The
pointer is still typed as <code>A</code>, but the bytes it points to now belong to <code>B</code>: a type confusion.</p>
<hr>
<p>This being said, we care about memory unsafety primarily because it leads to exploitable software,
and it’s unclear just how impactful the example above is in practice. It is a happy coincidence that
by far the most exploitable memory error in practice, the infamous buffer overflow, is also trivial
to fix with compiler-inserted bounds checks. The biggest miss of the industry when it comes to
memory safety is not listening to Walter Bright:</p>
<p><a href="https://digitalmars.com/articles/C-biggest-mistake.html" class="url">https://digitalmars.com/articles/C-biggest-mistake.html</a></p>
<p>I bet that, had we got <code>char a[..]</code> syntax around C11, quite a few issues wouldn’t have happened!</p>
<p>See also <a href="https://matklad.github.io/2025/12/30/memory-safety-is.html"><em>What is Memory Safety?</em></a></p>
]]>
    </content>
  </entry>
</feed>
