<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://llogiq.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://llogiq.github.io/" rel="alternate" type="text/html" /><updated>2026-03-20T20:46:55+00:00</updated><id>https://llogiq.github.io/feed.xml</id><title type="html">Llogiq on stuff</title><subtitle>My github page</subtitle><author><name>Llogiq</name></author><entry><title type="html">Matching Puzzle Pieces and Disappointing Benchmarks</title><link href="https://llogiq.github.io/2026/03/20/case.html" rel="alternate" type="text/html" title="Matching Puzzle Pieces and Disappointing Benchmarks" /><published>2026-03-20T00:00:00+00:00</published><updated>2026-03-20T00:00:00+00:00</updated><id>https://llogiq.github.io/2026/03/20/case</id><content type="html" xml:base="https://llogiq.github.io/2026/03/20/case.html"><![CDATA[<p>I recently had a piece of code that used <code class="language-plaintext highlighter-rouge">.to_lowercase()</code> to sort some text. Which takes a bit of memory. On the plus side, the code used <code class="language-plaintext highlighter-rouge">.sort_by_cached_key</code>, which is pretty cool. But I wondered whether doing the <code class="language-plaintext highlighter-rouge">.to_lowercase()</code> log(n) times instead of n times would be slower than allocating a <code class="language-plaintext highlighter-rouge">String</code> for each entry, given that for many strings, even the first few charactes would be different.</p>

<p>First, case insensitively comparing two <code class="language-plaintext highlighter-rouge">&amp;str</code>s in Rust is possible, if a bit convoluted. The solution here is to iterate over all <code class="language-plaintext highlighter-rouge">char</code>s, then calling <code class="language-plaintext highlighter-rouge">char::to_lowercase</code> on that, which returns another iterator over <code class="language-plaintext highlighter-rouge">char</code> (because some chars can correspond to multiple chars in lowercase), which we can <code class="language-plaintext highlighter-rouge">flat_map</code>. The second piece of the puzzle is that <code class="language-plaintext highlighter-rouge">Iterator</code> has a <code class="language-plaintext highlighter-rouge">cmp</code> method but does not implement <code class="language-plaintext highlighter-rouge">Ord</code> because it is not idempotent: If you call <code class="language-plaintext highlighter-rouge">cmp</code>, you exhaust the iterator. Still, with <code class="language-plaintext highlighter-rouge">sort_by</code>, we can interleave the lowercase conversion and comparison.</p>

<p>For good measure, I also added the <code class="language-plaintext highlighter-rouge">unicase</code> crate to the benchmarks.</p>

<p>Being the curious person that I am, I naturally wrote a benchmark, which is short enough to reproduce here (if you aren’t interested, scroll down for the conclusion):</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">fake</span><span class="p">::</span><span class="nn">faker</span><span class="p">::</span><span class="nn">name</span><span class="p">::</span><span class="nn">raw</span><span class="p">::</span><span class="n">Name</span><span class="p">;</span>
<span class="k">use</span> <span class="nn">fake</span><span class="p">::{</span><span class="nn">locales</span><span class="p">::</span><span class="n">EN</span><span class="p">,</span> <span class="n">Fake</span><span class="p">};</span>

<span class="k">fn</span> <span class="nf">setup</span><span class="p">(</span><span class="n">num</span><span class="p">:</span> <span class="nb">usize</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="p">(</span><span class="mi">0</span><span class="o">..</span><span class="n">num</span><span class="p">)</span><span class="nf">.map</span><span class="p">(|</span><span class="n">_</span><span class="p">|</span> <span class="nf">Name</span><span class="p">(</span><span class="n">EN</span><span class="p">)</span><span class="nf">.fake</span><span class="p">())</span><span class="py">.collect</span><span class="p">::</span><span class="o">&lt;</span><span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;&gt;</span><span class="p">()</span>
<span class="p">}</span>

<span class="nd">#[divan::bench(args</span> <span class="nd">=</span> <span class="err">[</span><span class="mi">1</span><span class="nd">,</span> <span class="mi">5</span><span class="nd">,</span> <span class="mi">10</span><span class="nd">,</span> <span class="mi">100</span><span class="nd">,</span> <span class="mi">1000</span><span class="nd">,</span> <span class="mi">10000</span><span class="nd">]</span><span class="p">)]</span>
<span class="k">fn</span> <span class="nf">sort_by_cached_lowercase</span><span class="p">(</span><span class="n">bencher</span><span class="p">:</span> <span class="nn">divan</span><span class="p">::</span><span class="n">Bencher</span><span class="p">,</span> <span class="n">size</span><span class="p">:</span> <span class="nb">usize</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">names</span> <span class="o">=</span> <span class="nf">setup</span><span class="p">(</span><span class="n">size</span><span class="p">);</span>
    <span class="n">bencher</span><span class="nf">.counter</span><span class="p">(</span><span class="n">size</span><span class="p">)</span><span class="nf">.bench_local</span><span class="p">(||</span> <span class="p">{</span>
        <span class="k">let</span> <span class="k">mut</span> <span class="n">sorted</span> <span class="o">=</span> <span class="n">names</span><span class="nf">.clone</span><span class="p">();</span>
        <span class="n">sorted</span><span class="nf">.sort_by_cached_key</span><span class="p">(|</span><span class="n">name</span><span class="p">|</span> <span class="n">name</span><span class="nf">.to_lowercase</span><span class="p">());</span>
        <span class="n">sorted</span>
    <span class="p">})</span>
<span class="p">}</span>

<span class="nd">#[divan::bench(args</span> <span class="nd">=</span> <span class="err">[</span><span class="mi">1</span><span class="nd">,</span> <span class="mi">5</span><span class="nd">,</span> <span class="mi">10</span><span class="nd">,</span> <span class="mi">100</span><span class="nd">,</span> <span class="mi">1000</span><span class="nd">,</span> <span class="mi">10000</span><span class="nd">]</span><span class="p">)]</span>
<span class="k">fn</span> <span class="nf">sort_by_iter_lowercase</span><span class="p">(</span><span class="n">bencher</span><span class="p">:</span> <span class="nn">divan</span><span class="p">::</span><span class="n">Bencher</span><span class="p">,</span> <span class="n">size</span><span class="p">:</span> <span class="nb">usize</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">names</span> <span class="o">=</span> <span class="nf">setup</span><span class="p">(</span><span class="n">size</span><span class="p">);</span>
    <span class="n">bencher</span><span class="nf">.counter</span><span class="p">(</span><span class="n">size</span><span class="p">)</span><span class="nf">.bench_local</span><span class="p">(||</span> <span class="p">{</span>
        <span class="k">let</span> <span class="k">mut</span> <span class="n">sorted</span> <span class="o">=</span> <span class="n">names</span><span class="nf">.clone</span><span class="p">();</span>
        <span class="k">fn</span> <span class="nf">caseless</span><span class="p">(</span><span class="n">s</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">String</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">impl</span> <span class="nb">Iterator</span><span class="o">&lt;</span><span class="n">Item</span> <span class="o">=</span> <span class="nb">char</span><span class="o">&gt;</span> <span class="o">+</span> <span class="nv">'_</span> <span class="p">{</span>
            <span class="n">s</span><span class="nf">.chars</span><span class="p">()</span><span class="nf">.flat_map</span><span class="p">(</span><span class="nn">char</span><span class="p">::</span><span class="n">to_lowercase</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="n">sorted</span><span class="nf">.sort_by</span><span class="p">(|</span><span class="n">s1</span><span class="p">,</span> <span class="n">s2</span><span class="p">|</span> <span class="nf">caseless</span><span class="p">(</span><span class="n">s1</span><span class="p">)</span><span class="nf">.cmp</span><span class="p">(</span><span class="nf">caseless</span><span class="p">(</span><span class="n">s2</span><span class="p">)));</span>
        <span class="n">sorted</span>
    <span class="p">})</span>
<span class="p">}</span>

<span class="nd">#[divan::bench(args</span> <span class="nd">=</span> <span class="err">[</span><span class="mi">1</span><span class="nd">,</span> <span class="mi">5</span><span class="nd">,</span> <span class="mi">10</span><span class="nd">,</span> <span class="mi">100</span><span class="nd">,</span> <span class="mi">1000</span><span class="nd">,</span> <span class="mi">10000</span><span class="nd">]</span><span class="p">)]</span>
<span class="k">fn</span> <span class="nf">sort_by_unicase</span><span class="p">(</span><span class="n">bencher</span><span class="p">:</span> <span class="nn">divan</span><span class="p">::</span><span class="n">Bencher</span><span class="p">,</span> <span class="n">size</span><span class="p">:</span> <span class="nb">usize</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">names</span> <span class="o">=</span> <span class="nf">setup</span><span class="p">(</span><span class="n">size</span><span class="p">);</span>
    <span class="n">bencher</span><span class="nf">.counter</span><span class="p">(</span><span class="n">size</span><span class="p">)</span><span class="nf">.bench_local</span><span class="p">(||</span> <span class="p">{</span>
        <span class="k">let</span> <span class="k">mut</span> <span class="n">sorted</span> <span class="o">=</span> <span class="n">names</span><span class="nf">.clone</span><span class="p">();</span>
        <span class="n">sorted</span><span class="nf">.sort_by</span><span class="p">(|</span><span class="n">s1</span><span class="p">,</span> <span class="n">s2</span><span class="p">|</span> <span class="nn">unicase</span><span class="p">::</span><span class="nn">UniCase</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">s1</span><span class="p">)</span><span class="nf">.cmp</span><span class="p">(</span><span class="o">&amp;</span><span class="nn">unicase</span><span class="p">::</span><span class="nn">UniCase</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">s2</span><span class="p">)));</span>
        <span class="n">sorted</span>
    <span class="p">})</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// Run registered benchmarks.</span>
    <span class="nn">divan</span><span class="p">::</span><span class="nf">main</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The result on my M2-MAX MacBook Pro:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Timer precision: 41 ns
low                          fastest       │ slowest       │ median        │ mean          │ samples │ iters
├─ sort_by_cached_lowercase                │               │               │               │         │
│  ├─ 1                      16.68 ns      │ 18.14 ns      │ 17.49 ns      │ 17.49 ns      │ 100     │ 25600
│  │                         59.94 Mitem/s │ 55.1 Mitem/s  │ 57.15 Mitem/s │ 57.15 Mitem/s │         │
│  ├─ 5                      212.9 ns      │ 265 ns        │ 215.5 ns      │ 219.2 ns      │ 100     │ 3200
│  │                         23.47 Mitem/s │ 18.86 Mitem/s │ 23.19 Mitem/s │ 22.8 Mitem/s  │         │
│  ├─ 10                     452.5 ns      │ 567.1 ns      │ 457.7 ns      │ 462.2 ns      │ 100     │ 1600
│  │                         22.09 Mitem/s │ 17.63 Mitem/s │ 21.84 Mitem/s │ 21.63 Mitem/s │         │
│  ├─ 100                    5.207 µs      │ 11.33 µs      │ 5.291 µs      │ 5.455 µs      │ 100     │ 100
│  │                         19.2 Mitem/s  │ 8.824 Mitem/s │ 18.89 Mitem/s │ 18.32 Mitem/s │         │
│  ├─ 1000                   73.62 µs      │ 110.9 µs      │ 75.99 µs      │ 78.89 µs      │ 100     │ 100
│  │                         13.58 Mitem/s │ 9.009 Mitem/s │ 13.15 Mitem/s │ 12.67 Mitem/s │         │
│  ╰─ 10000                  853.7 µs      │ 1.053 ms      │ 864.8 µs      │ 886.3 µs      │ 100     │ 100
│                            11.71 Mitem/s │ 9.495 Mitem/s │ 11.56 Mitem/s │ 11.28 Mitem/s │         │
├─ sort_by_iter_lowercase                  │               │               │               │         │
│  ├─ 1                      13.91 ns      │ 23.35 ns      │ 14.89 ns      │ 15.68 ns      │ 100     │ 25600
│  │                         71.87 Mitem/s │ 42.81 Mitem/s │ 67.15 Mitem/s │ 63.76 Mitem/s │         │
│  ├─ 5                      134.8 ns      │ 196 ns        │ 137.4 ns      │ 148.6 ns      │ 100     │ 3200
│  │                         37.08 Mitem/s │ 25.5 Mitem/s  │ 36.37 Mitem/s │ 33.64 Mitem/s │         │
│  ├─ 10                     442.1 ns      │ 1.03 µs       │ 483.8 ns      │ 519.1 ns      │ 100     │ 800
│  │                         22.61 Mitem/s │ 9.702 Mitem/s │ 20.66 Mitem/s │ 19.26 Mitem/s │         │
│  ├─ 100                    17.33 µs      │ 34.12 µs      │ 18.16 µs      │ 19.66 µs      │ 100     │ 100
│  │                         5.769 Mitem/s │ 2.93 Mitem/s  │ 5.504 Mitem/s │ 5.086 Mitem/s │         │
│  ├─ 1000                   337.6 µs      │ 433 µs        │ 352.1 µs      │ 355.4 µs      │ 100     │ 100
│  │                         2.961 Mitem/s │ 2.309 Mitem/s │ 2.839 Mitem/s │ 2.813 Mitem/s │         │
│  ╰─ 10000                  5.543 ms      │ 6.579 ms      │ 5.572 ms      │ 5.602 ms      │ 100     │ 100
│                            1.803 Mitem/s │ 1.519 Mitem/s │ 1.794 Mitem/s │ 1.784 Mitem/s │         │
╰─ sort_by_unicase                         │               │               │               │         │
   ├─ 1                      15.05 ns      │ 22.05 ns      │ 15.87 ns      │ 16.78 ns      │ 100     │ 25600
   │                         66.42 Mitem/s │ 45.34 Mitem/s │ 63 Mitem/s    │ 59.59 Mitem/s │         │
   ├─ 5                      86.66 ns      │ 125 ns        │ 91.86 ns      │ 97.79 ns      │ 100     │ 6400
   │                         57.69 Mitem/s │ 39.97 Mitem/s │ 54.42 Mitem/s │ 51.12 Mitem/s │         │
   ├─ 10                     202.5 ns      │ 470.8 ns      │ 207.7 ns      │ 230.9 ns      │ 100     │ 1600
   │                         49.36 Mitem/s │ 21.24 Mitem/s │ 48.13 Mitem/s │ 43.3 Mitem/s  │         │
   ├─ 100                    4.749 µs      │ 18.33 µs      │ 4.833 µs      │ 5.201 µs      │ 100     │ 100
   │                         21.05 Mitem/s │ 5.454 Mitem/s │ 20.68 Mitem/s │ 19.22 Mitem/s │         │
   ├─ 1000                   107.2 µs      │ 158 µs        │ 107.5 µs      │ 111.4 µs      │ 100     │ 100
   │                         9.327 Mitem/s │ 6.325 Mitem/s │ 9.298 Mitem/s │ 8.973 Mitem/s │         │
   ╰─ 10000                  1.753 ms      │ 1.919 ms      │ 1.757 ms      │ 1.772 ms      │ 100     │ 100
                             5.702 Mitem/s │ 5.208 Mitem/s │ 5.688 Mitem/s │ 5.641 Mitem/s │         │
</code></pre></div></div>

<p>So unless you only have one element (which is the trivial case), <code class="language-plaintext highlighter-rouge">sort_by_cached_key</code> is worth it, and iterating over UTF-8 characters to do case conversion is a lot slower than I would have thought, drowning out the benefit of not needing to allocate. The real surprise is that Unicase can often be faster, despite making the comparison more complex.</p>]]></content><author><name>Llogiq</name></author><summary type="html"><![CDATA[I recently had a piece of code that used .to_lowercase() to sort some text. Which takes a bit of memory. On the plus side, the code used .sort_by_cached_key, which is pretty cool. But I wondered whether doing the .to_lowercase() log(n) times instead of n times would be slower than allocating a String for each entry, given that for many strings, even the first few charactes would be different.]]></summary></entry><entry><title type="html">Write small Rust scripts</title><link href="https://llogiq.github.io/2026/03/05/auto.html" rel="alternate" type="text/html" title="Write small Rust scripts" /><published>2026-03-05T00:00:00+00:00</published><updated>2026-03-05T00:00:00+00:00</updated><id>https://llogiq.github.io/2026/03/05/auto</id><content type="html" xml:base="https://llogiq.github.io/2026/03/05/auto.html"><![CDATA[<p>Recently I was working on a <a href="https://github.com/rust-lang/rust/pull/149543">Rust PR to reduce <code class="language-plaintext highlighter-rouge">unreachable_code</code> lint churn after <code class="language-plaintext highlighter-rouge">todo!()</code> calls</a>, that basically removes lint messages from <code class="language-plaintext highlighter-rouge">unreachable_code</code> after <code class="language-plaintext highlighter-rouge">todo!()</code> and instead adds a <code class="language-plaintext highlighter-rouge">todo_macro_uses</code> lint which can be turned off while the code is still being worked on. However, once that change was done, I ran into a number of failing tests, because while they had a <code class="language-plaintext highlighter-rouge">#![allow(unused)]</code> or some such, this didn’t cover the <code class="language-plaintext highlighter-rouge">todo_macro_uses</code> lint.</p>

<p>Brief digression: rustc itself is tested by a tool called compiletest. That tool runs the compiler on code snippets, captures the output and compares it with known-good golden master output it stores alongside the snippets. In this case, there were a good number of tests that had <code class="language-plaintext highlighter-rouge">todo!()</code> but didn’t <code class="language-plaintext highlighter-rouge">#![allow(todo_macro_uses)]</code>. More tests than I’d care to change manually.</p>

<p>In this year of the lord, many of us would ask some agent to do it for them, but I didn’t like the fact that I would have to review the output (I have seen too many needless formatting changes to be comfortable with investing time and tokens into that). Also I had a code snippet to find all rust files lying around that only used standard library functions and could easily be pasted into a throwaway project.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="n">io</span><span class="p">;</span>
<span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">path</span><span class="p">::</span><span class="n">Path</span><span class="p">;</span>

<span class="k">fn</span> <span class="nf">check_files</span><span class="p">(</span><span class="n">path</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Path</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">io</span><span class="p">::</span><span class="nb">Result</span><span class="o">&lt;</span><span class="p">()</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">for</span> <span class="n">e</span> <span class="k">in</span> <span class="nn">std</span><span class="p">::</span><span class="nn">fs</span><span class="p">::</span><span class="nf">read_dir</span><span class="p">(</span><span class="n">path</span><span class="p">)</span><span class="o">?</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nf">Ok</span><span class="p">(</span><span class="n">d</span><span class="p">)</span> <span class="o">=</span> <span class="n">e</span> <span class="k">else</span> <span class="p">{</span> <span class="k">continue</span><span class="p">;</span> <span class="p">};</span>
        <span class="k">if</span> <span class="n">d</span><span class="nf">.file_type</span><span class="p">()</span><span class="nf">.is_ok_and</span><span class="p">(|</span><span class="n">ft</span><span class="p">|</span> <span class="n">ft</span><span class="nf">.is_dir</span><span class="p">())</span> <span class="p">{</span>
            <span class="nf">check_files</span><span class="p">(</span><span class="o">&amp;</span><span class="n">d</span><span class="nf">.path</span><span class="p">())</span><span class="o">?</span><span class="p">;</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">let</span> <span class="n">path</span> <span class="o">=</span> <span class="n">d</span><span class="nf">.path</span><span class="p">();</span>
            <span class="k">if</span> <span class="n">path</span><span class="nf">.extension</span><span class="p">()</span><span class="nf">.is_some_and</span><span class="p">(|</span><span class="n">ext</span><span class="p">|</span> <span class="n">ext</span> <span class="o">==</span> <span class="s">"rs"</span><span class="p">)</span> <span class="p">{</span>
                <span class="nf">check_file</span><span class="p">(</span><span class="o">&amp;</span><span class="n">path</span><span class="p">)</span><span class="o">?</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="nf">Ok</span><span class="p">(())</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This can be called on a <code class="language-plaintext highlighter-rouge">Path</code> and walks it recursively, calling <code class="language-plaintext highlighter-rouge">check_file</code> on all Rust files. I also had done a few read-modify-write functions in Rust (notably in my <a href="https://github.com/llogiq/twirer">twirer</a> tool I use for my weekly <a href="https://this-week-in-rust.org">This Week in Rust</a> contributions). They look like this:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">check_file</span><span class="p">(</span><span class="n">path</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Path</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">io</span><span class="p">::</span><span class="nb">Result</span><span class="o">&lt;</span><span class="p">()</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">orig_text</span> <span class="o">=</span> <span class="nn">std</span><span class="p">::</span><span class="nn">fs</span><span class="p">::</span><span class="nf">read_to_string</span><span class="p">(</span><span class="n">path</span><span class="p">)</span><span class="o">?</span><span class="p">;</span>

    <span class="k">let</span> <span class="n">text</span> <span class="o">=</span> <span class="nd">todo!</span><span class="p">();</span> <span class="c1">// put the changed `orig_text` into `text`</span>

    <span class="nn">std</span><span class="p">::</span><span class="nn">fs</span><span class="p">::</span><span class="nf">write</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">text</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>There was some slight complication in that a) I wanted to amend any <code class="language-plaintext highlighter-rouge">#![allow(..)]</code> annotation I would find instead of adding another, and b) to add one, I would have to find the first position after the initial comments (which are interpreted by compiletest, which would be foiled by putting them below a non-comment line). Also I didn’t want to needlessly add empty lines, so I had to check whether to insert a newline. All in all this came out to less than 50 lines of Rust code, which I’m reproducing here; perhaps someone can use them to copy into their own code to have their own one-off Rust scripts.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">fs</span><span class="p">::{</span><span class="n">read_dir</span><span class="p">,</span> <span class="n">read_to_string</span><span class="p">,</span> <span class="n">write</span><span class="p">};</span>
<span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="n">io</span><span class="p">;</span>
<span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">path</span><span class="p">::</span><span class="n">Path</span><span class="p">;</span>

<span class="k">fn</span> <span class="nf">check_file</span><span class="p">(</span><span class="n">path</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Path</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">io</span><span class="p">::</span><span class="nb">Result</span><span class="o">&lt;</span><span class="p">()</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">orig_text</span> <span class="o">=</span> <span class="nf">read_to_string</span><span class="p">(</span><span class="n">path</span><span class="p">)</span><span class="o">?</span><span class="p">;</span>
    <span class="k">if</span> <span class="o">!</span><span class="n">orig_text</span><span class="nf">.contains</span><span class="p">(</span><span class="s">"todo!("</span><span class="p">)</span> <span class="p">||</span> <span class="n">orig_text</span><span class="nf">.contains</span><span class="p">(</span><span class="s">"todo_macro_uses"</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="nf">Ok</span><span class="p">(());</span>
    <span class="p">}</span>
    <span class="k">let</span> <span class="n">text</span> <span class="o">=</span> <span class="k">if</span> <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="n">pos</span><span class="p">)</span> <span class="o">=</span> <span class="n">orig_text</span><span class="nf">.find</span><span class="p">(</span><span class="s">"#![allow("</span><span class="p">)</span> <span class="p">{</span>
       <span class="c1">// we have an `#[allow(..)]` we can extend</span>
       <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="n">insert_pos</span><span class="p">)</span> <span class="o">=</span> <span class="n">orig_text</span><span class="p">[</span><span class="n">pos</span><span class="o">..</span><span class="p">]</span><span class="nf">.find</span><span class="p">(</span><span class="s">")]"</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span>
           <span class="nd">panic!</span><span class="p">(</span><span class="s">"unclosed #![allow()]"</span><span class="p">);</span>
       <span class="p">};</span>
       <span class="k">let</span> <span class="p">(</span><span class="n">before</span><span class="p">,</span> <span class="n">after</span><span class="p">)</span> <span class="o">=</span> <span class="n">orig_text</span><span class="nf">.split_at</span><span class="p">(</span><span class="n">pos</span> <span class="o">+</span> <span class="n">insert_pos</span><span class="p">);</span>
       <span class="nd">format!</span><span class="p">(</span><span class="s">"{before}, todo_macro_uses{after}"</span><span class="p">)</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="c1">// find the first line after all // comments</span>
        <span class="k">let</span> <span class="k">mut</span> <span class="n">pos</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="k">while</span> <span class="n">orig_text</span><span class="p">[</span><span class="n">pos</span><span class="o">..</span><span class="p">]</span><span class="nf">.starts_with</span><span class="p">(</span><span class="s">"//"</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="n">nl</span><span class="p">)</span> <span class="o">=</span> <span class="n">orig_text</span><span class="p">[</span><span class="n">pos</span><span class="o">..</span><span class="p">]</span><span class="nf">.find</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span>
                <span class="n">pos</span> <span class="o">=</span> <span class="n">orig_text</span><span class="nf">.len</span><span class="p">();</span>
                <span class="k">break</span><span class="p">;</span>
            <span class="p">};</span>
            <span class="n">pos</span> <span class="o">+=</span> <span class="n">nl</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">let</span> <span class="p">(</span><span class="n">before</span><span class="p">,</span> <span class="n">after</span><span class="p">)</span> <span class="o">=</span> <span class="n">orig_text</span><span class="nf">.split_at</span><span class="p">(</span><span class="n">pos</span><span class="p">);</span>
        <span class="c1">// insert a newline unless at beginning or we already have one</span>
        <span class="k">let</span> <span class="n">nl</span> <span class="o">=</span> <span class="k">if</span> <span class="n">pos</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">||</span> <span class="n">before</span><span class="nf">.ends_with</span><span class="p">(</span><span class="sc">'\n'</span><span class="p">)</span> <span class="p">{</span>
            <span class="s">""</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="s">"</span><span class="se">\n</span><span class="s">"</span>
        <span class="p">};</span>
        <span class="nd">format!</span><span class="p">(</span><span class="s">"{before}{nl}#![allow(todo_macro_uses)]</span><span class="se">\n</span><span class="s">{after}"</span><span class="p">)</span>
    <span class="p">};</span>
    <span class="nf">write</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">text</span><span class="p">)</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">check_files</span><span class="p">(</span><span class="n">path</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Path</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">io</span><span class="p">::</span><span class="nb">Result</span><span class="o">&lt;</span><span class="p">()</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">for</span> <span class="n">e</span> <span class="k">in</span> <span class="nf">read_dir</span><span class="p">(</span><span class="n">path</span><span class="p">)</span><span class="o">?</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nf">Ok</span><span class="p">(</span><span class="n">d</span><span class="p">)</span> <span class="o">=</span> <span class="n">e</span> <span class="k">else</span> <span class="p">{</span> <span class="k">continue</span><span class="p">;</span> <span class="p">};</span>
        <span class="k">if</span> <span class="n">d</span><span class="nf">.file_type</span><span class="p">()</span><span class="nf">.is_ok_and</span><span class="p">(|</span><span class="n">ft</span><span class="p">|</span> <span class="n">ft</span><span class="nf">.is_dir</span><span class="p">())</span> <span class="p">{</span>
            <span class="nf">check_files</span><span class="p">(</span><span class="o">&amp;</span><span class="n">d</span><span class="nf">.path</span><span class="p">())</span><span class="o">?</span><span class="p">;</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">let</span> <span class="n">path</span> <span class="o">=</span> <span class="n">d</span><span class="nf">.path</span><span class="p">();</span>
            <span class="k">if</span> <span class="n">path</span><span class="nf">.extension</span><span class="p">()</span><span class="nf">.is_some_and</span><span class="p">(|</span><span class="n">ext</span><span class="p">|</span> <span class="n">ext</span> <span class="o">==</span> <span class="s">"rs"</span><span class="p">)</span> <span class="p">{</span>
                <span class="nf">check_file</span><span class="p">(</span><span class="o">&amp;</span><span class="n">path</span><span class="p">)</span><span class="o">?</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="nf">Ok</span><span class="p">(())</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="nn">io</span><span class="p">::</span><span class="nb">Result</span><span class="o">&lt;</span><span class="p">()</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="nf">check_files</span><span class="p">(</span><span class="o">&amp;</span><span class="nn">Path</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="s">"../rust/tests/ui"</span><span class="p">))</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The script ran flawlessly, I didn’t need to check the output for errors, and I can reuse parts of it whenever I feel like it.</p>

<p>Conclusion: It’s easy and quick to write small Rust scripts to transform code. And since you know what the code does, you don’t need any time to review the output. And Rust’s standard library, while missing pieces that might simplify some tasks, is certainly servicable for work like this. Even if I had the need for, say, regexes, those would’ve been a mere <code class="language-plaintext highlighter-rouge">cargo add regex</code> away. So next time you need to mechanically transform some code, don’t reach for AI, simply rust it.</p>]]></content><author><name>Llogiq</name></author><summary type="html"><![CDATA[Recently I was working on a Rust PR to reduce unreachable_code lint churn after todo!() calls, that basically removes lint messages from unreachable_code after todo!() and instead adds a todo_macro_uses lint which can be turned off while the code is still being worked on. However, once that change was done, I ran into a number of failing tests, because while they had a #![allow(unused)] or some such, this didn’t cover the todo_macro_uses lint.]]></summary></entry><entry><title type="html">Rust A Decade Later</title><link href="https://llogiq.github.io/2025/05/18/ten.html" rel="alternate" type="text/html" title="Rust A Decade Later" /><published>2025-05-18T00:00:00+00:00</published><updated>2025-05-18T00:00:00+00:00</updated><id>https://llogiq.github.io/2025/05/18/ten</id><content type="html" xml:base="https://llogiq.github.io/2025/05/18/ten.html"><![CDATA[<p>Hi folks, I’m back from RustWeek, and the ten-year celebration also almost
marks ten years of me coding in Rust. Cue the (self-)congratulation and the
feel-good things. This is hopefully one of those.</p>

<p>I want to raise one single point that most of the other posts seem to be
missing: We’re not done yet. Yes, we can have nice things (and comparing with
other languages, we very much <em>do</em> have nice things). But we shouldn’t content
ourselves with what we have.</p>

<p>We can have even nicer things.</p>

<p>What’s more, we should have even nicer things. I’ll give a few examples:</p>

<ul>
  <li>Rust’s error reporting is famously great. But at least for now, paths in
error and lint messages are usually fully qualified, because the compiler fails
to keep the information about what paths are in scope when creating the
diagnostics. I’ve teamed up with Esteban Kuber and Vadim Petrochenkov so that
we may get <em>path trimming</em> in those messages.</li>
  <li>The Rust compiler has more span information than most compilers do. But a) we
don’t always get the full info on macro expansion (notably the Rust for Linux
folks found an example where <code class="language-plaintext highlighter-rouge">macro_rules! m { ($e:$tt) =&gt; { $e }; } m!(1)</code>
fails to mark the span of the <code class="language-plaintext highlighter-rouge">1</code> as involved in a macro). Also unlike C, we
currently don’t have an annotation to declare that code has been generated from
non-Rust code, which would improve help programs that compile to Rust, such
as bindgen. We don’t yet have anyone taking up this thing, but here’s hope
we’ll get it anyway.</li>
  <li>The clippy lint implementer’s workshop led to multiple PRs to make clippy
better. I have yet to review some of them, but the results so far are
heartening. In the meantime, the clippy performance project has already given
us some initial benefits, but there’s a lot of work to be done still.</li>
  <li>The cargo team will add their own linting infrastructure and take over the
few cargo lints clippy currently has, which will likely improve their
performance because they will be able to hook into cargo internals for which
we currently need to call out to cargo.</li>
  <li>The current story around mutable statics is suboptimal, with the replacement
API being nightly-only, while the original idiom is already a lint error. I’m
positive we’ll see something better come out of it.</li>
</ul>

<p>And that’s only a small portion of the herculean amount of work that continues
to be poured into Rust.</p>

<p>So here’s to the next 10 years of Rust improvement. It’s already become better
than most of us would have dared to dream, and we should expect to continue to
raise the bar even further.</p>]]></content><author><name>Llogiq</name></author><summary type="html"><![CDATA[Hi folks, I’m back from RustWeek, and the ten-year celebration also almost marks ten years of me coding in Rust. Cue the (self-)congratulation and the feel-good things. This is hopefully one of those.]]></summary></entry><entry><title type="html">Rust Life Improvement</title><link href="https://llogiq.github.io/2025/05/15/life.html" rel="alternate" type="text/html" title="Rust Life Improvement" /><published>2025-05-15T00:00:00+00:00</published><updated>2025-05-15T00:00:00+00:00</updated><id>https://llogiq.github.io/2025/05/15/life</id><content type="html" xml:base="https://llogiq.github.io/2025/05/15/life.html"><![CDATA[<p>This is the companion blog post to my eponymous <a href="https://rustikon.dev">Rustikon</a>
talk. The <a href="https://youtu.be/fG4s_b0aG00">video recording</a> and
<a href="/talks/life.html">slides</a> are also available now.</p>

<p>As is my tradition, I started with a musical number, this time replacing the
lyrics to Deep Blue Something’s “Breakfast at Tiffany”, inspired by some recent
discussions I got into:</p>

<p>You say that Rust is like a religion<br />
the community is toxic<br />
and you rather stay apart.<br />
You say that C can be coded safely<br />
that it is just a skill issue<br />
still I know you just don’t care.</p>

<p>R: And I say “what about mem’ry unsafety?”<br />
You say “I think I read something about it<br />
and I recall I think that hackers quite like it”<br />
And I say “well that’s one thing you got!”</p>

<p>In C you are tasked with managing mem’ry<br />
no help from the compiler<br />
there’s so much that can go wrong?<br />
So what now? The hackers are all over<br />
your systems, running over<br />
with CVEs galore.</p>

<p>R: And I say “what about…”</p>

<p>You say that Rust is a woke mind virus,<br />
rustaceans are all queer furries<br />
and you rather stay apart.<br />
You say that C can be coded safely<br />
that one just has to get gud<br />
still I know you just don’t care.</p>

<hr />

<h2 id="beta-channel">Beta Channel</h2>

<p>I started out the talk by encouraging people who use Rustup to try the Beta
channel. Unlike stable, one can get six weeks of performance improvements and
despite thirty-five point-releases since 1.0.0, most of those didn’t fix issues
that many people happened upon.</p>

<p>Even when one wants to be sure to only update once the first point release
is likely to be out, the median release appeared roughly two weeks after the
point-zero one it was based on. Besides, if more people test the beta channel,
its quality is also likely to improve. It’s a win-win situation.</p>

<h2 id="cargo">Cargo</h2>

<p>Cargo has a number of tricks up its sleeve that not everyone knows (so if you
already do, feel free to skip ahead). E.g. there are a number of shortcuts:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>cargo b <span class="c"># build</span>
<span class="nv">$ </span>cargo c <span class="c"># check</span>
<span class="nv">$ </span>cargo d <span class="c"># doc</span>
<span class="nv">$ </span>cargo d <span class="nt">--open</span> <span class="c"># opens docs in browser</span>
<span class="nv">$ </span>cargo t <span class="c"># test</span>
<span class="nv">$ </span>cargo r <span class="c"># run</span>
<span class="nv">$ </span>cargo <span class="nb">rm</span> <span class="nv">$CRATE</span> <span class="c"># remove</span>
</code></pre></div></div>

<p>besides, if one has rust programs in the <code class="language-plaintext highlighter-rouge">examples/</code> subfolder, one can run
them using <code class="language-plaintext highlighter-rouge">cargo r --example &lt;name&gt;</code>.</p>

<p>I also noted that cargo can strip release binaries (but doesn’t by default),
if you add the following to your project’s <code class="language-plaintext highlighter-rouge">Cargo.toml</code>:</p>

<div class="language-toml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[profile.release]</span>
<span class="py">strip</span><span class="p">=</span><span class="kc">true</span>
</code></pre></div></div>

<h4 id="cargo-configuration">Cargo: Configuration</h4>

<p>Cargo not only looks for the <code class="language-plaintext highlighter-rouge">Cargo.toml</code> manifests, it also has its own
project- or user-wide configuration:</p>

<ul>
  <li>project-wide: <code class="language-plaintext highlighter-rouge">.cargo/config.toml</code></li>
  <li>user-wide, UNIX-like (Linux, MacOS, etc.): <code class="language-plaintext highlighter-rouge">~/.cargo/config.toml</code></li>
  <li>user-wide, Windows: <code class="language-plaintext highlighter-rouge">%USERPROFILE%\.cargo\config.toml</code></li>
</ul>

<p>The user configuration can be helpful to…</p>

<p>Add more shortcuts:</p>

<div class="language-toml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[alias]</span>
<span class="py">c</span> <span class="p">=</span> <span class="s">"clippy"</span>
<span class="py">do</span> <span class="p">=</span> <span class="s">"doc --open"</span>
<span class="py">ex</span> <span class="p">=</span> <span class="s">"run --example"</span>
<span class="py">rr</span> <span class="p">=</span> <span class="s">"run --release"</span>
<span class="py">bl</span> <span class="p">=</span> <span class="s">"bless"</span>
<span class="py">s</span> <span class="p">=</span> <span class="s">"semver-checks"</span>
</code></pre></div></div>

<p>Have Rust compile code for the CPU in your computer (which lets the compiler
use all its bells and whistles that normally are off limits in case you give
that executable to a friend):</p>

<div class="language-toml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[build]</span>
<span class="py">rustflags</span> <span class="p">=</span> <span class="p">[</span><span class="s">"-C"</span><span class="p">,</span> <span class="py">"target-cpu</span><span class="p">=</span><span class="err">native</span><span class="s">"]</span><span class="err">
</span></code></pre></div></div>

<p>Have Rust compile your code into a zero-install relocatable static binary</p>

<div class="language-toml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[build]</span>
<span class="py">rustflags</span> <span class="p">=</span> <span class="p">[</span><span class="s">"-C"</span><span class="p">,</span> <span class="py">"target-feature</span><span class="p">=</span><span class="err">+crt-static</span><span class="s">"]</span><span class="err">
</span></code></pre></div></div>

<p>Use a shared <code class="language-plaintext highlighter-rouge">target</code> folder for all your Rust projects (This is very useful if
you have multiple Rust projects with some overlap in dependencies, because build
artifacts will be shared across projects, so they will only need to be compiled
once, conserving both compile time &amp; disk space):</p>

<div class="language-toml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[build]</span>
<span class="py">target</span> <span class="p">=</span> <span class="s">"/home/&lt;user&gt;/.cargo/target"</span>
</code></pre></div></div>

<p>Configure active lints for your project(s):</p>

<div class="language-toml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[lints.rust]</span>
<span class="py">missing_docs</span> <span class="p">=</span> <span class="s">"deny"</span>
<span class="py">unsafe_code</span> <span class="p">=</span> <span class="s">"forbid"</span>

<span class="nn">[lints.clippy]</span>
<span class="py">dbg_macro</span> <span class="p">=</span> <span class="s">"warn"</span>
</code></pre></div></div>

<p>There are sections for both Rust and Clippy. Speaking of which:</p>

<h2 id="clippy">Clippy</h2>

<p>This section has a few allow by default lints to try:</p>

<p><code class="language-plaintext highlighter-rouge">missing_panics_doc</code>, <code class="language-plaintext highlighter-rouge">missing_errors_doc</code>, <code class="language-plaintext highlighter-rouge">missing_safety_doc</code></p>

<p>If you have a function that looks like this:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">unsafe</span> <span class="k">fn</span> <span class="nf">unsafe_panicky_result</span><span class="p">(</span><span class="n">foo</span><span class="p">:</span> <span class="n">Foo</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="n">Bar</span><span class="p">,</span> <span class="n">Error</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">match</span> <span class="k">unsafe</span> <span class="p">{</span> <span class="nf">frobnicate</span><span class="p">(</span><span class="o">&amp;</span><span class="n">foo</span><span class="p">)</span> <span class="p">}</span> <span class="p">{</span>
        <span class="nn">Foo</span><span class="p">::</span><span class="nf">Amajig</span><span class="p">(</span><span class="n">bar</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="nf">Ok</span><span class="p">(</span><span class="n">bar</span><span class="p">),</span>
        <span class="nn">Foo</span><span class="p">::</span><span class="nf">Fighters</span><span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="nd">panic!</span><span class="p">(</span><span class="s">"at the concert"</span><span class="p">);</span>
        <span class="nn">Foo</span><span class="p">::</span><span class="n">FieFoFum</span> <span class="k">=&gt;</span> <span class="nf">Err</span><span class="p">(</span><span class="nn">Error</span><span class="p">::</span><span class="n">GiantApproaching</span><span class="p">),</span>
    <span class="p">}</span>
<span class="p">}</span><span class="err">`</span>
</code></pre></div></div>

<p>The lints will require <code class="language-plaintext highlighter-rouge"># Errors</code>, <code class="language-plaintext highlighter-rouge"># Panics</code> and <code class="language-plaintext highlighter-rouge"># Safety</code> sections in the
function docs, respectively:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cd">/// # Errors</span>
<span class="cd">/// This function returns a `GiantApproaching` error on detecting giant noises</span>
<span class="cd">///</span>
<span class="cd">/// # Panics</span>
<span class="cd">/// The function might panic when called at a Foo Fighters concert</span>
<span class="cd">///</span>
<span class="cd">/// # Safety</span>
<span class="cd">/// Callers must uphold [`frobnicate`]'s invariants'</span>
</code></pre></div></div>

<p>There’s also an <code class="language-plaintext highlighter-rouge">unnecessary_safety_doc</code> lint that warns on <code class="language-plaintext highlighter-rouge"># Safety</code> sections
in docs of safe functions (which is likely a remnant of an unsafe function
being made safe without removing the section from the docs, which might mislead
users):</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cd">/// # Safety</span>
<span class="cd">///</span>
<span class="cd">/// This function is actually completely safe`</span>
<span class="k">pub</span> <span class="k">fn</span> <span class="nf">actually_safe_fn</span><span class="p">()</span> <span class="p">{</span> <span class="nd">todo!</span><span class="p">()</span> <span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">multiple_crate_versions</code> will look at your dependencies and see if you
have multiple versions of a dependency there. For example, if you have the
following dependencies:</p>

<ul>
  <li>mycrate 0.1.0
    <ul>
      <li>rand 0.9.0</li>
      <li>quickcheck 1.0.0
        <ul>
          <li>rand 0.8.0</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<p>The <code class="language-plaintext highlighter-rouge">rand</code> crate will be compiled twice. Of course, that’s totally ok in many
cases (especially if you know that your dependencies will catch up soon-ish),
but if have bigger dependencies, your compile time may suffer. Worse, you may
end up with incompatible types, as a type from one version of a crate isn’t
necessarily compatible with the same type from another version.</p>

<p>So if you have long compile times, or face error messages where a type seems
to be not equal to itself, this lint may help you improve things.</p>

<p>The <code class="language-plaintext highlighter-rouge">non_std_lazy_statics</code> lint will help you to update your code if you still
have a dependency on <code class="language-plaintext highlighter-rouge">lazy_static</code> or <code class="language-plaintext highlighter-rouge">once_cell</code> for functionality that has
been pulled into <code class="language-plaintext highlighter-rouge">std</code>. For example:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// old school lazy statics</span>
<span class="nd">lazy_static!</span> <span class="p">{</span> <span class="k">static</span> <span class="k">ref</span> <span class="n">FOO</span><span class="p">:</span> <span class="n">Foo</span> <span class="o">=</span> <span class="nn">Foo</span><span class="p">::</span><span class="nf">new</span><span class="p">();</span> <span class="p">}</span>
<span class="k">static</span> <span class="n">BAR</span><span class="p">:</span> <span class="nn">once_cell</span><span class="p">::</span><span class="nn">sync</span><span class="p">::</span><span class="n">Lazy</span><span class="o">&lt;</span><span class="n">Foo</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nn">once_cell</span><span class="p">::</span><span class="nn">sync</span><span class="p">::</span><span class="nn">Lazy</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="nn">Foo</span><span class="p">::</span><span class="n">new</span><span class="p">);</span>

<span class="c1">// now in the standard library</span>
<span class="k">static</span> <span class="n">BAZ</span><span class="p">:</span> <span class="nn">std</span><span class="p">::</span><span class="nn">sync</span><span class="p">::</span><span class="n">LazyLock</span><span class="o">&lt;</span><span class="n">Foo</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nn">std</span><span class="p">::</span><span class="nn">sync</span><span class="p">::</span><span class="nn">LazyLock</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="nn">Foo</span><span class="p">::</span><span class="n">new</span><span class="p">);</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">ref_option</code> and <code class="language-plaintext highlighter-rouge">ref_option_ref</code> lints should help you avoid using
references on options as function arguments. Since an <code class="language-plaintext highlighter-rouge">Option&lt;&amp;T&gt;</code> is the same
size as an <code class="language-plaintext highlighter-rouge">&amp;Option&lt;T&gt;</code>, it’s a good idea to use the former to avoid the double
reference.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">foo</span><span class="p">(</span><span class="n">opt_bar</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">Option</span><span class="o">&lt;</span><span class="n">Bar</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{</span> <span class="nd">todo!</span><span class="p">()</span> <span class="p">}</span>
<span class="k">fn</span> <span class="nf">bar</span><span class="p">(</span><span class="n">foo</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Foo</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="o">&amp;</span><span class="nb">Option</span><span class="o">&lt;&amp;</span><span class="n">Bar</span><span class="o">&gt;</span> <span class="p">{</span> <span class="nd">todo!</span><span class="p">()</span> <span class="p">}</span>

<span class="c1">// use instead</span>
<span class="k">fn</span> <span class="nf">foo</span><span class="p">(</span><span class="n">opt_bar</span><span class="p">:</span> <span class="nb">Option</span><span class="o">&lt;&amp;</span><span class="n">Bar</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{</span> <span class="nd">todo!</span><span class="p">()</span> <span class="p">}</span>
<span class="k">fn</span> <span class="nf">bar</span><span class="p">(</span><span class="n">foo</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Foo</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Option</span><span class="o">&lt;&amp;</span><span class="n">Bar</span><span class="o">&gt;</span> <span class="p">{</span> <span class="nd">todo!</span><span class="p">()</span> <span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">same_name_method</code> lint helps you avoid any ambiguities with would later
require a turbo fish to resolve.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">I</span><span class="p">;</span>
<span class="k">impl</span> <span class="n">I</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">into_iter</span><span class="p">(</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">Iter</span> <span class="p">{</span> <span class="n">Iter</span> <span class="p">}</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="nb">IntoIterator</span> <span class="k">for</span> <span class="n">I</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">into_iter</span><span class="p">(</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">Iter</span> <span class="p">{</span> <span class="n">Iter</span> <span class="p">}</span>
    <span class="c1">// ...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">fn_params_excessive_bools</code> lint will warn if you use multiple bools as
arguments in your methods. Those can easily be confused, leading to logic
errors.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">frobnicate</span><span class="p">(</span><span class="n">is_foo</span><span class="p">:</span> <span class="nb">bool</span><span class="p">,</span> <span class="n">is_bar</span><span class="p">:</span> <span class="nb">bool</span><span class="p">)</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>

<span class="c1">// use types to avoid confusion</span>
<span class="k">enum</span> <span class="n">Fooish</span> <span class="p">{</span>
    <span class="n">Foo</span>
    <span class="n">NotFoo</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Clippy looks for a <code class="language-plaintext highlighter-rouge">clippy.toml</code> configuration file you may want to use in your
project:</p>

<div class="language-toml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># for non-library or unstable API projects</span>
<span class="py">avoid-breaking-exported-api</span> <span class="p">=</span> <span class="kc">false</span>
<span class="c"># let's allow even less bools</span>
<span class="py">max-fn-params-bools</span> <span class="p">=</span> <span class="mi">2</span>

<span class="c"># allow certain things in tests</span>
<span class="c"># (if you activate the restriction lints)</span>
<span class="py">allow-dbg-in-tests</span> <span class="p">=</span> <span class="kc">true</span>
<span class="py">allow-expect-in-tests</span> <span class="p">=</span> <span class="kc">true</span>
<span class="py">allow-indexing-slicing-in-tests</span> <span class="p">=</span> <span class="kc">true</span>
<span class="py">allow-panic-in-tests</span> <span class="p">=</span> <span class="kc">true</span>
<span class="py">allow-unwrap-in-tests</span> <span class="p">=</span> <span class="kc">true</span>
<span class="py">allow-print-in-tests</span> <span class="p">=</span> <span class="kc">true</span>
<span class="py">allow-useless-vec-in-tests</span> <span class="p">=</span> <span class="kc">true</span>
</code></pre></div></div>

<h2 id="cargo-semver-checks">Cargo-Semver-Checks</h2>

<p>If you have a library, please use cargo semver-checks before cutting a release.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>cargo semver-checks
    Building optional v0.5.0 <span class="o">(</span>current<span class="o">)</span>
       Built <span class="o">[</span>   1.586s] <span class="o">(</span>current<span class="o">)</span>
     Parsing optional v0.5.0 <span class="o">(</span>current<span class="o">)</span>
      Parsed <span class="o">[</span>   0.004s] <span class="o">(</span>current<span class="o">)</span>
    Building optional v0.5.0 <span class="o">(</span>baseline<span class="o">)</span>
       Built <span class="o">[</span>   0.306s] <span class="o">(</span>baseline<span class="o">)</span>
     Parsing optional v0.5.0 <span class="o">(</span>baseline<span class="o">)</span>
      Parsed <span class="o">[</span>   0.003s] <span class="o">(</span>baseline<span class="o">)</span>
    Checking optional v0.5.0 -&gt; v0.5.0 <span class="o">(</span>no change<span class="o">)</span>
     Checked <span class="o">[</span>   0.005s] 148 checks: 148 pass, 0 skip
     Summary no semver update required
    Finished <span class="o">[</span>  10.641s] optional
</code></pre></div></div>

<p>Your users will be glad you did.</p>

<h2 id="cargo-test">Cargo test</h2>

<p>First, doctests are fast now (apart from <code class="language-plaintext highlighter-rouge">compile_fail</code> ones), so if you
avoided them to keep your turnaround time low, you may want to reconsider.</p>

<p>Also if you have a binary crate, you can still use <code class="language-plaintext highlighter-rouge">#[test]' by converting
your crate to a mixed crate. Put this in your </code>Cargo.toml`:</p>

<div class="language-toml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[lib]</span>
<span class="py">name</span> <span class="p">=</span> <span class="s">"my_lib"</span>
<span class="py">path</span> <span class="p">=</span> <span class="s">"src/lib.rs"</span>

<span class="nn">[[bin]]</span>
<span class="py">name</span> <span class="p">=</span> <span class="s">"my_bin"</span>
<span class="py">path</span> <span class="p">=</span> <span class="s">"src/main.rs"</span>
</code></pre></div></div>

<p>Now you can test all items you have in <code class="language-plaintext highlighter-rouge">lib.rs</code> and any and all modules
reachable from there.</p>

<h2 id="insta">Insta</h2>

<p>Insta is a crate to do <em>snapshot tests</em>. That means it will use the debug
representation or a serialization in JSON or YAML to create a “snapshot” once,
then complain if the snapshot has changed. This removes the need to come up
with known good values, since your tests will create them for you.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[test]</span>
<span class="k">fn</span> <span class="nf">snapshot_test</span><span class="p">()</span> <span class="p">{</span>
    <span class="nn">insta</span><span class="p">::</span><span class="nd">assert_debug_snapshot!</span><span class="p">(</span><span class="nf">my_function</span><span class="p">());</span>
<span class="p">}</span>
</code></pre></div></div>

<p>insta has a few tricks up its sleeve to deal with uncertainty arising from
indeterminism. You can redact the output to e.g. mask randomly chosen IDs:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[test]</span>
<span class="k">fn</span> <span class="nf">redacted_snapshot_test</span><span class="p">()</span> <span class="p">{</span>
    <span class="nn">insta</span><span class="p">::</span><span class="nd">assert_json_snapshot!</span><span class="p">(</span>
        <span class="nf">my_function</span><span class="p">(),</span>
        <span class="p">{</span> <span class="s">".id"</span> <span class="k">=&gt;</span> <span class="s">"[id]"</span> <span class="p">}</span>
    <span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The replacement can also be a function. I have used this with a
<code class="language-plaintext highlighter-rouge">Mutex&lt;HashMap&lt;..&gt;&gt;</code> in the past to replace random IDs with sequence numbers
to ensure that equal IDs stay equal while ignoring their randomness.</p>

<h2 id="cargo-mutants">Cargo Mutants</h2>

<p>Mutation testing is a cool technique where you change your code to check your
tests. It will apply certain changes (for example replacing a <code class="language-plaintext highlighter-rouge">+</code> with a <code class="language-plaintext highlighter-rouge">-</code>
or returning a default value instead of the function result) to your code and
see if tests fail. Those changes are called mutations (or sometimes mutants)
and if they don’t fail any tests, they are deemed “alive”.</p>

<p>I wrote a bit about that technique in the past and even wrote a tool to do
that as a proc macro. Unfortunately, it used specialization and as such was
nightly only, so nowadays I recommend <code class="language-plaintext highlighter-rouge">cargo-mutants</code>. A typical run might look
like this:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>cargo mutants
Found 309 mutants to <span class="nb">test
</span>ok       Unmutated baseline <span class="k">in </span>3.0s build + 2.1s <span class="nb">test
 </span>INFO Auto-set <span class="nb">test timeout </span>to 20s
MISSED   src/lib.rs:1448:9: replace &lt;impl Deserialize <span class="k">for </span>Optioned&lt;T&gt;&gt;::deserialize -&gt; Result&lt;Optioned&lt;T&gt;,
 D::Error&gt; with Ok<span class="o">(</span>Optioned::from_iter<span class="o">([</span>Default::default<span class="o">()]))</span> <span class="k">in </span>0.3s build + 2.1s <span class="nb">test
</span>MISSED   src/lib.rs:1425:9: replace &lt;impl Hash <span class="k">for </span>Optioned&lt;T&gt;&gt;::hash with <span class="o">()</span> <span class="k">in </span>0.3s build + 2.1s <span class="nb">test
</span>MISSED   src/lib.rs:1202:9: replace &lt;impl OptEq <span class="k">for </span>u64&gt;::opt_eq -&gt; bool with <span class="nb">false </span><span class="k">in </span>0.3s build + 2.1s <span class="nb">test
</span>TIMEOUT  src/lib.rs:972:9: replace &lt;impl From <span class="k">for </span>Option&lt;bool&gt;&gt;::from -&gt; Option&lt;bool&gt; with Some<span class="o">(</span><span class="nb">false</span><span class="o">)</span> <span class="k">in </span>0.4s
 build + 20.0s <span class="nb">test
</span>MISSED   src/lib.rs:1139:9: replace &lt;impl Noned <span class="k">for </span>isize&gt;::get_none -&gt; isize with 0 <span class="k">in </span>0.4s build + 2.3s <span class="nb">test
</span>MISSED   src/lib.rs:1228:14: replace <span class="o">==</span> with <span class="o">!=</span> <span class="k">in</span> &lt;impl OptEq <span class="k">for </span>i64&gt;::opt_eq <span class="k">in </span>0.3s build + 2.1s <span class="nb">test
</span>MISSED   src/lib.rs:1218:9: replace &lt;impl OptEq <span class="k">for </span>i16&gt;::opt_eq -&gt; bool with <span class="nb">false </span><span class="k">in </span>0.3s build + 2.1s <span class="nb">test
</span>MISSED   src/lib.rs:1248:9: replace &lt;impl OptEq <span class="k">for </span>f64&gt;::opt_eq -&gt; bool with <span class="nb">true </span><span class="k">in </span>0.4s build + 2.1s <span class="nb">test
</span>MISSED   src/lib.rs:1239:9: replace &lt;impl OptEq <span class="k">for </span>f32&gt;::opt_eq -&gt; bool with <span class="nb">false </span><span class="k">in </span>0.4s build + 2.1s <span class="nb">test</span>
...
309 mutants tested <span class="k">in </span>9m 26s: 69 missed, 122 caught, 112 unviable, 6 timeouts
</code></pre></div></div>

<p>Unlike code coverage, mutation testing not only finds which code is run by your
tests, but which code is actually tested against changes – at least as far as
they can be automatically applied.</p>

<p>Also mutation testing can give you the information which tests cover what
possible mutations, so you sometimes can remove some tests, making your test
suite leaner and faster.</p>

<h2 id="rust-analyzer">rust-analyzer</h2>

<p>I just gave a few settings that may improve your experience:</p>

<div class="language-toml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># need to install the rust-src component with rustup</span>
<span class="py">rust-analyzer.rustc.source</span> <span class="p">=</span> <span class="s">"discover"</span> 
<span class="c"># on auto-import, prefer importing from `prelude`</span>
<span class="py">rust-analyzer.imports.preferPrelude</span> <span class="p">=</span> <span class="kc">true</span>
<span class="c"># don't look at references from tests</span>
<span class="py">rust-analyzer.references.excludeTests</span> <span class="p">=</span> <span class="kc">true</span>
</code></pre></div></div>

<h2 id="cargo-sweep">cargo sweep</h2>

<p>If you are like me, you can get a very large <code class="language-plaintext highlighter-rouge">target/</code> folder.</p>

<p><code class="language-plaintext highlighter-rouge">cargo sweep</code> will remove outdated build artifacts:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>cargo sweep <span class="nt">--time</span> 14 <span class="c"># remove build artifacts older than 2 weeks</span>
<span class="nv">$ </span>cargo sweep <span class="nt">--installed</span> <span class="c"># remove build artifacts from old rustcs</span>
</code></pre></div></div>

<p>Pro Tip: Add a cronjob (for example every Friday on 10 AM):</p>

<pre><code class="language-crontab">0 10 * * fri sh -c "rustup update &amp;&amp; cargo sweep --installed"
</code></pre>

<h2 id="cargo-wizard">cargo wizard</h2>

<p>This is a subcommand that will give you a TUI to configure your project, giving
you a suitable Cargo.toml etc.</p>

<h2 id="cargo-pgo">cargo pgo</h2>

<p>Profile-guided optimization is a great technique to eke out that last bit of
performance without needing any code changes. I didn’t go into detail on it
because Aliaksandr Zaitsau did a whole talk on it and I wanted to avoid the
duplication.</p>

<h2 id="cargo-component">cargo component</h2>

<p>This tool will allow you to run your code locally under a WASM runtime.</p>

<ul>
  <li>Run your code in <code class="language-plaintext highlighter-rouge">wasm32-wasip1</code> (or later)</li>
  <li>the typical subcommands (<code class="language-plaintext highlighter-rouge">test</code>, <code class="language-plaintext highlighter-rouge">run</code>, etc.) work as usual</li>
  <li>can use a target runner:</li>
</ul>

<div class="language-toml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[target.wasm32-wasip1]</span>
<span class="py">runner</span> <span class="p">=</span> <span class="p">[</span><span class="s">"wasmtime"</span><span class="p">,</span> <span class="py">"--dir</span><span class="p">=</span><span class="err">.</span><span class="s">"]</span><span class="err">
</span></code></pre></div></div>

<p>The argument is used to allow accessing the current directory (because by
default the runtime will disallow all file access). You can of course also use
different directories there.</p>

<h2 id="bacon">bacon</h2>

<p>compiles and runs tests on changes</p>

<p>great to have in a sidebar terminal</p>

<p>‘nuff said.</p>

<h2 id="language-pattern-matching">Language: Pattern matching</h2>

<p>Rust patterns are super powerful. You can</p>

<ul>
  <li>destructure tuples and slices</li>
  <li>match integer and char ranges</li>
  <li>or-combine patterns with the pipe symbol, even within other patterns (note
that the bindings need to have the same types). You can even use a pipe at the
start of your pattern to get a nice vertical line in your code (see below)</li>
  <li>use guard clauses within patterns (<code class="language-plaintext highlighter-rouge">pattern if guard(pattern) =&gt; arm</code>)</li>
</ul>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">match</span> <span class="p">(</span><span class="n">foo</span><span class="p">,</span> <span class="n">bar</span><span class="p">)</span> <span class="p">{</span>
  <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="p">[</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="o">..</span><span class="p">])</span> <span class="k">=&gt;</span> <span class="nd">todo!</span><span class="p">(),</span>
  <span class="p">(</span><span class="mi">2</span> <span class="o">..=</span> <span class="mi">4</span><span class="p">,</span> <span class="n">x</span><span class="p">)</span> <span class="k">if</span> <span class="nf">predicate</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="nf">frobnicate</span><span class="p">(</span><span class="n">x</span><span class="p">),</span>
  <span class="p">(</span><span class="mi">5</span><span class="o">..</span><span class="mi">8</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="nd">todo!</span><span class="p">(),</span>
  <span class="n">_</span> <span class="k">=&gt;</span> <span class="p">()</span>
<span class="p">}</span>

<span class="k">if</span> <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="mi">1</span> <span class="p">|</span> <span class="mi">23</span><span class="p">)</span> <span class="p">|</span> <span class="nb">None</span> <span class="o">=</span> <span class="n">x</span> <span class="p">{</span> <span class="nd">todo!</span><span class="p">()</span> <span class="p">}</span>

<span class="k">match</span> <span class="n">foo</span> <span class="p">{</span>
  <span class="p">|</span> <span class="nn">Foo</span><span class="p">::</span><span class="n">Bar</span>
  <span class="p">|</span> <span class="nn">Foo</span><span class="p">::</span><span class="nf">Baz</span><span class="p">(</span><span class="nn">Baz</span><span class="p">::</span><span class="n">Blorp</span> <span class="p">|</span> <span class="nn">Baz</span><span class="p">::</span><span class="n">Blapp</span><span class="p">)</span>
  <span class="p">|</span> <span class="nn">Foo</span><span class="p">::</span><span class="nf">Boing</span><span class="p">(</span><span class="n">_</span><span class="p">)</span>
  <span class="p">|</span> <span class="nn">Foo</span><span class="p">::</span><span class="nf">Blammo</span><span class="p">(</span><span class="o">..</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="nd">todo!</span><span class="p">(),</span>
  <span class="n">_</span> <span class="k">=&gt;</span> <span class="p">()</span>
<span class="p">}</span>

<span class="nd">matches!</span><span class="p">(</span><span class="n">foo</span><span class="p">,</span> <span class="nn">Foo</span><span class="p">::</span><span class="n">Bar</span><span class="p">)</span>
</code></pre></div></div>

<p>Also patterns may appear in surprising places: Arguments in function signatures
are patterns, too – and so are closure arguments:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">frobnicate</span><span class="p">(</span><span class="n">Bar</span> <span class="p">{</span> <span class="n">baz</span><span class="p">,</span> <span class="n">blorp</span> <span class="p">}:</span> <span class="n">Bar</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">let</span> <span class="n">closure</span> <span class="o">=</span> <span class="p">|</span><span class="nf">Blorp</span><span class="p">(</span><span class="n">flip</span><span class="p">,</span> <span class="n">flop</span><span class="p">)|</span> <span class="nf">blorp</span><span class="p">(</span><span class="n">flip</span><span class="p">,</span> <span class="n">flop</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>What’s more, patterns can be used in <code class="language-plaintext highlighter-rouge">let</code> and in plain assignments:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="k">mut</span> <span class="n">b</span><span class="p">,</span> <span class="k">mut</span> <span class="n">x</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">z</span><span class="p">);</span>
<span class="k">let</span> <span class="nf">Some</span><span class="p">((</span><span class="n">e</span><span class="p">,</span> <span class="n">f</span><span class="p">))</span> <span class="o">=</span> <span class="n">foo</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span><span class="p">;</span> <span class="p">};</span>
<span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="n">x</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="n">f</span><span class="p">);</span>
</code></pre></div></div>

<p>As you can see, with plain <code class="language-plaintext highlighter-rouge">let</code> and assignment, you need an <em>irrefutable</em>
pattern (that must always match by definition), otherwise you can do
<code class="language-plaintext highlighter-rouge">let-else</code>.</p>

<h3 id="language-annotations">Language: Annotations</h3>

<p>use <code class="language-plaintext highlighter-rouge">#[expect(..)]</code> instead of <code class="language-plaintext highlighter-rouge">#[allow(..)]</code>, because it will warn if the
code in question is no longer linted (either because the code or clippy
changed), so the <code class="language-plaintext highlighter-rouge">#[allow(..)]</code> will just linger.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[expect(clippy::collapsible_if)</span>
<span class="nd">fn</span> <span class="nd">foo(b:</span> <span class="nd">bool,</span> <span class="nd">c:</span> <span class="nd">u8)</span> <span class="err">[</span>
    <span class="nd">if</span> <span class="nd">b</span> <span class="err">{</span>
        <span class="nd">if</span> <span class="nd">c</span> <span class="err">&lt;</span> <span class="mi">25</span> <span class="err">{</span>
            <span class="nd">todo</span><span class="err">!</span><span class="nd">()</span><span class="err">;</span>
        <span class="err">}</span>
    <span class="err">}</span>
<span class="err">}</span>
</code></pre></div></div>

<p>Add <code class="language-plaintext highlighter-rouge">#[must_use]</code> judiciously on library APIs to help your users avoid
mistakes. There’s even a pedantic <code class="language-plaintext highlighter-rouge">clippy::must_use_candidates</code> lint that you
can auto-apply to help you do it.</p>

<p>You can also annotate types that should always be used when returned from
functions.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[must_use]</span>
<span class="k">fn</span> <span class="nf">we_care_for_the_result</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="n">Foo</span> <span class="p">{</span> <span class="nd">todo!</span><span class="p">()</span> <span class="p">}</span>

<span class="nd">#[must_use]</span>
<span class="k">enum</span> <span class="n">MyResult</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span> <span class="nf">Ok</span><span class="p">(</span><span class="n">T</span><span class="p">),</span> <span class="nf">Err</span><span class="p">(</span><span class="k">crate</span><span class="p">::</span><span class="n">Error</span><span class="p">),</span> <span class="n">SomethingElse</span> <span class="p">}</span>

<span class="nf">we_care_for_the_result</span><span class="p">();</span> <span class="c1">// Err: unused_must_use</span>
<span class="nf">returns_my_result</span><span class="p">();</span> <span class="c1">// Err: unused_must_use</span>
</code></pre></div></div>

<p>Traits sometimes need special handling. Tell your users what to do:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[diagnostic::on_unimplemented(</span>
    <span class="nd">message</span> <span class="nd">=</span> <span class="s">"Don't `impl Fooable&lt;{T}&gt;` directly, `#[derive(Bar)]` on `{Self}` instead"</span><span class="nd">,</span>
    <span class="nd">label</span> <span class="nd">=</span> <span class="s">"This is the {Self}"</span>
    <span class="nd">note</span> <span class="nd">=</span> <span class="s">"additional context"</span>
<span class="nd">)]</span>
<span class="k">trait</span> <span class="n">Fooable</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span> <span class="o">..</span> <span class="p">}</span>
</code></pre></div></div>

<p>Sometimes, you want internals to stay out of the compiler’s error messages:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[diagnostic::do_not_recommend]</span>
<span class="k">impl</span> <span class="n">Fooable</span> <span class="k">for</span> <span class="n">FooInner</span> <span class="p">{</span> <span class="o">..</span> <span class="p">}</span>
</code></pre></div></div>

<h2 id="library-boxleak">library: <code class="language-plaintext highlighter-rouge">Box::leak</code></h2>

<p>For <code class="language-plaintext highlighter-rouge">&amp;'static</code>, once-initialized things that don’t need to be <code class="language-plaintext highlighter-rouge">drop</code>ped</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">config</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">'static</span> <span class="n">Configuration</span> <span class="o">=</span> <span class="nn">Box</span><span class="p">::</span><span class="nf">leak</span><span class="p">(</span><span class="nf">create_config</span><span class="p">());</span>
<span class="nf">main_entry_point</span><span class="p">(</span><span class="n">config</span><span class="p">);</span>
</code></pre></div></div>

<h2 id="the-end">The End?</h2>

<p>That’s all I could fit in my talk, so thanks for reading this far.</p>]]></content><author><name>Llogiq</name></author><summary type="html"><![CDATA[This is the companion blog post to my eponymous Rustikon talk. The video recording and slides are also available now.]]></summary></entry><entry><title type="html">Easy Mode Rust</title><link href="https://llogiq.github.io/2024/03/28/easy.html" rel="alternate" type="text/html" title="Easy Mode Rust" /><published>2024-03-28T00:00:00+00:00</published><updated>2024-03-28T00:00:00+00:00</updated><id>https://llogiq.github.io/2024/03/28/easy</id><content type="html" xml:base="https://llogiq.github.io/2024/03/28/easy.html"><![CDATA[<p>This post is based on my <a href="https://rustnationuk.com">RustNationUK ‘24</a> talk with the same title. The <a href="https://www.youtube.com/watch?v=33FG6O3qejM">talk video</a> is on youtube, the slides <a href="/talks/easy.html">are served from here</a>.</p>

<p>Also, here’s the lyrics of the song I introduced the talk with (sung to the tune of Bob Dylan’s “The times, they are a-changin’”):</p>

<blockquote>
  <p>Come gather Rustaceans wherever you roam<br />
and admit that our numbers have steadily grown.<br />
The community’s awesomeness ain’t set in stone,<br />
so if that to you is worth saving<br />
then you better start teamin’ up instead of toilin’ alone<br />
for the times, they are a-changin’.</p>

  <p>Come bloggers and writers who tutorize with your pen<br />
and teach those new folks, the chance won’t come again!<br />
Where there once was one newbie, there soon will be ten<br />
and your knowledge is what they are cravin’.<br />
Know that what you share with them is what you will gain<br />
for the times, they are a-changin’.</p>

  <p>Researchers and coders, please heed the call,<br />
Without your efforts Rust would be nothin’ at all<br />
and unsafety would rise where it now meets its fall.<br />
May C++ proponents be ravin’.<br />
What divides them from us is but a rustup install<br />
for the times, they are a-changin’.</p>

  <p>Fellow moderators throughout the land,<br />
don’t you dare censor what is not meant to offend<br />
otherwise far too soon helpful people be banned<br />
and what’s left will be angry folks ragin’.<br />
Our first order of business is to help understand<br />
that the times, they are a-changin’.</p>

  <p>The line it is drawn, the type it is cast<br />
What debug runs slow, release will run fast<br />
as the present now will later be past<br />
and our values be rapidly fadin’<br />
unless we find new people who can make them last<br />
for the times, they are a-changin’.</p>
</blockquote>

<p>Rust has an only somewhat deserved reputation for being hard to learn. But that is mostly an unavoidable consequence of being a systems language that has to supply full control over the myriad of specifics of your code and runtime. But I’d argue that our method of teaching Rust is rather more at fault for this reputation. So as an antidote to this “the right way to do it” thinking, I offer this set of ideas on how to learn as little Rust as possible to become productive in Rust, so you can start and have success right away and learn the harder parts later when you’re comfortable with the basics.</p>

<p>In the talk I started with the “ground rules” for the exercise: I wanted to identify a small subset to learn that will allow people to successfully write Rust programs to solve the problems in front of them without being overwhelmed by all kinds of new concepts. I am happy to forgo on performance, brevity of the code or idiomatic code. In fact, some of the suggestions fly in the face of conventional guidelines on how good Rust code should look like. One of the questions after the talk was how to deal with new contributors or colleagues pushing “substandard” code to a project, and here my suggestion is to just merge it and clean it up after the fact. New users will feel unsure about their abilities, and nitpicking on details will put them off where we want to encourage them in their growth and learning, at least in the beginning.</p>

<p>Of course, the flipside of this is that I don’t suggest that every Rustacean learn only this subset and forever avoid all else. The idea here is to make you productive and successful quickly, and you can then build on that. A suggestion that also came up after my talk was to create a poster with a “research tree” (that is sometimes used in strategy games like e.g. Civilization to give people a path to progress without making it too linear). This is still on my list and I’ll open a repo for that soon, in the hope of finding people who’ll help me.</p>

<p>So without further ado, here are the things we want to avoid learning, and how to do that:</p>

<h3 id="syntax">Syntax</h3>

<p>Rust is not a small language. When starting out, for flow control it’s best to stick to basic things like <code class="language-plaintext highlighter-rouge">if</code>, <code class="language-plaintext highlighter-rouge">for</code> and <code class="language-plaintext highlighter-rouge">while</code>. If you need to distinguish e.g. enum variants, you can also use <code class="language-plaintext highlighter-rouge">match</code>, but keep it simple: Only match one thing, and avoid more complex things like guard clauses:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Don't nest patterns in match arms</span>
<span class="k">match</span> <span class="n">err_opt_val</span> <span class="p">{</span>
  <span class="nf">Some</span><span class="p">(</span><span class="nf">Err</span><span class="p">(</span><span class="n">e</span><span class="p">))</span> <span class="k">=&gt;</span> <span class="nd">panic!</span><span class="p">(</span><span class="s">"{e}"</span><span class="p">),</span>
  <span class="n">_</span> <span class="k">=&gt;</span> <span class="p">(),</span>
<span class="p">}</span>

<span class="c1">// instead, nest `match` expressions</span>
<span class="k">match</span> <span class="n">err_opt_val</span> <span class="p">{</span>
  <span class="nf">Some</span><span class="p">(</span><span class="n">err_val</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="k">match</span> <span class="n">err_val</span> <span class="p">{</span>
    <span class="nf">Err</span><span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="nd">panic!</span><span class="p">(</span><span class="s">"{e}"</span><span class="p">),</span>
    <span class="n">_</span> <span class="k">=&gt;</span> <span class="p">(),</span>
  <span class="p">},</span>
  <span class="n">_</span> <span class="k">=&gt;</span> <span class="p">(),</span>
<span class="p">}</span>

<span class="c1">// Don't use guards</span>
<span class="k">match</span> <span class="n">w</span> <span class="p">{</span>
  <span class="nf">Some</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">if</span> <span class="n">x</span> <span class="o">&gt;</span> <span class="mi">3</span> <span class="k">=&gt;</span> <span class="p">{</span> <span class="nf">one</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="p">},</span>
  <span class="nf">Some</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span> <span class="nf">other</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="p">},</span>
  <span class="nb">None</span> <span class="k">=&gt;</span> <span class="p">(),</span>
<span class="p">}</span>

<span class="c1">// instead, nest with `if`</span>
<span class="c1">// (that might require you to copy code)</span>
<span class="k">match</span> <span class="n">w</span> <span class="p">{</span>
  <span class="nf">Some</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
    <span class="k">if</span> <span class="n">x</span> <span class="o">&gt;</span> <span class="mi">3</span> <span class="p">{</span>
      <span class="nf">one</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="nf">other</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
    <span class="p">}</span>
  <span class="p">},</span>
  <span class="nb">None</span> <span class="k">=&gt;</span> <span class="p">(),</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Avoid other constructs for now (such as <code class="language-plaintext highlighter-rouge">if let</code> or <code class="language-plaintext highlighter-rouge">let</code>-<code class="language-plaintext highlighter-rouge">else</code>). While they might make the code more readable, you can learn them later and have your IDE refactor your code quickly as you become privy to how they work. Within loops, avoid <code class="language-plaintext highlighter-rouge">break</code> and <code class="language-plaintext highlighter-rouge">continue</code>, especially with values. Rather introduce a new function that <code class="language-plaintext highlighter-rouge">return</code>s the value from within a loop.</p>

<p>As discussed in the introduction, this will take more code and thus exacerbate both brevity and readability, but the individual moving parts are far simpler.</p>

<h3 id="the-sborrow-checker">The <del>S</del>Borrow checker</h3>

<p>In my talk, I used a classic example that will often come up during search algorithms: Extending a collection of items with filtered and modified versions of the prior items.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">item</span> <span class="k">in</span> <span class="n">items</span><span class="nf">.iter</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">if</span> <span class="nf">predicate</span><span class="p">(</span><span class="n">item</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">items</span><span class="nf">.push</span><span class="p">(</span><span class="nf">modify</span><span class="p">(</span><span class="n">item</span><span class="p">));</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The code here pretty much mirrors what you’d do in e.g. Python. It’s simple to read and understand, and there aren’t any needless moving parts. Unfortunately, it is also wrong, and the compiler won’t hesitate to point that out:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  Compiling unfortunate v0.0.1
error[E0502]: cannot borrow `items` as mutable because it is also borrowed as immutable
  --&gt; src/main.rs:16:13
   |
13 |     for item in items.iter() {
   |                 ------------
   |                 |
   |                 immutable borrow occurs here
   |                 immutable borrow later used here
...
16 |             items.push(new_item);
   |             ^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here

For more information about this error, try `rustc --explain E0502`.
</code></pre></div></div>

<p>Luckily, in almost all of the cases, we can split the immutable from the mutable borrows. In this particular case, we can simply iterate over a clone of our item list:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//               vvvvvvvv</span>
<span class="k">for</span> <span class="n">item</span> <span class="k">in</span> <span class="n">items</span><span class="nf">.clone</span><span class="p">()</span><span class="nf">.iter</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">if</span> <span class="nf">predicate</span><span class="p">(</span><span class="n">item</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">items</span><span class="nf">.push</span><span class="p">(</span><span class="nf">modify</span><span class="p">(</span><span class="n">item</span><span class="p">));</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I used to be very wary of cloning in the past, considering it an antipattern, but unless that code is on the hot path, it literally won’t show up on your application’s profile. so going to the effort of avoiding that clone is premature optimization. However, if you have measured and note that the clone <em>is</em> in fact showing up either on your memory or CPU profile, you can switch to indexing instead:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="n">items</span><span class="nf">.len</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">if</span> <span class="nf">predicate</span><span class="p">(</span><span class="o">&amp;</span><span class="n">items</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">new_item</span> <span class="o">=</span> <span class="nf">modify</span><span class="p">(</span><span class="o">&amp;</span><span class="n">items</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
    <span class="n">items</span><span class="nf">.push</span><span class="p">(</span><span class="n">new_item</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Note that this approach is more brittle than the clone based one: While in this case the loop itself only uses integers and thus doesn’t borrow anything, we might still inadvertently introduce overlapping borrows into the loop body. For example, if we replaced the <code class="language-plaintext highlighter-rouge">if</code> with <code class="language-plaintext highlighter-rouge">if let</code>, <code class="language-plaintext highlighter-rouge">items</code> would be borrowed from for the duration of the then-clause, thus causing the exact error we were trying to avoid in the first place. Also note that we put <code class="language-plaintext highlighter-rouge">modify(..)</code> into a local to avoid having it within the <code class="language-plaintext highlighter-rouge">push</code>, which might also possibly trip up the borrow checker.</p>

<p>Again, we’re not generally aiming for performance, so I would prefer the clone-based variant as much as possible.</p>

<h3 id="macros">Macros</h3>

<p>Macros come up early in Rust. Literally the first Rust program everyone compiles (or even writes) is:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="nd">println!</span><span class="p">(</span><span class="s">"Hello, World!"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>You’ll have a hard time writing Rust without calling macros, so I would suggest you treat them like you’d treat functions, with the caveat that they can have a rather variable syntax, but they’ll usually document that. So as long as you roughly know how to call the macro and what it does, feel free to call them as you like.</p>

<p><em>Writing</em> macros is something we’ll want to avoid. Rust has a number of macro types (declarative macros, declarative macros 2.0, derive macros, annotation macros and procedural bang-macros), but we’re not going to look into writing any of those. All of those macro variants solve a single problem: Code duplication.</p>

<p>Now the obvious simple solution to avoiding macros is: Duplicate your code.</p>

<p>While that sounds very simple, in fact the advice can be split into a hierarchy of solutions that depend on the problem at hand:</p>

<ol>
  <li>up to 5 times, less than 10 lines of code, not expected to change: In this case I’d just copy &amp; paste the code and edit it to fit your requirements. Of course, you still have the risk of introducing errors in one of the instances of the copied code, but with the resulting code being reasonably compact, you’ll have a good chance to catch those quickly.</li>
  <li>more than that, still not expected to change within a certain time: Who is better than creating multiple almost-same instances of the same thing than you? Your computer of course! So write some Rust code that builds Rust code by building strings (using <code class="language-plaintext highlighter-rouge">format!(..)</code> or <code class="language-plaintext highlighter-rouge">println!(..)</code>), call it once and copy the output into your code. Voilà!</li>
  <li>expected to be up to date with the rest of the code? In that case, put your code generation into a unit test that reads and splits out the current version of the code, generates the possibly updated version, compares both and if they differ writes the updated version of the code, then panic with a message to tell whoever ran the test they need to commit the changes. It is helpful to add start and end marker comments to the generated code to make splitting it out easier and to document the fact that code is generated.</li>
</ol>

<p>In code, instead of doing:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">macro_rules!</span> <span class="n">make_foo</span> <span class="p">{</span>
  <span class="p">{</span> <span class="nv">$</span><span class="p">(</span><span class="nv">$a:ident</span><span class="p">),</span><span class="o">*</span> <span class="p">}</span> <span class="k">=&gt;</span> <span class="p">{</span> <span class="nv">$</span><span class="p">(</span><span class="k">let</span> <span class="nv">$a</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"foo"</span> <span class="p">};)</span><span class="o">*</span> <span class="p">};</span>
<span class="p">}</span>
<span class="nd">make_foo!</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">);</span>
</code></pre></div></div>

<p>Either 1. copy &amp; paste instead:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">a</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"foo"</span> <span class="p">};</span>
<span class="k">let</span> <span class="n">b</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"foo"</span> <span class="p">};</span>
</code></pre></div></div>

<p>Or 2. write code to generate code:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">format_code</span><span class="p">(</span><span class="n">names</span><span class="p">:</span> <span class="o">&amp;</span><span class="p">[</span><span class="o">&amp;</span><span class="nb">str</span><span class="p">])</span> <span class="k">-&gt;</span> <span class="nb">String</span> <span class="p">{</span>
  <span class="k">let</span> <span class="k">mut</span> <span class="n">result</span> <span class="o">=</span> <span class="nn">String</span><span class="p">::</span><span class="nf">new</span><span class="p">();</span>
  <span class="k">for</span> <span class="n">name</span> <span class="k">in</span> <span class="n">names</span> <span class="p">{</span>
    <span class="n">result</span> <span class="o">+=</span> <span class="nd">format!</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">let {name} = </span><span class="se">\"</span><span class="s">foo</span><span class="se">\"</span><span class="s">;"</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="n">result</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Additionally, 3. use a test to keep code updated:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[test]</span>
<span class="k">fn</span> <span class="nf">update_code</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">let</span> <span class="p">(</span><span class="n">prefix</span><span class="p">,</span> <span class="n">actual</span><span class="p">,</span> <span class="n">suffix</span><span class="p">)</span> <span class="o">=</span> <span class="nf">read_code</span><span class="p">();</span>
  <span class="k">let</span> <span class="n">expected</span> <span class="o">=</span> <span class="nf">format_code</span><span class="p">(</span><span class="o">&amp;</span><span class="p">[</span><span class="s">"a"</span><span class="p">,</span> <span class="s">"b"</span><span class="p">]);</span>
  <span class="k">if</span> <span class="n">expected</span> <span class="o">==</span> <span class="n">actual</span> <span class="p">{</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span>
  <span class="nf">write_code</span><span class="p">(</span><span class="n">prefix</span><span class="p">,</span> <span class="n">expected</span><span class="p">,</span> <span class="n">suffix</span><span class="p">);</span>
  <span class="nd">panic!</span><span class="p">(</span><span class="s">"updated generated code, please commit"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Alexey Kladov explains the latter technique better than I could in his blog post about <a href="https://matklad.github.io/2022/03/26/self-modifying-code.html">Self-modifying code</a>.</p>

<h3 id="generics">Generics</h3>

<p>Generics lets us re-use code in various situations by being able to swap out types. However, they can also be a great source of complexity, so we’ll of course want to avoid them. As Go before version 1.2 has shown, you can get quite far without them, so unless it’s for the element type of collections, we’ll want to avoid using them.</p>

<p>So instead of writing</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">Foo</span><span class="o">&lt;</span><span class="n">A</span><span class="p">,</span> <span class="n">B</span><span class="o">&gt;</span> <span class="p">{</span> <span class="o">..</span> <span class="p">}</span>
<span class="k">fn</span> <span class="n">foo</span><span class="o">&lt;</span><span class="n">A</span><span class="p">,</span> <span class="n">B</span><span class="o">&gt;</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="n">A</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="n">B</span><span class="p">)</span> <span class="p">{</span> <span class="o">..</span> <span class="p">}</span>
</code></pre></div></div>

<p>we’d monomorphize by hand (that is build a copy of the code for each set of concrete types we need), so for each <code class="language-plaintext highlighter-rouge">A</code>/<code class="language-plaintext highlighter-rouge">B</code> combination, we’d write:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// given `struct X; struct Y`</span>
<span class="k">struct</span> <span class="n">FooXY</span> <span class="p">{</span> <span class="o">..</span> <span class="p">}</span>
<span class="k">fn</span> <span class="nf">foo_x_y</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="n">X</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="n">Y</span><span class="p">)</span> <span class="p">{</span> <span class="o">..</span> <span class="p">}</span>
</code></pre></div></div>

<p>Of course, you might end up with a lot of copies, so use the code generation from above to deal with that.</p>

<h3 id="lifetimes">Lifetimes</h3>

<p>Lifetime annotations are those arcane tick+letter things that sometimes even stump intermediate Rust programmers, so you won’t be surprised to find them on this list. They look like this:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">Borrowed</span><span class="o">&lt;</span><span class="nv">'a</span><span class="o">&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="nv">'a</span> <span class="nb">u32</span><span class="p">);</span>
<span class="k">fn</span> <span class="n">borrowing</span><span class="o">&lt;</span><span class="nv">'a</span><span class="p">,</span> <span class="nv">'b</span><span class="o">&gt;</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="o">&amp;</span><span class="nv">'a</span> <span class="nb">str</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="o">&amp;</span><span class="nv">'b</span> <span class="nb">str</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="o">&amp;</span><span class="nv">'a</span> <span class="nb">str</span> <span class="p">{</span> <span class="o">..</span> <span class="p">}</span>
</code></pre></div></div>

<p>Of course, we don’t want to burden ourselves with those sigil-laden monstrosities for now. To get rid of those, we have to avoid borrowing in function signatures. So instead of taking a reference, take an owned instance. And yes, this will incur more cloning yet. If you need to share an object (e.g. because you want to mutate it), wrap it in an <code class="language-plaintext highlighter-rouge">Arc</code>:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="nf">Arced</span><span class="p">(</span><span class="nb">Arc</span><span class="o">&lt;</span><span class="nb">u32</span><span class="o">&gt;</span><span class="p">);</span>
<span class="k">fn</span> <span class="nf">cloned</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="nb">String</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="nb">String</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">String</span> <span class="p">{</span> <span class="o">..</span> <span class="p">}</span>
<span class="k">fn</span> <span class="nf">arced</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="nb">Arc</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="nb">Arc</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">String</span> <span class="p">{</span> <span class="o">..</span> <span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">Arc</code> is a smart pointer that lets you <code class="language-plaintext highlighter-rouge">.clone()</code> without cloning the wrapped value. Both <code class="language-plaintext highlighter-rouge">Arc</code>s will lead to the exact same value, and if you mutate one, the other will also have changed.</p>

<h3 id="traits">Traits</h3>

<p>You can get a lot done without ever implementing a trait in Rust. However, there are some traits (especially in the standard library, but also in trait-heavy crates like <a href="https://serde.rs">serde</a>) that you might need to get some stuff done. In many cases, you can use a <code class="language-plaintext highlighter-rouge">#[derive(..)]</code>-annotation, such as</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[derive(Copy,</span> <span class="nd">Clone,</span> <span class="nd">Default,</span> <span class="nd">Eq,</span> <span class="nd">PartialEq,</span> <span class="nd">Hash)]</span>
<span class="k">struct</span> <span class="n">MyVeryBadExampleIAmSoSorry</span> <span class="p">{</span>
  <span class="n">size</span><span class="p">:</span> <span class="nb">usize</span><span class="p">,</span>
  <span class="n">makes_sense</span><span class="p">:</span> <span class="nb">bool</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In some cases, Rust lore would tell you to use trait based dispatch, but in most of those cases, an <code class="language-plaintext highlighter-rouge">enum</code> and a <code class="language-plaintext highlighter-rouge">match</code> or even a bag of <code class="language-plaintext highlighter-rouge">if</code>-clauses will do the trick. Remember, we’re not attempting to have our code win a beauty contest, just get the job done.</p>

<p>Finally, if you use a framework that requires you to manually implement a trait, write <code class="language-plaintext highlighter-rouge">impl WhateverTrait for SomeType</code> and use the <code class="language-plaintext highlighter-rouge">insert missing members</code> code action from your IDE if available.</p>

<h3 id="modules-and-imports">Modules and Imports</h3>

<p>This is something we cannot completely avoid. If we don’t use any imports, our code can only use what’s defined in the standard library prelude, and we won’t get very far with that. Also even if we did, we’d end up with a 10+k lines of code file, and no one wants to navigate that. On the other hand, when using modules, we should strive to not go overboard, lest we find ourselves in a maze of twisty little <code class="language-plaintext highlighter-rouge">mod.rs</code> files, all different (pardon the text adventure reference).</p>

<p>So we obviously need to import stuff we <code class="language-plaintext highlighter-rouge">use</code>, but how do we introduce <code class="language-plaintext highlighter-rouge">mod</code>s? The key to keeping this simple is the observation that mods conflate code 
organization with the hierarchy of paths in our crate. So if I have a <code class="language-plaintext highlighter-rouge">mod foo</code> containing a <code class="language-plaintext highlighter-rouge">bar</code>, people using my code will have to either import or directly specify <code class="language-plaintext highlighter-rouge">foo::bar</code>. But there are two recipes we can follow to untangle those. Given an example <code class="language-plaintext highlighter-rouge">lib.rs</code> where our code has two functions:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">fn</span> <span class="nf">a</span><span class="p">()</span> <span class="p">{}</span>
<span class="k">pub</span> <span class="k">fn</span> <span class="nf">b</span><span class="p">()</span> <span class="p">{}</span>
</code></pre></div></div>

<p>Now in practice, those functions likely won’t be empty, and in most cases we’ll have more than two of them, but you want to read this blog post, not wade through screens of code, so let’s look at the first recipe we will use to move <code class="language-plaintext highlighter-rouge">b</code> to a new <code class="language-plaintext highlighter-rouge">b.rs</code> file without changing the path where <code class="language-plaintext highlighter-rouge">b</code> is visible from the outside. The recipe has three steps:</p>

<ol>
  <li>Declare the <code class="language-plaintext highlighter-rouge">b</code> module in <code class="language-plaintext highlighter-rouge">lib.rs</code> and <code class="language-plaintext highlighter-rouge">pub use</code> <code class="language-plaintext highlighter-rouge">b</code> from it:</li>
</ol>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">mod</span> <span class="n">b</span><span class="p">;</span>
<span class="k">pub</span> <span class="k">use</span> <span class="nn">b</span><span class="p">::</span><span class="n">b</span><span class="p">;</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="nf">a</span><span class="p">()</span> <span class="p">{}</span>
<span class="k">pub</span> <span class="k">fn</span> <span class="nf">b</span><span class="p">()</span> <span class="p">{}</span>
</code></pre></div></div>

<ol>
  <li>Create <code class="language-plaintext highlighter-rouge">b.rs</code>, non-publicly importing everything from above, so that <code class="language-plaintext highlighter-rouge">fn b()</code> won’t fail to compile because of missing paths from <code class="language-plaintext highlighter-rouge">lib.rs</code>:</li>
</ol>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="k">super</span><span class="p">::</span><span class="o">*</span><span class="p">;</span>
</code></pre></div></div>

<ol>
  <li>Move <code class="language-plaintext highlighter-rouge">fn b()</code> into <code class="language-plaintext highlighter-rouge">b.rs</code>:</li>
</ol>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="k">super</span><span class="p">::</span><span class="o">*</span><span class="p">;</span>

<span class="c1">// moved from `lib.rs`:</span>
<span class="k">pub</span> <span class="k">fn</span> <span class="nf">b</span><span class="p">()</span> <span class="p">{}</span>
</code></pre></div></div>

<p>Congratulations, you just split your code without anyone using it being the wiser.</p>

<p>The second recipe is to move the <code class="language-plaintext highlighter-rouge">b</code> path into the <code class="language-plaintext highlighter-rouge">b</code> module. In this case, we have to make the <code class="language-plaintext highlighter-rouge">b</code> module publicly visible and then remove the <code class="language-plaintext highlighter-rouge">pub use</code> from our <code class="language-plaintext highlighter-rouge">lib.rs</code>:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">mod</span> <span class="n">b</span><span class="p">;</span> <span class="c1">// added `pub` here</span>
<span class="c1">//pub use b::b; &lt;-- no longer needed</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="nf">a</span><span class="p">()</span> <span class="p">{}</span>
</code></pre></div></div>

<p>Voilà, your users won’t be able to call <code class="language-plaintext highlighter-rouge">b()</code> directly anymore unless they import it from <code class="language-plaintext highlighter-rouge">b::b</code>. Now modules are still a messy beast, but at least there are easy steps to take to deal with them.</p>

<h3 id="async">Async</h3>

<p>Rust async is arcane, powerful and still has a good number of rough edges. So unless you’re writing a web service that needs to serve more than fifty thousand concurrent users on a single machine, try to avoid it (remember, we’re not after performance here). However, some libraries you may want to use <em>will</em> require async code. In this case, pick an async runtime (most libraries will work with <a href="https://docs.rs/tokio">tokio</a>, so that seems a safe choice) and</p>

<ul>
  <li>write your functions as you would write a normal function, prepending <code class="language-plaintext highlighter-rouge">async</code> before the <code class="language-plaintext highlighter-rouge">fn</code></li>
  <li>add <code class="language-plaintext highlighter-rouge">.await</code> after every function call, and then remove it again wherever the compiler complains</li>
  <li>avoid potentially locking mechanisms such as <code class="language-plaintext highlighter-rouge">Mutex</code>, <code class="language-plaintext highlighter-rouge">RwLock</code>, and channels. If you absolutely must use one of them, your async runtime will provide replacements that won’t deadlock on you</li>
</ul>

<p>You might still run into weird errors. Don’t say I didn’t warn you.</p>

<h3 id="data-structures">Data Structures</h3>

<p>If I had a penny for each internet troll asking me to write a doubly-linked list in safe Rust (hint: I can, but I don’t need to, there’s one in the standard library), I’d be a very well-off man. So you won’t be surprised to read me suggesting you avoid writing your own data structures. In fact I’ll go one step further and allow you to put off learning what data structures are already provided, because you can get by with only two in the majority of cases: Sequence-like and Lookup-like.</p>

<h4 id="sequences">Sequences</h4>

<p>Whether you call them lists, or arrays, or sequences is of little import. This type of structure is usually there to be filled and later iterated over. For example, if we want to have three items:</p>

<ol>
  <li>start</li>
  <li>more</li>
  <li>end</li>
</ol>

<p>we can put them in a <code class="language-plaintext highlighter-rouge">Vec</code>:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">vec!</span><span class="p">[</span><span class="s">"start"</span><span class="p">,</span> <span class="s">"more"</span><span class="p">,</span> <span class="s">"end"</span><span class="p">]</span>
</code></pre></div></div>

<p>Use <code class="language-plaintext highlighter-rouge">Vec</code>s everywhere you want to sequentially iterate items.</p>

<h4 id="lookups">Lookups</h4>

<p>Those may be called maps, or dictionaries, and allow you to associate a key with a value, to later retrieve the value given the key.</p>

<table>
  <thead>
    <tr>
      <th><strong>KEY</strong></th>
      <th><strong>VALUE</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>recursion</td>
      <td>please look at “recursion”</td>
    </tr>
  </tbody>
</table>

<p>we will use <code class="language-plaintext highlighter-rouge">HashMap</code>s for this case:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">HashMap</span><span class="p">::</span><span class="nf">from</span><span class="p">([</span>
  <span class="p">(</span><span class="s">"recursion"</span><span class="p">,</span> <span class="s">"please look at recursion"</span><span class="p">),</span>
<span class="p">])</span>
</code></pre></div></div>

<p>While hashmaps can be iterated, the main use case is to get a value given a key.</p>

<p>You will be surprised how many programs you can create by just sticking to those two structures.</p>

<h3 id="custom-iterators">Custom Iterators</h3>

<p>Rust iterators are also very powerful, and being able to call their combinator functions (like <code class="language-plaintext highlighter-rouge">filter</code>, <code class="language-plaintext highlighter-rouge">map</code> etc.) you can create new iterators. There are only two points to be mindful of: First, the combinator functions <em>won’t</em> iterate anything, they will just wrap the given iterator into a new iterator type that will modify the iterator’s behavior, and second, the resulting types are usually very hard if not impossible to write out. So you should try avoiding returning such a custom iterator from a function, instead <code class="language-plaintext highlighter-rouge">collect</code>ing into a <code class="language-plaintext highlighter-rouge">Vec</code> or <code class="language-plaintext highlighter-rouge">HashMap</code> (see above). If however your iterator is infinite (yes, that can happen), you obviously cannot collect it. In those rare cases, here’s the magic trick to make it work:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">return_custom_iterator</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="nb">Box</span><span class="o">&lt;</span><span class="k">dyn</span> <span class="nb">Iterator</span><span class="o">&lt;</span><span class="n">Item</span> <span class="o">=</span> <span class="n">MyItemType</span><span class="o">&gt;&gt;</span> <span class="p">{</span>
  <span class="c1">// let's say we filter and map an effectively infinite range of integers</span>
  <span class="k">let</span> <span class="n">iter</span> <span class="o">=</span> <span class="p">(</span><span class="mi">0_usize</span><span class="o">..</span><span class="p">)</span><span class="nf">.filter</span><span class="p">(</span><span class="n">predicate</span><span class="p">)</span><span class="nf">.map</span><span class="p">(</span><span class="n">modify</span><span class="p">);</span>
  <span class="nn">Box</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">iter</span><span class="p">)</span> <span class="k">as</span> <span class="nb">Box</span><span class="o">&lt;</span><span class="k">dyn</span> <span class="nb">Iterator</span><span class="o">&lt;</span><span class="n">Item</span> <span class="o">=</span> <span class="n">MyItemType</span><span class="o">&gt;&gt;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>So now you know what things you can put off learning while you’re being productive in Rust. Have fun!</p>]]></content><author><name>Llogiq</name></author><summary type="html"><![CDATA[This post is based on my RustNationUK ‘24 talk with the same title. The talk video is on youtube, the slides are served from here.]]></summary></entry><entry><title type="html">Semantic Search with Rust, Bert and Qdrant</title><link href="https://llogiq.github.io/2023/11/25/search.html" rel="alternate" type="text/html" title="Semantic Search with Rust, Bert and Qdrant" /><published>2023-11-25T00:00:00+00:00</published><updated>2023-11-25T00:00:00+00:00</updated><id>https://llogiq.github.io/2023/11/25/search</id><content type="html" xml:base="https://llogiq.github.io/2023/11/25/search.html"><![CDATA[<p>First for a bit of backstory: While working at <a href="https://qdrant.tech">Qdrant</a>, I proposed a talk to RustLab Italy 2023, where I would live code a semantic search. Unfortunately, I missed that my window manager (sway) did not support mirroring the screen, so I had to crane my neck to at least partially see what I was doing the whole time, which wasn’t conductive to explaining what I was doing. To everyone who attended the talk and was unhappy about my lackluster explanations, I offer this post, along with my sincere apology.</p>

<p>Before we delve into the code, let me give a short explanation of what semantic search is: Unlike a plain text search, we first transform the input (that may be text or anything else, depending on our transformation) into a vector (usually of floating-point numbers). The transformation has been designed and optimize to give inputs with similar “meaning” nearby values according to some distance metrics. In machine learning circles, we call the transformation “transformer model” and the resulting vector “embedding”. A vector database is a database specialized to search in such a vector space to find nearby points in such a vector space very efficiently.</p>

<p>Now the program I wrote didn’t only search but also set up the database for the search. So the code contained the following steps:</p>

<ol>
  <li>set up the model</li>
  <li>set up the Qdrant client</li>
  <li>match on the first program argument. If “insert”, go to step 4, if “find”, go to step 8, otherwise exit with an error</li>
  <li>(optionally) delete and re-create the collection in Qdrant</li>
  <li>read a JSONL file containing the text along with some data</li>
  <li>iterate over the lines, parse the JSON into a <code class="language-plaintext highlighter-rouge">HashMap</code>, get the text as a value, embed it using the model and collect all of that into a <code class="language-plaintext highlighter-rouge">Vec</code>.</li>
  <li>insert the points into Qdrant in chunks of 100 and exit</li>
  <li>embed the next argument and</li>
  <li>search for it in Qdrant, print the result and exit</li>
</ol>

<p>The reason I used JSONL for importing my points was that I had a file from the Qdrant <a href="https://qdrant.tech/articles/search-as-you-type/">page search</a> lying around that was created by crawling the page, containing text, page links and CSS selectors, so the page could highlight the match. In hindsight, I could have pre-populated my database and the talk would still have been pretty decent and 33% shorter, but this way it was really from scratch, unless you count the <code class="language-plaintext highlighter-rouge">Cargo.toml</code>, which because the network at the conference had been flaky the day before, I had prepopulated with the following dependencies, running <code class="language-plaintext highlighter-rouge">cargo build</code> to pre-download all the things I might need during the talk:</p>

<div class="language-toml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[dependencies]</span>
<span class="py">qdrant-client</span> <span class="p">=</span> <span class="s">"1.6.0"</span>
<span class="nn">rust-bert</span> <span class="o">=</span> <span class="p">{</span> <span class="py">version</span> <span class="p">=</span> <span class="s">"0.21.0"</span><span class="p">,</span> <span class="py">features</span> <span class="p">=</span> <span class="nn">["download-libtorch"]</span> <span class="p">}</span>
<span class="py">serde_json</span> <span class="p">=</span> <span class="s">"1.0.108"</span>
<span class="nn">tokio</span> <span class="o">=</span> <span class="p">{</span> <span class="py">version</span> <span class="p">=</span> <span class="s">"1.34.0"</span><span class="p">,</span> <span class="py">features</span> <span class="p">=</span> <span class="nn">["macros"]</span> <span class="p">}</span>
</code></pre></div></div>

<p>I didn’t use an argument parsing library because the arg parsing I had to do was so primitive (just two subcommands with one argument each) and chosing a crate would invariably have led to questions as to why. <code class="language-plaintext highlighter-rouge">std::env::args</code> is actually OK, folks. Just don’t forget that the zeroeth item is the running program. I also had <code class="language-plaintext highlighter-rouge">main</code> return a <code class="language-plaintext highlighter-rouge">Result&lt;(), Box&lt;dyn std::error::Error&gt;&gt;</code>, which let me use the <code class="language-plaintext highlighter-rouge">?</code> operator almost everywhere, but in one closure (which used <code class="language-plaintext highlighter-rouge">unwrap</code>s because I wasn’t ready to also explain how Rust will collect Iterators of <code class="language-plaintext highlighter-rouge">Result&lt;T, E&gt;</code> into <code class="language-plaintext highlighter-rouge">Resule&lt;Vec&lt;T&gt;, E&gt;</code>).</p>

<p>For the embedding I chose the <a href="https://docs.rs/rust-bert">rust-bert</a> crate, because it is the easiest way to do workable embeddings. The model I used, MiniLMallL12V2, is a variant of the simple and well-tested BERT model with an output size of 384 values and trained to perform semantic search via cosine similarity (both facts will feature later in the code). For the data storage and retrieval, I obviously chose Qdrant, not only for the reason that it’s my former employer, but also because it performs really well, and I could run it locally without any compromise. One thing that was a bit irritating is that rust-bert always embeds a slice of texts and always returns a <code class="language-plaintext highlighter-rouge">Vec&lt;Vec&lt;f32&gt;&gt;</code> where we only need one <code class="language-plaintext highlighter-rouge">Vec&lt;f32&gt;</code>, so I did the <code class="language-plaintext highlighter-rouge">.into_iter().next().unwrap()</code> dance to extract that.</p>

<p>In practice, chosing such a small-ish model allows one to obtain well-performing embedding code, leading to lean resource usage and acceptable recall in most cases. For specialized applications, the model may be re-trained to perform better without any performance penalty. While the trend has been going towards larger and larger models (the so called Large Language Models, or LLMs for short), there is still a lot of mileage in the small ones, and they’re much less cost-intensive.</p>

<p>In Qdrant, the workflow for setting up the database for search is to create the collection, which must at least be configured with the vector size (here 384) and the distance function (here cosine similarity). I left everything else at the default value, which for such a small demo is just fine. Qdrant, being the speed devil among the vector databases, has a lot of knobs to turn to make it, in the best Rust tradition, run blazingly fast.</p>

<p>In the code I removed the collection first, so I would delete any failed attempts of setting up the collection before, just as a safety measure. Then I embedded all the JSONL objects, converted them into Qdrant’s <code class="language-plaintext highlighter-rouge">PointStruct</code>s and upserted (a term that means a mixture of inserting and updating) them in batches of 100s, another safety measure to steer widely clear of any possible timeouts that might have derailed the demo. For the search, the minimum parameters are the vector, the limit (one can also use a distance threshold instead) and a <code class="language-plaintext highlighter-rouge">with_payload</code> argument that will make Qdrant actually retrieve the payload. I cannot explain why the latter is not the default, but it’s not too painful to ask for it. I can only conjecture that some users will only care for IDs, having downstream data sources from where to retrieve their payloads.</p>

<p>Because Qdrant’s API is async, I also pulled in tokio with the <code class="language-plaintext highlighter-rouge">main</code> macro enabled. This way, I had to <code class="language-plaintext highlighter-rouge">await</code> a few things, but it otherwise didn’t detract from the clarity of the code, which is nice.</p>

<p>I hope that clears things up. Here is the code 100% as it was at the end of my talk:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">collections</span><span class="p">::</span><span class="n">HashMap</span><span class="p">;</span>

<span class="k">use</span> <span class="nn">qdrant_client</span><span class="p">::{</span>
    <span class="nn">prelude</span><span class="p">::</span><span class="o">*</span><span class="p">,</span>
    <span class="nn">qdrant</span><span class="p">::{</span><span class="n">VectorParams</span><span class="p">,</span> <span class="n">VectorsConfig</span><span class="p">},</span>
<span class="p">};</span>
<span class="k">use</span> <span class="nn">rust_bert</span><span class="p">::</span><span class="nn">pipelines</span><span class="p">::</span><span class="nn">sentence_embeddings</span><span class="p">::</span><span class="n">SentenceEmbeddingsBuilder</span><span class="p">;</span>

<span class="nd">#[tokio::main]</span>
<span class="k">async</span> <span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="p">(),</span> <span class="nb">Box</span><span class="o">&lt;</span><span class="k">dyn</span> <span class="nn">std</span><span class="p">::</span><span class="nn">error</span><span class="p">::</span><span class="n">Error</span><span class="o">&gt;&gt;</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">model</span> <span class="o">=</span> <span class="nn">SentenceEmbeddingsBuilder</span><span class="p">::</span><span class="nf">remote</span><span class="p">(</span>
        <span class="nn">rust_bert</span><span class="p">::</span><span class="nn">pipelines</span><span class="p">::</span><span class="nn">sentence_embeddings</span><span class="p">::</span><span class="nn">SentenceEmbeddingsModelType</span><span class="p">::</span><span class="n">AllMiniLmL12V2</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="nf">.create_model</span><span class="p">()</span><span class="o">?</span><span class="p">;</span>
    <span class="k">let</span> <span class="n">client</span> <span class="o">=</span> <span class="nn">QdrantClient</span><span class="p">::</span><span class="nf">from_url</span><span class="p">(</span><span class="o">&amp;</span><span class="nn">std</span><span class="p">::</span><span class="nn">env</span><span class="p">::</span><span class="nf">var</span><span class="p">(</span><span class="s">"QDRANT_URL"</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">())</span>
        <span class="nf">.with_api_key</span><span class="p">(</span><span class="nn">std</span><span class="p">::</span><span class="nn">env</span><span class="p">::</span><span class="nf">var</span><span class="p">(</span><span class="s">"QDRANT_API_KEY"</span><span class="p">))</span>
        <span class="nf">.build</span><span class="p">()</span><span class="o">?</span><span class="p">;</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">args</span> <span class="o">=</span> <span class="nn">std</span><span class="p">::</span><span class="nn">env</span><span class="p">::</span><span class="nf">args</span><span class="p">();</span>
    <span class="k">match</span> <span class="n">args</span><span class="nf">.nth</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="nf">.as_deref</span><span class="p">()</span> <span class="p">{</span>
        <span class="nf">Some</span><span class="p">(</span><span class="s">"insert"</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
            <span class="k">let</span> <span class="n">_</span> <span class="o">=</span> <span class="n">client</span><span class="nf">.delete_collection</span><span class="p">(</span><span class="s">"points"</span><span class="p">)</span><span class="k">.await</span><span class="p">;</span>
            <span class="n">client</span>
                <span class="nf">.create_collection</span><span class="p">(</span><span class="o">&amp;</span><span class="n">CreateCollection</span> <span class="p">{</span>
                    <span class="n">collection_name</span><span class="p">:</span> <span class="s">"points"</span><span class="nf">.into</span><span class="p">(),</span>
                    <span class="n">vectors_config</span><span class="p">:</span> <span class="nf">Some</span><span class="p">(</span><span class="n">VectorsConfig</span> <span class="p">{</span>
                        <span class="n">config</span><span class="p">:</span> <span class="nf">Some</span><span class="p">(</span><span class="nn">qdrant_client</span><span class="p">::</span><span class="nn">qdrant</span><span class="p">::</span><span class="nn">vectors_config</span><span class="p">::</span><span class="nn">Config</span><span class="p">::</span><span class="nf">Params</span><span class="p">(</span>
                            <span class="n">VectorParams</span> <span class="p">{</span>
                                <span class="n">size</span><span class="p">:</span> <span class="mi">384</span><span class="p">,</span>
                                <span class="n">distance</span><span class="p">:</span> <span class="nn">Distance</span><span class="p">::</span><span class="n">Cosine</span> <span class="k">as</span> <span class="nb">i32</span><span class="p">,</span>
                                <span class="o">..</span><span class="nn">Default</span><span class="p">::</span><span class="nf">default</span><span class="p">()</span>
                            <span class="p">},</span>
                        <span class="p">)),</span>
                    <span class="p">}),</span>
                    <span class="o">..</span><span class="nn">Default</span><span class="p">::</span><span class="nf">default</span><span class="p">()</span>
                <span class="p">})</span>
                <span class="k">.await</span><span class="o">?</span><span class="p">;</span>
            <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="n">input_file</span><span class="p">)</span> <span class="o">=</span> <span class="n">args</span><span class="nf">.next</span><span class="p">()</span> <span class="k">else</span> <span class="p">{</span>
                <span class="nd">eprintln!</span><span class="p">(</span><span class="s">"usage: semantic insert &lt;path/to/file&gt;"</span><span class="p">);</span>
                <span class="k">return</span> <span class="nf">Ok</span><span class="p">(());</span>
            <span class="p">};</span>
            <span class="k">let</span> <span class="n">contents</span> <span class="o">=</span> <span class="nn">std</span><span class="p">::</span><span class="nn">fs</span><span class="p">::</span><span class="nf">read_to_string</span><span class="p">(</span><span class="n">input_file</span><span class="p">)</span><span class="o">?</span><span class="p">;</span>
            <span class="k">let</span> <span class="k">mut</span> <span class="n">id</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
            <span class="k">let</span> <span class="n">points</span> <span class="o">=</span> <span class="n">contents</span>
                <span class="nf">.lines</span><span class="p">()</span>
                <span class="nf">.map</span><span class="p">(|</span><span class="n">line</span><span class="p">|</span> <span class="p">{</span>
                    <span class="k">let</span> <span class="n">payload</span><span class="p">:</span> <span class="n">HashMap</span><span class="o">&lt;</span><span class="nb">String</span><span class="p">,</span> <span class="n">Value</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nn">serde_json</span><span class="p">::</span><span class="nf">from_str</span><span class="p">(</span><span class="n">line</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span>
                    <span class="k">let</span> <span class="n">text</span> <span class="o">=</span> <span class="n">payload</span><span class="nf">.get</span><span class="p">(</span><span class="s">"text"</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">()</span><span class="nf">.to_string</span><span class="p">();</span>
                    <span class="k">let</span> <span class="n">embeddings</span> <span class="o">=</span> <span class="n">model</span>
                        <span class="nf">.encode</span><span class="p">(</span><span class="o">&amp;</span><span class="p">[</span><span class="n">text</span><span class="p">])</span>
                        <span class="nf">.unwrap</span><span class="p">()</span>
                        <span class="nf">.into_iter</span><span class="p">()</span>
                        <span class="nf">.next</span><span class="p">()</span>
                        <span class="nf">.unwrap</span><span class="p">()</span>
                        <span class="nf">.into</span><span class="p">();</span>
                    <span class="n">id</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
                    <span class="n">PointStruct</span> <span class="p">{</span>
                        <span class="n">id</span><span class="p">:</span> <span class="nf">Some</span><span class="p">(</span><span class="n">id</span><span class="nf">.into</span><span class="p">()),</span>
                        <span class="n">payload</span><span class="p">,</span>
                        <span class="n">vectors</span><span class="p">:</span> <span class="nf">Some</span><span class="p">(</span><span class="n">embeddings</span><span class="p">),</span>
                        <span class="o">..</span><span class="nn">Default</span><span class="p">::</span><span class="nf">default</span><span class="p">()</span>
                    <span class="p">}</span>
                <span class="p">})</span>
                <span class="py">.collect</span><span class="p">::</span><span class="o">&lt;</span><span class="nb">Vec</span><span class="o">&lt;</span><span class="n">_</span><span class="o">&gt;&gt;</span><span class="p">();</span>
            <span class="k">for</span> <span class="n">batch</span> <span class="k">in</span> <span class="n">points</span><span class="nf">.chunks</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">let</span> <span class="n">_</span> <span class="o">=</span> <span class="n">client</span>
                    <span class="nf">.upsert_points</span><span class="p">(</span><span class="s">"points"</span><span class="p">,</span> <span class="n">batch</span><span class="nf">.to_owned</span><span class="p">(),</span> <span class="nb">None</span><span class="p">)</span>
                    <span class="k">.await</span><span class="o">?</span><span class="p">;</span>
                <span class="nd">print!</span><span class="p">(</span><span class="s">"."</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="nd">println!</span><span class="p">()</span>
        <span class="p">}</span>
        <span class="nf">Some</span><span class="p">(</span><span class="s">"find"</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
            <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="n">text</span><span class="p">)</span> <span class="o">=</span> <span class="n">args</span><span class="nf">.next</span><span class="p">()</span> <span class="k">else</span> <span class="p">{</span>
                <span class="nd">eprintln!</span><span class="p">(</span><span class="s">"usage: semantic insert &lt;path/to/file&gt;"</span><span class="p">);</span>
                <span class="k">return</span> <span class="nf">Ok</span><span class="p">(());</span>
            <span class="p">};</span>
            <span class="k">let</span> <span class="n">vector</span> <span class="o">=</span> <span class="n">model</span><span class="nf">.encode</span><span class="p">(</span><span class="o">&amp;</span><span class="p">[</span><span class="n">text</span><span class="p">])</span><span class="o">?</span><span class="nf">.into_iter</span><span class="p">()</span><span class="nf">.next</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">();</span>
            <span class="k">let</span> <span class="n">result</span> <span class="o">=</span> <span class="n">client</span>
                <span class="nf">.search_points</span><span class="p">(</span><span class="o">&amp;</span><span class="n">SearchPoints</span> <span class="p">{</span>
                    <span class="n">collection_name</span><span class="p">:</span> <span class="s">"points"</span><span class="nf">.into</span><span class="p">(),</span>
                    <span class="n">vector</span><span class="p">,</span>
                    <span class="n">limit</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
                    <span class="n">with_payload</span><span class="p">:</span> <span class="nf">Some</span><span class="p">(</span><span class="k">true</span><span class="nf">.into</span><span class="p">()),</span>
                    <span class="o">..</span><span class="nn">Default</span><span class="p">::</span><span class="nf">default</span><span class="p">()</span>
                <span class="p">})</span>
                <span class="k">.await</span><span class="o">?</span><span class="p">;</span>
            <span class="nd">println!</span><span class="p">(</span><span class="s">"{result:#?}"</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="n">_</span> <span class="k">=&gt;</span> <span class="p">{</span>
            <span class="nd">eprintln!</span><span class="p">(</span><span class="s">"usage: semantic insert &lt;path/to/file&gt;"</span><span class="p">);</span>
            <span class="k">return</span> <span class="nf">Ok</span><span class="p">(());</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="nf">Ok</span><span class="p">(())</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name>Llogiq</name></author><summary type="html"><![CDATA[First for a bit of backstory: While working at Qdrant, I proposed a talk to RustLab Italy 2023, where I would live code a semantic search. Unfortunately, I missed that my window manager (sway) did not support mirroring the screen, so I had to crane my neck to at least partially see what I was doing the whole time, which wasn’t conductive to explaining what I was doing. To everyone who attended the talk and was unhappy about my lackluster explanations, I offer this post, along with my sincere apology.]]></summary></entry><entry><title type="html">bytecount now ARMed and Web-Assembled</title><link href="https://llogiq.github.io/2023/10/02/armed.html" rel="alternate" type="text/html" title="bytecount now ARMed and Web-Assembled" /><published>2023-10-02T00:00:00+00:00</published><updated>2023-10-02T00:00:00+00:00</updated><id>https://llogiq.github.io/2023/10/02/armed</id><content type="html" xml:base="https://llogiq.github.io/2023/10/02/armed.html"><![CDATA[<p>It’s been some time since I wrote on this blog. I did blog at other venues and worked a lot, which left too little time to write here. So this is “just” a release announcement for my <a href="https://github.com/llogiq/bytecount">bytecount</a> crate. And also a bit of talk around SIMD.</p>

<p>First things first, bytecount as of version 0.6.4 now supports ARM natively on stable using NEON intrinsics. Yay! While bytecount had unstable ARM SIMD support under the <code class="language-plaintext highlighter-rouge">generic-simd</code> feature (which used the nightly only <a href="https://docs.rs/packed-simd"><code class="language-plaintext highlighter-rouge">packed-simd</code></a> crate that mimics the upcoming <code class="language-plaintext highlighter-rouge">std::simd</code> API), for stable Rust ARM users were restricted to 64-bit integer based pseudo-SIMD, which used some bit twiddling hacks to simulate SIMD with scalar integer operations.</p>

<p>One reason for this is that for the longest time, the only ARM CPU I owned was in my mobile phone, a cheap-ish Android device that nonetheless had the 4+4 core ARM CPU setup that is now so common. Many folks probably don’t know, but there’s <a href="https://termux.dev/">Termux</a>, an Android terminal emulator + environment which has included Rust for quite some time. So I ran a small benchmark trying to count through 1 gigabyte of random bytes, which came in at around 6G/s. Not too bad, given that it used bit twiddling instead of actual SIMD.</p>

<p>Termux only supplies a stable Rust toolchain though, but that didn’t stop me from also benchmarking the generic-SIMD version. Using the <code class="language-plaintext highlighter-rouge">RUSTC_BOOTSTRAP</code> env variable lets one use unstable features on stable Rust (beware: This is not guaranteed to be safe for any use!), so I did the same benchmark with our generic SIMD implementation. The result came it at 11G/s.</p>

<p>As I recently had some motivation to get into SIMD stuff again, I set out to port bytecount to ARM. The algorithm is mostly the same; the only difference is the data layout: Where Intel’s AVX2 has 256 bit registers, and AVX-512 even 512 bit ones, NEON is still at 128 bits. However, there are enough registers available to check 4 of them for equality while still having enough to keep the count, so that’s what I did.</p>

<p>The implementation work was sped up considerably by me getting a MacBook Pro with an M2 from <a href="https://flatfile.com/">Flatfile</a> for some optimization work which they generously let me use to test bytecount.</p>

<p>The final implementation is equal up to a small bit faster than the generic-SIMD one, because I can get away with a few less instructions in certain cases.</p>

<p>So bolstered by this success, I went on to tackle WebAssembly support next. The <code class="language-plaintext highlighter-rouge">simd128</code> intrinsics in <code class="language-plaintext highlighter-rouge">core::arch::wasm32</code> are actually quite similar to what you’d find on ARM, but named differently, plus for some reason there doesn’t seem to be a horizontal addition operation. However, that lack is compensated for by a number of widening addition ops that I could use to combine multiple values, so in the end the code looks quite similar to the <code class="language-plaintext highlighter-rouge">aarch64</code> one.</p>

<p>This one got into the current version 0.6.5. So if you use bytecount, upgrade to enjoy a very healthy speed boost on ARM and on the web.</p>]]></content><author><name>Llogiq</name></author><summary type="html"><![CDATA[It’s been some time since I wrote on this blog. I did blog at other venues and worked a lot, which left too little time to write here. So this is “just” a release announcement for my bytecount crate. And also a bit of talk around SIMD.]]></summary></entry><entry><title type="html">Rust in Rhymes II explainer</title><link href="https://llogiq.github.io/2023/02/19/rhymes2.html" rel="alternate" type="text/html" title="Rust in Rhymes II explainer" /><published>2023-02-19T00:00:00+00:00</published><updated>2023-02-19T00:00:00+00:00</updated><id>https://llogiq.github.io/2023/02/19/rhymes2</id><content type="html" xml:base="https://llogiq.github.io/2023/02/19/rhymes2.html"><![CDATA[<p>Update: The talk video is now <a href="https://www.youtube.com/watch?v=rwH57zNm-A8">on YouTube</a>. The slides are <a href="/talks/rhymes2.html">here</a>.</p>

<p>As promised, here are some links and explanations to all the rhymes. First of all, the rap lyrics:</p>

<blockquote>
  <p>My boss said this time I should try a Rust rap<br />
but my rapping skills are positively crap,<br />
Even so, Rust and rapping are a thrilling combination<br />
with which to introduce my talk to RustNation</p>

  <p>In London we meet, and the workshops are complete,<br />
so now you people have my talk approaching at full speed,<br />
wakey, wakey, here we are, and I hope you will get<br />
entertainment out of this, or some knowledge instead</p>

  <p>Of course I won’t start right away with pages full of code<br />
I’ll start with a few stories, and then switch to teaching mode.<br />
I hope that by the end you’ll know a bit more than before<br />
but otherwise enjoy the show, I’ll try hard not to bore.</p>

  <p>I could go on and on about how many talks are lacking<br />
the silliness, the fireworks, just going straight to hacking,<br />
so have some whimsy, have some rhyme, some bars, a little flow<br />
we’re here for having a good time, by golly, HERE WE GO!</p>
</blockquote>

<p>In the first bars, we have the very rare self-diss, followed by some flow to announce our whereabouts. Then I go on to establish what I offer in the talk. Again “I’ll try hard not to bore” is a sarcastic self-diss, because of course no one was bored in the slightest. Finally a <em>very</em> tame diss against other talks that lack the entertainment value of this one. After all, I didn’t want to appear too aggressive during a Rust conference. Of course we will have some echoes of this later.</p>

<p>On to the rhymes:</p>

<blockquote>
  <p>Welcome to this talk, dear Rustacean,<br />
You’re hailing from many a nation,<br />
And you’ve waited some time<br />
for most uplifting rhyme<br />
so let’s start the edification!</p>
</blockquote>

<p>A cordial welcome to the audience and setting some expectations for the talk.</p>

<blockquote>
  <p>Someone from outside London got<br />
his head in my hotel too hot<br />
and the fire alarm<br />
worked this night like a charm<br />
and woke everyone up on the spot.</p>
</blockquote>

<p>I would like to have you know that I wrote the rhyme with the fire alarm after experiencing this exact thing: Someone activated the fire alarm in the hotel the RustNation speakers where sleeping in at about 2AM in the morning. So it took a <em>lot</em> of coffee for me to be mostly coherent during the talk.</p>

<blockquote>
  <p>Since I last talked in rhyme about Rust<br />
I worked in Java and just<br />
three years ago moved<br />
to Rust which behooved<br />
me to go in completely. Or bust.</p>
</blockquote>

<p>This is referencing my first <a href="https://www.youtube.com/watch?v=JR6aHXlRAOM">“Rust in Rhymes” talk</a>, where after declaring that the talk was about Rust, I showed a Java logo in the second slide as a half-joke. I was actually working in Java during that time.</p>

<blockquote>
  <p>JFrog, from the name you suppose<br />
they used Java a while, and now those<br />
folks who work to contain<br />
errors in our supply chain<br />
they’re here getting their five lines of prose.</p>
</blockquote>

<p>The Rust foundation has <a href="https://foundation.rust-lang.org/news/2022-09-13-rust-foundation-establishes-security-team">teamed up with JFrog</a> to evaluate and improve the crates.io/cargo ecosystem supply chain security. Good for us, I say!</p>

<blockquote>
  <p>Nowadays I write Rust to help you<br />
getting into Rust code old and new<br />
It will show you the code<br />
and in semantic mode<br />
it will also explain what’s it do.</p>
</blockquote>

<p>Please have a look at <a href="https://bloop.ai">bloop</a> where we do code search with a semantic twist if you want to know more.</p>

<blockquote>
  <p>Rust’s steadily been on the rise,<br />
to no Rustacean’s surprise<br />
many people take note<br />
of our bug-antidote<br />
without any speed compromise.</p>
</blockquote>

<p>The slide has a google trends chart that shows the Rust community experiencing exponential growth. The rhyme also exhorts the quality of our language which combines correctness with performance without compromising on either.</p>

<blockquote>
  <p>Rust has now made it on TV<br />
where hackers compiling you see<br />
so join us and write<br />
Rust code with shades by night<br />
if you want as cool as them to be.</p>
</blockquote>

<p>I must admit that I found this claim on twitter and didn’t bother to look up the TV show. The slide shows what I presume is a frame of the show that was part of the tweet. Still, it makes for a good rhyme, don’t you think?</p>

<blockquote>
  <p>Sometimes in Rust you will need<br />
more time ‘til your code is complete<br />
Where in Python you’re “done”<br />
When the code is still wrong<br />
And on perf it’ll never compete.</p>
</blockquote>

<p>The slides show two persons, one having solved a puzzle with square pieces incorrectly, while the other is still missing a few pieces for a correctly solved puzzle, alluding to the fact that Rust won’t let you cut any corners with your solution, while dynamically typed languages will be far more lenient, leading to potentially costly bugs later down the road.</p>

<blockquote>
  <p>There once was a runtime called bun<br />
who thought beating Rust perf was fun.<br />
Rustaceans said “please<br />
bench with dash-dash-release”<br />
Now look at the benchmarks…they’re gone.</p>
</blockquote>

<p>The <a href="https://bun.sh/">bun</a> javascript runtime written in Zig arrived with a splash, boasting impressive benchmark claims (“faster than Rust!”) that didn’t hold up well to scrutinity (they had compiled the Rust benchmarks in debug mode). Once Rustaceans pointed that out, the benchmarks were quickly removed without further comment.</p>

<blockquote>
  <p>With Rust and Ada some find<br />
them competing for shares of our mind<br />
Ferrous and AdaCore<br />
thought “we might achieve more<br />
by applying our powers combined!”</p>
</blockquote>

<p>Ferrous Systems and AdaCore <a href="https://ferrous-systems.com/blog/ferrous-systems-adacore-joining-forces">announced</a> their partnership to promote the use of both Ada and Rust in safety-critical fields like automotive, aerospace, medicine-tech etc. The rhyme celebrates this announcement while arguing for more unity between developers of different language communities.</p>

<blockquote>
  <p>The Ferrocene scheme will contain<br />
necessary steps to obtain<br />
a Rust certified to<br />
ISO 26262<br />
Let’s hope the effort’s not in vain.</p>
</blockquote>

<p>Ferrous Systems announced working on certification of a Rust compiler that will lag behind the most current version and hopefully be allowed to be used in those safety-critical fields where certification is often required by law, bringing safety, performance and productivity benefits to those working with it.</p>

<blockquote>
  <p>Some functional zealots will tell<br />
that mutation can never go well<br />
but if we never share<br />
what’s mutated, we spare<br />
us from data race debugging hell.</p>
</blockquote>

<p>This rhyme notes that in functional programming, you avoid mutation altogether, even if it’s not mutation itself that is the problem, but only the overlap of mutated and shared state. Rust lets us rule out this overlap without the cost of avoiding mutation. This has led some functional purists to decry Rust as inferior, which of course we know is just more gatekeeping.</p>

<blockquote>
  <p>Some coders will still look in vain<br />
for reasons with C to remain<br />
but “freedom” to crash<br />
is an unholy mash<br />
of stupidity, hubris and pain.</p>
</blockquote>

<p>Coming from the other side, the slide cites a redditor who claims to “use C for freedom” and asks others to follow that example. However, the perceived “freedom” here <em>is</em> mostly the freedom to get undefined behavior, as in practice the constraints Rust places upon us do not keep us from writing delight- and useful programs at all. So I’m poking some fun at this really bad take.</p>

<blockquote>
  <p>Bjarne wants C++ to sprout<br />
some memory safety without<br />
needing more than some lint<br />
Well, good luck, and a hint:<br />
That’s what Rust’s type system is all about.</p>
</blockquote>

<p>This alludes to a discussion led by Bjarne Stroustrup, the original creator of C++, who claims they can get the benefit of memory safety with “some additional static checks”, despite the fact that both the Firefox and the Chrome teams tried to make C++ safer and both have turned to Rust now instead. Even with the past showing that this endeavor hasn’t seen much success in the past, I sincerely wish the C++ committee the utmost success in this, as improving the memory safety of a great many programs will be a fantastic boon to our digital security.</p>

<blockquote>
  <p>The Rust foundation is here<br />
not to mandate, prescribe, boss or steer,<br />
they support from within<br />
Rustaceans for the win,<br />
so let’s everyone give them some cheer!</p>
</blockquote>

<p>I wrote this rhyme after talking to Rebecca Rumbul at the foundation stand in the conference lobby, mostly paraphrasing what she said to me. I applaud the work the foundation has done so far, and the audience audibly concurred.</p>

<blockquote>
  <p>By this time you probably just<br />
have heard elsewhere of <a href="https://this-week-in-rust.org">This Week in Rust</a>.<br />
I collect crate and quote<br />
and PRs people wrote<br />
for our main source of Rust news you trust.</p>
</blockquote>

<p>I couldn’t stop myself from doing some advertisement of one of the community projects I’m involved in, also outlining my engagement with the project.</p>

<blockquote>
  <p>There was a certain Rust PR<br />
that some folks think went too far<br />
make internal code omit<br />
certain numbers, to wit<br />
in hex they’d fill the swearing jar.</p>
</blockquote>

<p>The hex numbers are mostly things like <code class="language-plaintext highlighter-rouge">0xB00B1E5</code> which is juvenile and doesn’t fit in the professional setting of the standard library documentation. As there is no useful reason to have those numbers in the code whatsoever, the <a href="https://github.com/rust-lang/rust/pull/92469">PR</a> in question replaced them with plain consecutive digit ranges like <code class="language-plaintext highlighter-rouge">0x1234</code> and introduced a <code class="language-plaintext highlighter-rouge">tidy</code> check that disallowed re-adding those numbers in the future.</p>

<blockquote>
  <p>In Rust you will gladly embrace<br />
the compiler’s help if you face<br />
a refactor so hairy<br />
your brain’s feeling airy<br />
It’ll keep track of time, stuff and space.</p>
</blockquote>

<p>I’ll get back to the reasons later, but this rhyme airs my feelings towards refactoring in Rust, where the compiler will often be <em>very</em> helpful, often not only showing what needs to be changed, but also how.</p>

<blockquote>
  <p>We all have learned that a bad<br />
craftsman blamed the tools that they had<br />
but Rust will well teach<br />
us it’s not out of reach<br />
to build far better tooling instead.</p>
</blockquote>

<p>Rust’s tooling is top notch in many places. Kudos to the respoective teams. The rhyme still reminds us that the work here is never really done though, and exhorts us to think about how we can improve our tooling even further, then do the work to get there.</p>

<blockquote>
  <p>For Androids it’s been a must<br />
To be shiny and free of Rust<br />
But to safety’s advance<br />
Google loosened their stance<br />
hoping blue tooths no longer combust.</p>
</blockquote>

<p>Some word play here; both for Rust (the language, here <a href="#" title="I know that Rust is named after a fungus, and said no less in my RustFest Zürich talk.">mixed up with iron oxide</a>) and Android (the operating system, here taken as a humanoid machine). The last line references the first part of Android that got reimplemented in Rust, which is the bluetooth stack, and the security problems of the previous implementation.</p>

<blockquote>
  <p>Now in Android version thirteen<br />
there’s more Rust than ever has been.<br />
But now that they wrote<br />
millions Rust lines of code<br />
memory bugs remain to be seen.</p>
</blockquote>

<p>I had read a <a href="https://security.googleblog.com/2021/04/rust-in-android-platform.html">blog post</a> by Jeffrey Vander Stoep, who found that unlike their C++ code, which contained roughly one vulnerability per 1000 lines of code (which by the way makes Android’s code top of the line in the industry), there hasn’t been found any memory safety-related vulnerability in their Rust code so far in more than a million lines of code!</p>

<blockquote>
  <p>Meanwhile Rust in Linux progressed<br />
to the point where it’s put to the test,<br />
The patch builds the foundation<br />
for the carcination<br />
of drivers. This stuff is the best!</p>
</blockquote>

<p>The <a href="https://github.com/Rust-for-Linux">Rust for Linux patches</a> have been merged into mainline and there are some experimental drivers using it already. Those who have been benchmarked show similar performance to their C counterparts despite not having been thoroughly optimized, which is very promising.</p>

<blockquote>
  <p>Asahi Lina just took<br />
the GPU of a Macbook<br />
to write drivers in Rust<br />
for Linux, one must<br />
think this is an elite-worthy look.</p>
</blockquote>

<blockquote>
  <p>15 thousand lines of Rust code later<br />
Despite claims from many a hater<br />
Lina’s work here shows<br />
How kernel dev goes<br />
Without any in-GPU crater.</p>
</blockquote>

<p>Those two rhymes refer to the <a href="https://github.com/AsahiLinux/gpu">M1 GPU drivers project</a>, which has working drivers written in Rust. The author so far has experienced very little problems during the implementation, presumably because of the strong type system and memory safety guarantees the language affords. Contrast this with the development experience of other GPU drivers, which still suffer from frequent crashes and hard to debug memory problems.</p>

<blockquote>
  <p>When your Rust goes embedded, you’ll find<br />
no need to leave abstractions behind,<br />
with a HAL (not the boor<br />
who won’t open the door)<br />
you get a piece of low-level mind.</p>
</blockquote>

<p>I must admit that I’m not that knowledgeable around embedded Rust. That said, I’ve seen some hardware abstraction layers, which offer really nice abstractions on top of the often complex and arcane hardware APIs. The abbreviation of hardware abstraction layer is HAL, which I associated with HAL 9000, the antagonistic AI from Arthur C. Clarke’s “Space Oddyssey” series, who is most certainly the opposite of helpful. The slide for this rhyme shows the famous red camera eye from the 1968 film.</p>

<blockquote>
  <p>When asked curl in Rust to rewrite<br />
Daniel Stenberg said that he might<br />
But he won’t rearrange<br />
everything in one change<br />
So it sure won’t be done overnight.</p>
</blockquote>

<p>Daniel Stenberg, the author of the popular HTTP client tool curl has now introduced Rust into the code base. He’s quite pragmatic with it though, and has so far carefully and considerately introduced Rust.</p>

<blockquote>
  <p>Rust would be far too arcane<br />
Without Esteban keeping us sane<br />
for the message on error<br />
sparks joy and not terror<br />
though the steep learning curve does remain.</p>
</blockquote>

<p>Esteban Küber (I hope that this is the right spelling) is leading the diagnostics working group, which has made rustc produce the most delightful error and warning messages ever known by programmerkind. The rhyme imagines a dystopian alternate reality where Rust error messages would be as incoherent and unhelpful as their counterparts in some other languages’ compiler implementation (although I should note that a rising tide lifts all boats; many of those implementations have improved following Rustc’s example). The final line reminds us that while Rust has gotten far easier to learn in the last 7 years since 1.0.0, we can still improve on that front.</p>

<blockquote>
  <p>When rustc surprisingly showed<br />
it converts a large int to a float<br />
Mara Bos went complete-<br />
ly berserk for more speed<br />
the compiler’s builtins to demote.</p>
</blockquote>

<p>Mara Bos who is leading the libs team and is the author of the phantastic <a href="https://marabos.nl/atomics">“Rust Atomics and Locks”</a> book, had <a href="https://blog.m-ou.se/floats/">blogged</a> about <a href="https://blog.m-ou.se/floats/">a both entertaining and educational story</a> which led her to reimplement the int-to-float conversion routines for Rust’s compiler builtins.</p>

<blockquote>
  <p>Nyx toolkit brings Rust into space<br />
as other tools they replace<br />
at speed they simulate<br />
how craft accelerate<br />
at this scale you can’t let data race.</p>
</blockquote>

<p>Given that the industry sending people into space is usually quite conservative in what tech they use, it was surprising to me to see a <a href="https://nyxspace.com">Rust application</a> in astrodynamics. Yet here we are.</p>

<blockquote>
  <p>nushell builds on Rust to be able<br />
to work with data by the table<br />
and letting you pipe<br />
your data with type.<br />
It’s elegant, mighty and stable.</p>
</blockquote>

<p>I use <a href="https://nushell.sh">nushell</a> as my main shell on windows (haven’t made the switch on Linux yet though), and I find it much more usable than PowerShell.</p>

<blockquote>
  <p>If your code depends at any rate<br />
on the so-called “rustdecimal” crate<br />
and you also did choose<br />
Gitlab CI to use<br />
Check your systems before it’s too late.</p>
</blockquote>

<p>There was a <a href="https://rustsec.org/advisories/RUSTSEC-2022-0042.html">security advisory</a> from the rustsec group. Someone had typosquatted the rust-decimal (note the dash?) crate by copying it and including some malicious code in the build script that installed a backdoor when finding that it ran on GitLab CI.</p>

<blockquote>
  <p>Rust enums enjoy much attention,<br />
well deserved, so allow me to mention<br />
with <code class="language-plaintext highlighter-rouge">match</code> they enable<br />
us to code more stable<br />
and refactor to match our intention.</p>
</blockquote>

<p>Here the main joke was the slide which showed an <code class="language-plaintext highlighter-rouge">enum Talk { Boring(TechnicalStuff), Interesting(Rhymes) }</code>, echoing the diss from my Rap. The rhyme itself alludes to the fact that we have exhaustiveness checking for matching enums, and so changing them will give us compiler errors at the exact places we need to change to continue our refactor.</p>

<blockquote>
  <p>Now if you just need a bool<br />
from a match, this macro is cool<br />
unless you must take out<br />
of your type, it’s about<br />
the best match code-size reducing tool.</p>
</blockquote>

<p>Having written a good number of <code class="language-plaintext highlighter-rouge">if let ... { true } else { false }</code> during my work on clippy, I’m totally happy to have the <code class="language-plaintext highlighter-rouge">matches!</code> macro at my disposal, so here it got its well-deserved rhyme. The code on the slide just matches the enum of the previous one.</p>

<blockquote>
  <p>With traits your code faces ascension<br />
by internal Rust type extension<br />
where you tell everything<br />
how to do anything<br />
so allow me the pay-offs to mention.</p>
</blockquote>

<p>This rhyme refers to the power of extension traits which allow us to implement behavior for types defined elsewhere. I wrote this rhyme on short notice after <a href="https://twitter.com/loige">Luciano Mammino</a>’s talk where he spoke about his delight at being able to implement his own iterator methods.</p>

<blockquote>
  <p>When in Rust code macros you use<br />
I worry a hard way you choose<br />
for debugging it might<br />
become heck of a fight<br />
which means more time later you lose.</p>
</blockquote>

<p>I’ve written <a href="https://github.com/llogiq/flamer">quite</a> <a href="https://github.com/llogiq/overflower">some</a> <a href="https://github.com/llogiq/mutagen">macros</a> during my work in Rust, and I’m keenly aware that they can make debugging much harder. So here’s a warning to the unwary to think twice if they need a macro for something. Often, generics can do the trick, or the duplication that a macro is built to prevent might be the lesser evil to choose.</p>

<blockquote>
  <p>In Rust when a thing you mutate<br />
And desire to annotate<br />
that it no longer should<br />
change you easily could<br />
<code class="language-plaintext highlighter-rouge">let</code> it be itself until its fate.</p>
</blockquote>

<p>This rhyme shows how one can make use of <code class="language-plaintext highlighter-rouge">let</code> bindings and shadowing to restrict mutability of a value to a certain area of the code. Keeping values immutable often makes it easier to follow the code, because you don’t have to worry that they change from under your nose. The code on the slide is as follows:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="k">mut</span> <span class="n">x</span> <span class="o">=</span> <span class="nf">get_x</span><span class="p">();</span>
<span class="nf">mutate_x</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">x</span><span class="p">);</span>
<span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="n">x</span><span class="p">;</span>
<span class="c1">// x is now immutable</span>
</code></pre></div></div>

<blockquote>
  <p>Last time I showed how to break Rust<br />
but this easter egg is now dust,<br />
here’s how out of a scope<br />
you can jump without rope<br />
which is useful and far more robust.</p>
</blockquote>

<p>The first two lines reference the RustFest Zurich talk again, which had a rhyme about a Rust easter egg where if you wrote <code class="language-plaintext highlighter-rouge">loop { break rust; }</code>, the compiler would output a funny message. Now in the meantime, Rust has allowed <code class="language-plaintext highlighter-rouge">break</code> to be used outside loops as a limited form of <code class="language-plaintext highlighter-rouge">goto</code>. The slide shows a code example of this:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">'scope</span><span class="p">:</span> <span class="p">{</span> <span class="o">..</span>
    <span class="k">if</span> <span class="n">broke</span> <span class="p">{</span> <span class="k">break</span> <span class="nv">'scope</span><span class="p">;</span> <span class="p">}</span>
    <span class="o">..</span>
<span class="p">}</span>
<span class="k">let</span> <span class="n">result</span> <span class="o">=</span> <span class="nv">'scoop</span> <span class="p">{</span> <span class="o">..</span>
    <span class="k">if</span> <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">=</span> <span class="nf">scoop</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">break</span> <span class="nv">'scoop</span> <span class="n">value</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">default_value</span>
<span class="p">};</span>
</code></pre></div></div>

<blockquote>
  <p>Rust loops come in many a style<br />
but few know it too has a do-while<br />
though its looks may bemuse<br />
you can put it to use<br />
and rest assured it’ll compile.</p>
</blockquote>

<p>Again, a funny yet sometimes useful trick where one can emulate a <code class="language-plaintext highlighter-rouge">do-while</code> loop using a block in the <code class="language-plaintext highlighter-rouge">while</code> condition:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="k">mut</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="c1">// behold: do-while!</span>
<span class="k">while</span> <span class="p">{</span>
    <span class="n">x</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="n">x</span> <span class="o">&lt;</span> <span class="mi">10</span>
<span class="p">}</span> <span class="p">{}</span>
</code></pre></div></div>

<blockquote>
  <p>Sometimes iterating a <code class="language-plaintext highlighter-rouge">Vec</code><br />
by index incurs a bounds check<br />
however a slice<br />
often won’t, which is nice.<br />
So go get your runtime perf back.</p>
</blockquote>

<p>Sergey “Shnatsel” Davidoff wrote a <a href="https://shnatsel.medium.com/how-to-avoid-bounds-checks-in-rust-without-unsafe-f65e618b4c1e">very good blog post</a> on how to avoid bounds checks, which sometimes cost a bit of performance when they appear on the hot code path. The rhyme and code on the slide show the easiest technique, which is using a slice.</p>

<blockquote>
  <p>Just sometimes a Rust Result looks<br />
like an owl sitting on stacks of books.<br />
With two units as eyes,<br />
on embedded it flies<br />
over meadows and forests and brooks.</p>
</blockquote>

<p>This poem is dedicated to the “Owl” result type (<code class="language-plaintext highlighter-rouge">Result&lt;(), ()&gt;</code>), which is often used in embedded systems to denote success or failure, where it is far nicer than using a bool while still having the same size.</p>

<blockquote>
  <p>Rust has special syntax to let<br />
you a function to drop values get<br />
It is named for its looks<br />
toilet closure, the books<br />
tell you to use <code class="language-plaintext highlighter-rouge">mem::drop</code> instead.</p>
</blockquote>

<p>Another syntax-based poem, this time about the toilet closure (<code class="language-plaintext highlighter-rouge">|_|()</code>).</p>

<blockquote>
  <p>In Febuary a year ago<br />
Rust 1.59 stole the show<br />
with assignments that could<br />
destructure where you would<br />
do multiple ones, now you know.</p>
</blockquote>

<p>Rust <code class="language-plaintext highlighter-rouge">let</code> bindings use patterns to allow for destructuring, which also afforded them the option to assign multiple bindings via tuples (e.g. <code class="language-plaintext highlighter-rouge">let (a, b) = (1, 2)</code>). However, assignments were not awarded this luxury. Rust 1.59.0 finally brought them up to feature parity, so we can write <code class="language-plaintext highlighter-rouge">(a, b) = (1, 2);</code> in valid Rust code (given pre-existing mutable  bindings of <code class="language-plaintext highlighter-rouge">a</code> and <code class="language-plaintext highlighter-rouge">b</code>).</p>

<blockquote>
  <p>One cool byproduct of this option<br />
that has not seen too much adoption<br />
is that you can ignore foo<br />
without <code class="language-plaintext highlighter-rouge">let</code> as you do,<br />
it’s a small RSI antitoxin.</p>
</blockquote>

<p>A good number of types and functiosn in Rust have the <code class="language-plaintext highlighter-rouge">#[must_use]</code> attribute, which the <code class="language-plaintext highlighter-rouge">unused_must_use</code> lint will pick up and warn if the result of that functions or return values of that types are left unused. However, as an escape hatch, the lint does not warn if the result is assigned or bound via <code class="language-plaintext highlighter-rouge">let _ = ..</code>. So you’ll see such wildcard <code class="language-plaintext highlighter-rouge">let</code>s from time to time in Rust code. Since assignments now also target patterns, we can simply assign to a wildcard, thus saving the effort of writing <code class="language-plaintext highlighter-rouge">let</code>. It will likely be a while until this simplification is widely used.</p>

<blockquote>
  <p>Rust used to use <code class="language-plaintext highlighter-rouge">let</code> but with any<br />
irrefutable bindings, so many<br />
uses where one would guard<br />
the code was somewhat hard<br />
now you get them a dozen a penny.</p>
</blockquote>

<p>This rhyme celebrates the advent of <code class="language-plaintext highlighter-rouge">let-else</code>, which allows us to use a refutable <code class="language-plaintext highlighter-rouge">let</code> as a guard, follwoed by an <code class="language-plaintext highlighter-rouge">else</code> block.</p>

<blockquote>
  <p>Atomics allow you to choose<br />
what ordering on access to use.<br />
While on X-eighty-six<br />
You may easily mix<br />
them, on ARM the results may confuse.</p>
</blockquote>

<p>The slide showed a chain link fence. After reciting the rhyme, I asked the attendants: “Know what that is? – A memory fence.”</p>

<blockquote>
  <p>In Rust you may know that the Range<br />
type does not impl Copy, how strange!<br />
But I’ve heard that some fine<br />
folks hatch plans to combine<br />
their powers this to rearrange.</p>
</blockquote>

<p>Many people have noted that <code class="language-plaintext highlighter-rouge">Range&lt;T&gt;</code> doesn’t implement <code class="language-plaintext highlighter-rouge">Copy</code> even when <code class="language-plaintext highlighter-rouge">T</code> does. The reason for this is that historically, <code class="language-plaintext highlighter-rouge">Range</code> was defined to be iterable (instead of merely implementing <code class="language-plaintext highlighter-rouge">IntoIterator</code>) and copying the iterable would mean that the original would no longer be changed. In the meantime the iterator protocol (how Rust implements a <code class="language-plaintext highlighter-rouge">for</code> loop internally) has been overhauled so that this restriction is no longer necessary, but it will probably take another edition to allow us to copy ranges.</p>

<blockquote>
  <p>Sometimes dyn traits in Rust do require<br />
thinking outside the box to acquire<br />
a solution to track<br />
different types on the stack<br />
to quench our full-stack desire.</p>
</blockquote>

<p>I’ve <a href="/2020/03/14/ootb.html">blogged</a> about this before: You can get a <code class="language-plaintext highlighter-rouge">&amp;dyn</code> reference to different type without <code class="language-plaintext highlighter-rouge">Box</code>ing the values, but you need distinct stack slots for them because a value can only ever have one type.</p>

<blockquote>
  <p>To many a newbie’s surprise<br />
we often use a <code class="language-plaintext highlighter-rouge">str</code> slice<br />
for a lot of stuff<br />
borrowing is enough<br />
and not needing more memory is nice.</p>
</blockquote>

<p>This rhyme merely states the fact that when working with strings, borrowing a slice of them often is sufficient.</p>

<blockquote>
  <p>To defer a task isn’t hard,<br />
in Rust you can just use a guard<br />
which is a type that<br />
does on drop what you had<br />
it in mind to complete from the start.</p>
</blockquote>

<p>The slide for this rhyme shows a royal English guard. In Rust, those guard types are really useful, not only for locks.</p>

<blockquote>
  <p>When a large naïve linked list you drop<br />
your program will crash and then stop<br />
for you’ll exhaust the stack<br />
from there it’s no way back<br />
so please do manual <code class="language-plaintext highlighter-rouge">while let</code>-<code class="language-plaintext highlighter-rouge">pop</code>.</p>
</blockquote>

<p>Alexey Kladow <a href="https://matklad.github.io/2022/11/18/if-a-tree-falls-in-a-forest-does-it-overflow-the-stack.html">pointed out</a> that the autogenerated <code class="language-plaintext highlighter-rouge">Drop</code> implementation would recurse into a singly-linked list (that would be defined by <code class="language-plaintext highlighter-rouge">struct Node&lt;T&gt; { value: T, next: Option&lt;Box&lt;Node&lt;T&gt;&gt;&gt; }</code>) in a way that could not be done by tail recursion, so a reference to each node would land on the stack. For large lists, this could overflow the stack. The solution here is to manually follow the list with <code class="language-plaintext highlighter-rouge">while let</code>:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="nb">Drop</span> <span class="k">for</span> <span class="n">Node</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span>
  <span class="k">fn</span> <span class="nf">drop</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">while</span> <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="n">next</span><span class="p">)</span> <span class="o">=</span> <span class="k">self</span><span class="py">.next</span><span class="nf">.take</span><span class="p">()</span> <span class="p">{</span>
      <span class="o">*</span><span class="k">self</span> <span class="o">=</span> <span class="o">*</span><span class="n">next</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<blockquote>
  <p>With Rust, every coder feels safe,<br />
so some venture ever so brave<br />
into OS flaws to<br />
poke some holes into<br />
the cover to then rant and rave.</p>
</blockquote>

<p>The slide shows a tweet by Patrick Walton who lamented that it is hard to explain why Rust doesn’t forbid reading from or writing to <code class="language-plaintext highlighter-rouge">/proc/mem</code> on Linux (which can be misused to poke holes in our aliasing guarantees). It is generally felt that this is a weakness of the operating system and out of the purview of the language; besides checking this would carry a cost on every file access, which is certainly not proportional to the risk.</p>

<blockquote>
  <p>When in Rust crates features you use<br />
and don’t ask for docs.rs to choose<br />
to document all<br />
it’s dropping the ball,<br />
as with features it’s somewhat obtuse.</p>
</blockquote>

<p>If you want docs.rs to document all your crate’s features, put the following into your <code class="language-plaintext highlighter-rouge">Cargo.toml</code>:</p>

<div class="language-toml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[package.metadata.docs.rs]</span>
<span class="py">all-features</span> <span class="p">=</span> <span class="kc">true</span>
</code></pre></div></div>

<blockquote>
  <p>A cool thing in nightly rust be<br />
called “portable SIMD”,<br />
which means you can say<br />
in a cross-platform way<br />
how instructions should parallel be.</p>
</blockquote>

<p>The slides show the following code and output:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#![feature(portable_simd)]</span>
<span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">simd</span><span class="p">::</span><span class="n">f32x4</span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="nn">f32x4</span><span class="p">::</span><span class="nf">from_array</span><span class="p">([</span><span class="mf">0.</span><span class="p">,</span> <span class="mf">1.</span><span class="p">,</span> <span class="mf">2.</span><span class="p">,</span> <span class="mf">3.</span><span class="p">]);</span>
    <span class="nd">dbg!</span><span class="p">(</span><span class="n">x</span> <span class="o">*</span> <span class="mf">2.</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ rustc +nightly simd.rs -O &amp;&amp; simd              
[simd.rs:7] y = [
    0.0,
    2.0,
    4.0,
    6.0,
]
</code></pre></div></div>

<p>Thus showcasing a simple use for portable SIMD which will work on all CPU architectures (unlike <code class="language-plaintext highlighter-rouge">std::arch</code>-based SIMD which is stable on e.g. x86, but bound to that platform).</p>

<blockquote>
  <p>If your code has the requirement <br />
that a type be not Sync or not Send<br />
it’s time to introduce<br />
a phantom to choose<br />
which type’s contract you will amend.</p>
</blockquote>

<p>The slide shows the following code, which defines type aliases for <code class="language-plaintext highlighter-rouge">PhantomData</code>s that, when added to a type, preclude that type being <code class="language-plaintext highlighter-rouge">Send</code> or <code class="language-plaintext highlighter-rouge">Sync</code>, respectively. This way, Rust allows us to guarantee that values of certain types won’t ever pass or be shared across a thread boundary, which can sometimes be useful for correctness and optimizations.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">marker</span><span class="p">::</span><span class="n">PhantomData</span><span class="p">;</span>

<span class="k">type</span> <span class="n">NoSend</span> <span class="o">=</span> <span class="n">PhantomData</span><span class="o">&lt;*</span><span class="k">mut</span> <span class="nb">u8</span><span class="o">&gt;</span><span class="p">;</span>
<span class="k">type</span> <span class="n">NoSync</span> <span class="o">=</span> <span class="n">PhantomData</span><span class="o">&lt;</span><span class="n">Cell</span><span class="o">&lt;</span><span class="nb">u8</span><span class="o">&gt;&gt;</span><span class="p">;</span>

<span class="k">struct</span> <span class="nf">MyType</span><span class="p">(</span><span class="nb">usize</span><span class="p">,</span> <span class="n">NoSend</span><span class="p">,</span> <span class="n">NoSync</span><span class="p">);</span>
</code></pre></div></div>

<blockquote>
  <p><code class="language-plaintext highlighter-rouge">thread::scope</code>, the first time around <br />
Rust one-zero, alas, was unsound<br />
But if you’re keeping track<br />
you know that it’s back,<br />
this time with correct lifetime bound.</p>
</blockquote>

<p>One of Rust’s most impressive features, borrowing across threads, had a problem when it first appeared; it relied on the <code class="language-plaintext highlighter-rouge">drop</code> of the <code class="language-plaintext highlighter-rouge">JoinGuard</code> actually being called for soundness. So the feature was left unstable in the runup to 1.0.0. Later the crossbeam crate improved on that with its own <a href="https://docs.rs/crossbeam/latest/crossbeam/fn.scope.html">implementation</a> with a slightly different interface that introduced a lifetime bound into the method’s scope parameter. The standard library even improved on that in Rust version 1.63 by using two different lifetimes, one for the environment and one for the scope.</p>

<blockquote>
  <p>I told you about Cow last time<br />
but failed to mention in my rhyme<br />
that you can assign-add<br />
change in-place what you had<br />
here as_mut is its partner in crime.</p>
</blockquote>

<p>“last time” here is of course referring to the first “Rust in Rhymes” talk I gave at RustFest Zürich. This time, I also returned to <code class="language-plaintext highlighter-rouge">Cow</code> (no silly pictures this time, sorry) to showcase its abilities. The slide reads:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Cow says Moo</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">cow</span> <span class="o">=</span> <span class="nn">Cow</span><span class="p">::</span><span class="nf">Borrowed</span><span class="p">(</span><span class="s">"Clone on write"</span><span class="p">);</span>
<span class="n">cow</span> <span class="o">+=</span> <span class="s">" makes ownership optional"</span><span class="p">;</span>

<span class="n">cow</span><span class="nf">.as_mut</span><span class="p">()</span><span class="nf">.make_ascii_uppercase</span><span class="p">();</span>
</code></pre></div></div>

<p>This code constructs the abbreviated sentence “Clone on Write makes ownership optional” as in “Cow” says “moo”.</p>

<blockquote>
  <p>Formatting macros now combine<br />
format strings with arguments inline!<br />
Note that this will fail to<br />
work with all things you do<br />
not in local bindings define.</p>
</blockquote>

<p>This new feature is really cool; you can write <code class="language-plaintext highlighter-rouge">format!("&lt;{foo}&gt;")</code> to put the contents of <code class="language-plaintext highlighter-rouge">foo</code> in angly brackets. Unfortunately, rust-analyzer isn’t yet ready to follow references through inline formatting arguments, so it’ll likely be a while until this feature is widely used.</p>

<blockquote>
  <p>When in Rust async functions you call<br />
they don’t do any work at all<br />
instead they will wait<br />
for you to await<br />
them, this might cause your program to stall</p>
</blockquote>

<p>When async Rust was young, many coders fell into the trap of forgetting the <code class="language-plaintext highlighter-rouge">.await</code>, leading their async calls to go nowhere. In Rust, calling an async function simply creates the future, but it doesn’t poll it; that’s what awaiting is for. This is in contrast to other languages where simply calling an async function will start executing its code until the first yield or return.</p>

<blockquote>
  <p>When in Rust async code you borrow<br />
across an <code class="language-plaintext highlighter-rouge">await</code>, you reap sorrow<br />
for those two things don’t mix<br />
but with <code class="language-plaintext highlighter-rouge">Arc</code> you might fix<br />
your code’s problems until tomorrow.</p>
</blockquote>

<p>Another pitfall for new async Rust programmers is that while synchronous Rust is very keen on borrowing, this strategy will fall flat in async Rust. The reason for this is that you no longer control <em>when</em> your code is run, therefore it is impossible to constrain the lifetime of values within your async code. That includes borrowed values, and having something borrowed endlessly is usually a bad idea in Rust.</p>

<blockquote>
  <p>When a future no longer you need,<br />
and you won’t wait for it to complete,<br />
you <code class="language-plaintext highlighter-rouge">drop</code> it, it’s gone<br />
there’s no need to hold on,<br />
and your other code works at full speed.</p>
</blockquote>

<p>Cancellation is implemented in idiomatic Rust by dropping the future. Many people still don’t know this and ask how to cancel a future.</p>

<blockquote>
  <p>Rust hashmaps have some subtle tricks<br />
now throw <code class="language-plaintext highlighter-rouge">rustc-hash</code> into the mix<br />
if you do not expect<br />
to be perf-wise attacked<br />
use the hasher it has called Fx.</p>
</blockquote>

<p>The Fx hash function is used throughout the Rust compiler. It works very well on integers and short strings, of which there are a great many within the normal lifetime of a Rust compiling session.</p>

<blockquote>
  <p>When in Rust you’re coding for speed<br />
and write to a file, you will need<br />
to buffer the write<br />
or your system might<br />
wait for every byte to complete.</p>
</blockquote>

<p>Continuing our performance theme, this rhyme warns of a performance pitfall that many have experienced: Rust writes (and reads) are by default unbuffered. So you need a <code class="language-plaintext highlighter-rouge">BufReader</code> and <code class="language-plaintext highlighter-rouge">BufWriter</code> for buffering your reads and writes, lest every IO operation incur far more OS overhead than actually needed.</p>

<blockquote>
  <p>Likewise it may come as a shock<br />
all your <code class="language-plaintext highlighter-rouge">println!</code> calls silently block<br />
on standard out, be aware<br />
if for speed you do care<br />
in that case best use standard out’s <code class="language-plaintext highlighter-rouge">lock</code>.</p>
</blockquote>

<p>Unlike C, where <code class="language-plaintext highlighter-rouge">printf</code> may usually just assume to be able to write on stdout unimpeded, Rust is far more careful, locking the standard output on every write call. You can avoid this by manually calling <a href="https://doc.rust-lang.org/std/io/struct.Stdout.html#method.lock"><code class="language-plaintext highlighter-rouge">.lock</code></a> on <code class="language-plaintext highlighter-rouge">io::stdout()</code> and using that locked output.</p>

<blockquote>
  <p>Those decrying Rust’s unsafe ‘scape hatch<br />
because it “kills all safety”, natch?<br />
Most experts will tell<br />
that it works pretty well<br />
if all invariants you do catch.</p>
</blockquote>

<p>Very often in online discussion, people profess or pretend to mistake the <code class="language-plaintext highlighter-rouge">unsafe</code> keyword for meaning that the whole idea of safety go out the window. Of course, we know better, and it has even been mathematically proven that safe code can rely on unsafe code as long as the latter upholds its safety invariants.</p>

<blockquote>
  <p>Some people tell you <code class="language-plaintext highlighter-rouge">unsafe</code> will<br />
turn of borrowck and then shill<br />
for C, which is why<br />
they tell this bald lie<br />
because rustc is checking things still.</p>
</blockquote>

<p>Another popular myth busted: <code class="language-plaintext highlighter-rouge">unsafe</code> allows to work with pointers, which avoid the borrow checker, but it doesn’t turn off the latter.</p>

<blockquote>
  <p>When in unsafe Rust code you write<br />
and get some things not quite right<br />
it’s no longer the season<br />
for rhyme nor for reason<br />
though your program may run out of spite.</p>
</blockquote>

<p>The flipside of all of this is of course that the unsafe code must uphold its invariants come what may. This is often not exactly trivial, and the worst thing about such unsound code is that it may work (“out of spite”) for a good while before finally leading to surprising result come the next compiler version, target architecture or whatever; once UB appears, all bets are off.</p>

<p>The slide for this one is a meme picture showing a crab with the caption “If you put a crab up to your ear, you can hear what it’s like to be attacked by a crab”. The audience loved that one.</p>

<blockquote>
  <p>Though Rust saves you from a large class<br />
of errors when RAM you amass,<br />
it won’t fix any test<br />
on it’s own, so you best<br />
make sure that each one does pass</p>
</blockquote>

<p>This rhyme serves as a humbling reminder that while Rust’s type system and borrow checker will save us from a good set of error classes, it cannot ensure program correctness, so we still need to test. And preferrably also run the tests and make sure they pass.</p>

<blockquote>
  <p>When rustc with garbage you feed<br />
Don’t fret if the errors you read<br />
Are confusing as hell<br />
The compiler goes: “Well,<br />
I’m telling you straight what 𝐼 need.”</p>
</blockquote>

<p>The slide for this shows a <code class="language-plaintext highlighter-rouge">@ReductRs</code> tweet reading:</p>

<p>I’m sorry for being so confusing when you were feeding me garbage — Compiler Diagnostics Module Speaks Out.</p>

<blockquote>
  <p>When secrets you handle in Rust,<br />
deleting them safely you must,<br />
so you don’t get hacked<br />
and your defenses cracked.<br />
The zeroize crate I would trust.</p>
</blockquote>

<p>The <a href="https://docs.rs/zeroize">zeroize</a> crate offers a set of traits you can derive on your types that will make it easy to have data overwritten with <code class="language-plaintext highlighter-rouge">0</code> bytes when it gets dropped, thus ensuring that e.g. passphrases aren’t lingering in memory for malicious hackers to find them.</p>

<blockquote>
  <p>Dacquiri: you’d normally think<br />
I was rhyming while having a drink.<br />
But in Rust it describes<br />
A framework to use types<br />
so folks can’t step over the brink</p>
</blockquote>

<p>The <a href="https://docs.rs/dacquiri">dacquiri</a> framework allows annotating methods to require certain authorization elements in a way that ensures the implementation cannot be called without authorization.</p>

<blockquote>
  <p>Andrew Gallant of ripgrep fame<br />
is upping Rust’s byte string game<br />
to make prodding with Rust<br />
real-world data robust<br />
with impressive performance to claim.</p>
</blockquote>

<p>This of course refers to the <a href="https://docs.rs/bstr">bstr</a> crate.</p>

<blockquote>
  <p>If a Rust library you change<br />
to avoid things downstream going strange<br />
you can deftly depend<br />
on what future amend<br />
you’re going to pre-re-arrange.</p>
</blockquote>

<p>I was cackling about how clever I was when rhyming this (and while choosing the “back to the future” slide), but apparently people in the audience mostly failed to get the reference to David Tolnay’s great <a href="https://github.com/dtolnay/semver-trick">semver trick</a> where a crate depends on a future self to implement certain things, thus avoiding incompatibilities between both versions.</p>

<blockquote>
  <p>If in Rust code you need to parse<br />
some args either dense or sparse<br />
where one went for structopt<br />
this dependency stopped<br />
as now clap pulls it out of its…derives.</p>
</blockquote>

<p>The <a href="https://docs.rs/clap">clap</a> crate implements a really great argument parser with many useful features. But the idea to <code class="language-plaintext highlighter-rouge">derive</code> the argument parser from a type was born in the <a href="https://docs.rs/structopt">structopt</a> crate before being added to clap proper. The last line not rhyming reaped a lot of laughter and applause.</p>

<blockquote>
  <p>A bevy of games, not of geese<br />
in Rust is a sight that will please<br />
so join in the fun<br />
get some Rust games to run<br />
you’ll find one can do that with ease.</p>
</blockquote>

<p>The <a href="https://bevyengine.org/">Bevy</a> engine is a production-ready game engine written in Rust. There is a lot of good documentation, so it’s a great way to get into Rust game programming.</p>

<blockquote>
  <p>With tauri you stand on the shoulder<br />
of web tech giants, tread bolder<br />
to build quick applications<br />
in all variations<br />
and run them before getting older.</p>
</blockquote>

<p><a href="https://tauri.app/">Tauri</a> is a framework that relies on web technology to provide a UI for your application that can still be written in Rust. Relying on the system-native web view, it doesn’t need to bundle an almost-complete Chrome, which makes binaries much smaller compared to e.g. Electron.</p>

<blockquote>
  <p>When some Rustaceans exercise<br />
their muscles, it’s not very nice,<br />
their squats at any rate<br />
each go on their own crate<br />
so that others need to improvise.</p>
</blockquote>

<p>This rhyme laments the sad state of many a crate on <a href="https://crates.io">crates.io</a>, being a mere shell that has been put there to reserve the name. In consequence, others need to exercise their imagination to come up with a different name. I note that this topic has been discussed to death many times, and there is still no satisfactory solution in sight. So I won’t prescribe one here, I feel that every one that has been suggested so far had flaws large enough to render the idea moot.</p>

<blockquote>
  <p>We’re surprised to hear cargo test<br />
Is speedwise no longer the best<br />
so I’ll gladly install<br />
nextest, after all<br />
my cores don’t need too much rest.</p>
</blockquote>

<p>The <a href="https://crates.io/crates/cargo-nextest">cargo nextest</a> subcommand is a new, fast test runner for rust that will often outpace good old <code class="language-plaintext highlighter-rouge">cargo test</code>, mostly by maximizing parallelism.</p>

<blockquote>
  <p>insta will easily check<br />
if the objects your test code gets back<br />
are the same as before<br />
when a good state they bore<br />
so your tests are quickly on track.</p>
</blockquote>

<p>Armin Ronacher’s <a href="https://docs.rs/insta">insta</a> crate offers support for snapshot tests, where when you run the test it records the output which you can then save with a subcommand to be compared by future test runs. I cordially recommend it.</p>

<blockquote>
  <p>From Embark in Stockholm says hi<br />
the subcommand <code class="language-plaintext highlighter-rouge">cargo deny</code><br />
which will thoroughly see<br />
your dependency tree<br />
through for…wow, lots of problems, oh my.</p>
</blockquote>

<p>The <a href="https://crates.io/crates/cargo-deny"><code class="language-plaintext highlighter-rouge">cargo deny</code></a> subcommand checks the whole dependency tree for security as well as licensing problems. Suffice to say, I feel much better when it prints nothing upon running it on my code.</p>

<blockquote>
  <p>Clippy users who at first sense<br />
they’re constantly making amends<br />
To make their code “good”<br />
(as they probably should)<br />
will later become lifelong friends.</p>
</blockquote>

<blockquote>
  <p>cargo clippy now has dash-dash-fix<br />
which changes your code’s subpar schticks<br />
to improve it’s style<br />
and perf, and meanwhile<br />
it has got up it’s sleeve some more tricks.</p>
</blockquote>

<p>As a clippy maintainer, of course I had to write a couple of rhymes on it. The first one puts in rhyme a story someone (sorry, I cannot remember who it was) told me that they hated clippy in the beginning, feeling that “it always had something to criticize”, but later were proud and happy that their code was “clippy clean” and now use clippy on all of their code.</p>

<p>The second informs people that many lints now have suggestions that can be automatically applied using the <code class="language-plaintext highlighter-rouge">cargo clippy --fix</code> subcommand.</p>

<blockquote>
  <p>The clippy workshop went great<br />
most people stayed with us late<br />
with good work, much respect,<br />
clippy lints to correct<br />
and also new lints to create</p>
</blockquote>

<p>This one I wrote after holding the RustNation-adjacent clippy workshop. My hat’s off to all participants, you are an awesome bunch!</p>

<blockquote>
  <p><code class="language-plaintext highlighter-rouge">cargo semverver</code> will check<br />
if your current crate version is back-<br />
wards compatible and<br />
warn if change is on hand<br />
that gives users a pain in the neck.</p>
</blockquote>

<p>The cargo subcommand has in the meantime been renamed to <a href="https://crates.io/crates/cargo-semver-checks">cargo semver-checks</a>, but the idea is still the same: Giving you advance warning before you introduce changes to your Rust library crate in a minor version that would break your users’ builds. It will likely become part of cargo proper in due time.</p>

<blockquote>
  <p>Your performance desire to quench<br />
you can put your code on the bench<br />
but to make it complete<br />
a criterion you need<br />
lest measurement errors avenge.</p>
</blockquote>

<p>Some people rely on the <a href="https://docs.rs/bencher">bencher</a> crate, but I will usually recommend <a href="https://docs.rs/criterion">criterion</a>, which has more benchmark configuration options, as well as more solid statistics and optionally a nice graph output.</p>

<blockquote>
  <p>A helix of Rust can be made<br />
to work on the code you create.<br />
Between entering code<br />
you switch to command mode<br />
with complete &amp; assists, it is great!</p>
</blockquote>

<p>The <a href="https://helix-editor.com">helix editor</a> is a modal text editor like VI that has IDE-like features using the Language Server Protocol (LSP). Unlike VI, it goes selection-first, which takes some time to adapt your muscle memory, but the sleek interface and great performance is well worth it.</p>

<blockquote>
  <p>With the crypto winter afoot,<br />
Rust jobs are now mostly good<br />
in search, cloud, UI<br />
embedded, AI,<br />
if you haven’t applied yet, you should.</p>
</blockquote>

<p>I usually get a few job offers per week. During the late ’10s, most were blockchain job spam. I now get far less of those and more jobs in other fields, which given my <a href="https://llogiq.github.io/2019/11/05/fear.html">stance</a> on blockchain I personally find very pleasant. So if you haven’t sought out a Rust job because you thought it’d all be crypto blockchain web3 stuff, perhaps have another look.</p>

<blockquote>
  <p>Should I buy an aquarium, my wish<br />
is to fill it with Rust turbofish<br />
One fro, one reverse<br />
(to complete this verse)<br />
&amp; a TV with satellite dish.</p>
</blockquote>

<p>I should have put that one to the other syntax rhymes, but somehow failed to do so. Anyway the famed and feared <a href="https://turbo.fish">turbofish</a> is a somewhat weird feature of the Rust language that however ensures that Rust code be linearly parseable. The problem it solves is that a <code class="language-plaintext highlighter-rouge">&lt;</code> could be the start of generics or a less-than sign. So the fish disambiguates those cases.</p>

<blockquote>
  <p>Festive Ferris the Crab crossed the street<br />
a bunch of gophers to meet,<br />
and pythons and others<br />
all FFI brothers<br />
to get something to drink and to eat.</p>
</blockquote>

<p>This rhyme was a very roundabout way of saying that we programmers are all in the same boat, whether we write Rust, Go, Python or any other language. No programmer is better than another because of what language they choose and we should all strive to improve collectively instead of stupidly fighting among one another.</p>

<blockquote>
  <p>If you tell people that they must<br />
from now on write all code in Rust<br />
you will earn some ‘aw, shucks’,<br />
for your lobbying sucks.<br />
Good luck on regaining their trust.</p>
</blockquote>

<p>In my last Java job before switching my career to Rust, I often felt limited by what I couldn’t express in Java. Yet I knew my colleagues, unlike myself, did not sign up for learning a new language, and forcing them to do so would surely make them hate me. So when you want to introduce Rust at a job, please think of your colleagues who haven’t learned it yet and may not feel the same way about it as you do.</p>

<blockquote>
  <p>If your thirst for rhymes isn’t quenched<br />
and your love of Rust is entrenched<br />
you’ll find more of my verse<br />
on Twitter – or worse<br />
if that service is tabled – or benched.</p>
</blockquote>

<p>I for now have a <a href="https://twitter.com/llogiq">twitter account</a> where I will sometimes post more Rust rhymes. So if you want to avoid missing any of them, follow me there. The last line alludes to the fact that twitter is steering towards an unknown future since being bought and shedding a great many people. Perhaps I might move to some mastodon instance in the future. Don’t worry, I’ll let all my twitter followers know.</p>

<blockquote>
  <p>An ML model showed it could rhyme<br />
about Rust in a fraction of time<br />
it does take me to write<br />
a poem, I won’t fight<br />
it ‘cause having more rhymes is sublime.</p>
</blockquote>

<blockquote>
  <p>Well, tickle me pink! I admire,<br />
this work, for now I can retire<br />
and sit on a beach<br />
a stiff drink in my reach.<br />
Why should this AI draw my ire?</p>
</blockquote>

<p>The slide for this shows a GPT prompt “Show a rust error message in form of a limerick.” which the model answered with</p>

<p>There once was a code that was fraught<br />
with a borrow checker that thought<br />
“You’re taking a slice,
But not paying the price!”
And the error message it brought.</p>

<p>I was sent this on twitter with the question if I was worried if the AI would replace me. Why would I be worried? It’s my rhymes <em>and</em> the model’s, not either-or. Besides should I choose to retire, you can still ask the model for more rhymes if you want them.</p>

<blockquote>
  <p>Farewell now my friends, time to go<br />
I hope you’re enjoying the show<br />
and thank you so much<br />
for you being such<br />
a great audience! Cheerio!</p>
</blockquote>

<p>The final rhyme was me being thankful for the great audience (which I had correctly predicted would appear). I can assure you that I wouldn’t have given the rhyme to a lesser audience than this was. A good update to the disclaimer I ended the first <a href="https://www.youtube.com/watch?v=JR6aHXlRAOM">“Rust in Rhymes” talk</a> with, don’t you think?</p>

<p>And that’s it. The whole talk explained. I hope you’ve had a good time reading it, and feel free to ask questions on /r/rust if anything is still unclear.</p>]]></content><author><name>Llogiq</name></author><summary type="html"><![CDATA[Update: The talk video is now on YouTube. The slides are here.]]></summary></entry><entry><title type="html">Catch 22! Rust in Review</title><link href="https://llogiq.github.io/2022/12/11/catch22.html" rel="alternate" type="text/html" title="Catch 22! Rust in Review" /><published>2022-12-11T00:00:00+00:00</published><updated>2022-12-11T00:00:00+00:00</updated><id>https://llogiq.github.io/2022/12/11/catch22</id><content type="html" xml:base="https://llogiq.github.io/2022/12/11/catch22.html"><![CDATA[<p>It’s getting late in the year, so it’s time to write another blog post looking
what good and bad came out of the year for Rust and what’s likely to happen
next year.</p>

<p>I guess the second part is also a roadmap post for the next year, so there’s
that.</p>

<h2 id="language">Language</h2>

<p>Language-wise, 2022 was a great year for syntactic sugar, with <code class="language-plaintext highlighter-rouge">let</code>-<code class="language-plaintext highlighter-rouge">else</code>
rapidly reaching stable (I must admit I wasn’t even aware of it until I read
the release blog), and <code class="language-plaintext highlighter-rouge">let</code> chains being worked on in nightly. I was wary of
those until I tried them in the clippy codebase, since then I’m very happy
we’ll get them sooner or later. We also got 
<a href="https://blog.rust-lang.org/2022/10/28/gats-stabilization.html">GATs</a> in 1.65!
Those who don’t know what that is, don’t worry, it’s all good, and for the
others, that was well worth the wait. Finally!</p>

<p>There also has been some really great work on the async front, with a MVP for
async trait landing in nightly in mid-November. No more hacks with attribute
macros and boxing all futures, yay!</p>

<p>Some new features were just filling gaps, like destructuring assignments
which brought them to parity with <code class="language-plaintext highlighter-rouge">let</code> bindings. While they undoubtedly made
the implementation more complex, the language complexity was reduced by one
special case.</p>

<h2 id="compiler">Compiler</h2>

<p>The compiler still got faster, despite serving more features and improving the
error messages even more. Cranelift and rustc-codegen-gcc are progressing
nicely, and gcc-rust was recently merged into GCC proper. mrustc is still
alive and well, so we’ll soon have three compilers and three backends (LLVM,
gcc, cranelift) for the official compiler.</p>

<h2 id="tools--ecosystem">Tools &amp; Ecosystem</h2>

<p>Rust analyzer got a well-deserved promotion to a proper rust-lang project.
With <a href="https://helix-editor.com/">helix</a> for the terminal and
<a href="https://lapce.dev/">lapce</a> using its own GUI, we now have two native Rust
editors written in Rust that use the rust-analyzer backend. Cargo gained an
inbuild implementation of the <code class="language-plaintext highlighter-rouge">cargo add</code> subcommand, which was formerly only
available through the <a href="https://github.com/killercup/cargo-edit"><code class="language-plaintext highlighter-rouge">cargo-edit</code></a>
crate (update: With this year’s last release, the <code class="language-plaintext highlighter-rouge">cargo rm</code> subcommand joined
cargo proper, too). The <code class="language-plaintext highlighter-rouge">gitoxide</code> git source control system implementation
finally gained enough functionality to <code class="language-plaintext highlighter-rouge">clone</code> itself.</p>

<p>The tokio runtime saw a steady stream of improvements, with the former making
headway in safe &amp; fast web service implementations and notably tooling to dump
#an “async backtrace” in case something goes wrong, which finally closes an
important gap between sync and async debugging. Async-std development seems to
have stalled, with the last commit dating from July.</p>

<p>GUI-wise, we’ve seen multipe frameworks come to life, from the Xi-editor
inspired <a href="https://github.com/linebender/druid">druid</a> and its likely successor
<a href="https://github.com/linebender/xilem">xilem</a> over the startup-sponsored
<a href="https://slint-ui.com">slint</a> to <a href="https://github.com/iced-rs/iced">iced</a> and
<a href="https://github.com/DioxusLabs/dioxus">dioxus</a>. Other toolkits saw progress,
too, the <a href="https://www.areweguiyet.com">AreWeGUIyet</a> page lists many of them.</p>

<h2 id="growth">Growth</h2>

<p><a href="https://crates.io">Crates.io</a> has more than 99k crates now. This time last
year it was about 70k, so the growth hasn’t stalled at all. The registry has
counted 24.4 billion downloads, which is more than double last year’s count!
The <a href="https://reddit.com/r/rust">subreddit</a> has cracked 200k subscribers in
October and is now bigger than <a href="https://reddit.com/r/golang">r/golang</a> which
it had trailed all the years before. Rust has kept its 19th spot in the
redmonk index, and won top spot in the yearly “most loved”
<a href="https://survey.stackoverflow.co/2022/#most-loved-dreaded-and-wanted-language-love-dread">StackOverflow survey</a>,
in addition it’s “most wanted” for the second year in a row.</p>

<p>The blockchain winter is now in full swing, with many of the fraudulent
players going broke. Luckily, my
<a href="https://llogiq.github.io/2019/11/05/fear.html">fear</a> that Rust would suffer
by association didn’t become reality. The job growth in other fields like
machine learning, cloud and elsewhere easily outweighed the losses in
blockchain-related Rust jobs. Phew!</p>

<p>This part of the growth curve doesn’t come out of thin air: Many companies
large and small spun up Rust teams. We make inroads in the cloud, on mobile,
in embedded (notably automotive and aerospace, where the groundwork is
<a href="https://medium.com/volvo-cars-engineering/why-volvo-thinks-you-should-have-rust-in-your-car-4320bd639e09">currently being laid</a>),
in machine learning (where python still reigns supreme, but Rust could win
sometimes by superior performance and type-based coding ergonomics). The
current Android version 13 has
<a href="https://security.googleblog.com/2022/12/memory-safe-languages-in-android-13.html">more than 1.5 million Rust lines of code</a>.
There’s Rust
<a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8aebac82933ff1a7c8eede18cab11e1115e2062b">in the Linux kernel</a>!</p>

<p>Those folks who spelled Rust’s doom because there wasn’t enough buy-in from
companies now spell its doom because there’s so much buy-in. I guess some
things never change.</p>

<h2 id="governance">Governance</h2>

<p>The foundation has really come into its own this year. Kudos to all involved!
With the
<a href="https://old.reddit.com/r/rust/comments/vc4pcm/community_grants_program_awards_announcement/">community grants</a>,
the foundation directly allowed more people to work on what they love while
boosting the Rust ecosystem. Also I hear there has been some lobbying in the
US that resulted in Rust being the official recommendation for low-level
projects.</p>

<p>The project restructuring also saw some good progress; I am personally very
happy with the outcome so far.</p>

<hr />

<p>Now for a look <del>in the crystal ball</del> on the roadmap:</p>

<h2 id="language-1">Language</h2>

<p>I see chains of <code class="language-plaintext highlighter-rouge">let</code>. Async traits, too. I see them bloom for me and you. And
I think to myself, what a wonderful Rust… or something. <code class="language-plaintext highlighter-rouge">try</code> blocks will
have to prove their worth in the nightly implemenation. I haven’t tried them
yet, so I cannot predict if they land next year. We will see more whittling
down the missing pieces of features we got this year for sure. Especially on
the async side of things I expect more movement regarding pinning and avenues
for optimizing the task machinery for various use cases.</p>

<p>I have high hope for negative trait bounds which sound like they will enable a
bucket of interesting use cases, especially with regards to avoiding the need
for specialization. Though there is some compatibility hazard, I’m sure the
lang team will take sufficient precautions to make it a net win.</p>

<h2 id="compiler-1">Compiler</h2>

<p>I guess it’s safe to say the compiler will get even faster in ‘23, though we
may already see diminishing returns.</p>

<p>As for the backends, we’ll see gcc-rust mature somewhat, and rustc-codegen-gcc
as well as cranelift become useable for more cases. Perhaps even some kind of
stabilization.</p>

<p>I sure hope we’ll see more evolution on the macro machinery. WebAssembly could
be a great base on which to rebuild the macro system; we could even cache
pre-compiled macros or have crates.io serve proc-macro crates as WASM blobs,
thus reducing compile time for their users.</p>

<h2 id="tools--ecosystem-1">Tools &amp; Ecosystem</h2>

<p>The Rust experience is already pretty good, and the stream of improvements is
unlikely to ebb next year. I’d note that with the newly set up fmt team, I
expect notable advances in rustfmt functionality.</p>

<p>Most Rustaceans wish for some consolidation; do we really need three async
runtimes, eleven web frameworks, fourteen GUI libraries and a dozen or so hash
map crates? On the other hand, I think we haven’t seen the end of the
experimentation phase; in fact it may just have begun. So I fully expect more
delightful new libraries and surprising crates come up every now and then. One
size need not fit all, as they say.</p>

<h2 id="growth-1">Growth</h2>

<p>Rust has reached the tipping point. From here on the only way is up. The only
thing that could pull the trajectory back down would be for an even better new
language to emerge, and I don’t expect one to appear in the next three years.
That said, if it does appear, I’m <em>so</em> here for it.</p>

<p>With this explosive growth come a set of challenges, which nicely brings us to
the next point:</p>

<h2 id="governance-1">Governance</h2>

<p>The foundation has shown it can do more than taking care of the trademark or
sponsoring email services for This Week in Rust. Now is the time to expand the
involvement. The current community grants are a good start, but they’re pretty
narrow. The foundation could team up with universities to sponsor PhD theses
around Rust. It could give out a yearly award for the most innovative use of
Rust in the industry. It could lobby more governments to make memory safe
languages the official recommendation.</p>

<p>I dare not hope to see memory safety mandatory, but perhaps at one point it
could become a sign of negligence to write code in a memory unsafe language
unless there is a good reason to do so. The first country to enact such laws
is going to rule the software sector for years to come.</p>

<p>The project will continue spinning out working groups. Currently those can
organize however they see fit, but we’ll start to see some standards emerge.
While this will lead to some small frustration on some members’ part, it will
ultimately help transparency and inter-team ventures.</p>]]></content><author><name>Llogiq</name></author><summary type="html"><![CDATA[It’s getting late in the year, so it’s time to write another blog post looking what good and bad came out of the year for Rust and what’s likely to happen next year.]]></summary></entry><entry><title type="html">Rust 2021 – Looking Back and Forth</title><link href="https://llogiq.github.io/2021/12/30/review.html" rel="alternate" type="text/html" title="Rust 2021 – Looking Back and Forth" /><published>2021-12-30T00:00:00+00:00</published><updated>2021-12-30T00:00:00+00:00</updated><id>https://llogiq.github.io/2021/12/30/review</id><content type="html" xml:base="https://llogiq.github.io/2021/12/30/review.html"><![CDATA[<p>2021 was a great year for Rust. The language got more
<a href="https://github.com/rust-lang/rust/issues/44580#issuecomment-991782799">powerful</a>. The standard
library gained a good number of new functions and a lot of improvements on the existing ones. The
error messages were tweaked and tweaked again. The compiler got
<a href="https://nnethercote.github.io/2021/11/12/the-rust-compiler-has-gotten-faster-again.html">faster</a>
still, despite
<a href="https://github.com/rust-lang/rust/pull/88325">at last enabling noalias information by default</a>.</p>

<p>Also <a href="https://blog.rust-lang.org/2021/07/29/Rust-1.54.0.html">incremental compilation the default</a>
again after being backed out due to soundness problems. We got a 
<a href="https://github.com/rust-lang/rust/blob/master/RELEASES.md#internal-changes">new experimental gcc-based backend</a>,
and another gcc-based implementation (both are work in progress for now). Clippy gained a number of
lints and lost a lot of false positives in turn. Rust is 
<a href="https://security.googleblog.com/2021/04/rust-in-linux-kernel.html">getting into the Linux kernel</a>,
which also brings some improvements to both language and libraries to facilitate this feat.</p>

<p>The community also grew. <a href="https://crates.io">Crates.io</a> is now at more than 70000 crates in stock, 
and more than 11 billion downloads! The <a href="https://reddit.com/r/rust">rust subreddit</a> grew from about 
122k to more than 162k users, narrowly trailing <a href="https://reddit.com/r/golang">r/golang</a>. Rust 
<a href="https://redmonk.com/sogrady/2021/08/05/language-rankings-6-21/">entered the top 20 of the Redmonk index</a>
for the first time, and won the 
<a href="https://insights.stackoverflow.com/survey/2021#technology-most-loved-dreaded-and-wanted">Stack Overflow survey’s</a>
“Most loved programming language” crown for the sixth year in a row. I’ve had more people ask me 
for mentoring than ever before.</p>

<p>We have a <a href="https://foundation.rust-lang.org/">foundation</a> which is gaining members. The foundation has actually started to do useful
stuff, like 
<a href="https://foundation.rust-lang.org/posts/2021-10-18-crates-io-oncall-ferrous-systems/">professionally organizing the operations</a>
of crates.io which has so far been done by volunteers. Recently, the
<a href="https://medium.com/concordium/the-devx-initiative-sponsorship-program-goals-and-principles-e640063eeaa7">DevX initiative</a>
has started sponsoring work on Rust. This is great news for Rust and Open Source alike!</p>

<p>That’s not to say all has been roses. The 
<a href="https://www.reddit.com/r/rust/comments/qzme1z/moderation_team_resignation/">mod team quit</a>
due to a problem with the rules not allowing us to enforce the CoC. Fortunately, there is
<a href="https://blog.rust-lang.org/inside-rust/2021/12/17/follow-up-on-the-moderation-issue.html">active work underway</a>
to fix this problem, and the new mod team also seems to be doing a good job as far as I can tell.</p>

<p>We still have a
<a href="https://www.reddit.com/r/rust/comments/qqu1bw/what_should_we_do_about_cve202026235_localtime_r/">security flaw</a>
in one of the most popular time/date libraries, which most seem to simply have forgotten about. Ok, 
it’s fixed in time 0.3, but not in chrono so far. At least we have a 
<a href="https://rustsec.org/advisories/RUSTSEC-2020-0159.html">CVE</a> to remind us of that.</p>

<hr />

<p>Looking forward, we’re going to see more of the same. The compiler will be getting more powerful
while speeding up even more. Features that people have been missing are in the process of being
implemented and will arrive in the new year. The error message code will at one point become
sentient and decide to send a robot back in time to save mankind from skynet or something. The
community will continue to grow, and more Rust jobs will be available, ready to be taken by more
Rust programmers.</p>

<p>My guess is that we will see more active involvement from the foundation in the new year, which is
great news to the community. The work started after the mod team change will likely conclude within
the second quarter of 2022, improving the governance structure and paving the way for even more
success in the future.</p>

<p>With a bit of luck we will find a way to harden our systems against supply-chain attacks before
they become a real problem. We likely need more integration, documentation and raising awareness.</p>

<p>I still see some risk that there might be a tipping point where the backlash against the out of
control grift around blockchains will also harm Rust by association, because many projects in that
space use the language because of performance, safety and productivity. Knocking on wood here.</p>

<p>Around us, the pandemic is still raging. Meetup activity has gone online or is reduced. More people
work remotely than ever before. My hope and wish is for all of you to stay safe &amp; healthy.</p>]]></content><author><name>Llogiq</name></author><summary type="html"><![CDATA[2021 was a great year for Rust. The language got more powerful. The standard library gained a good number of new functions and a lot of improvements on the existing ones. The error messages were tweaked and tweaked again. The compiler got faster still, despite at last enabling noalias information by default.]]></summary></entry></feed>