<?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="http://www.dingostick.com/feed.xml" rel="self" type="application/atom+xml" /><link href="http://www.dingostick.com/" rel="alternate" type="text/html" /><updated>2026-04-08T13:58:33+00:00</updated><id>http://www.dingostick.com/feed.xml</id><title type="html">Adam Shirey’s code snippets</title><subtitle>∀ bp ∈ BlogPosts | bp ⊂ {Code, Data}</subtitle><entry><title type="html">Tampermonkey: remove GeeksForGeeks search results</title><link href="http://www.dingostick.com/code/2025/09/17/tampermonkey-remove-geeksforgeeks-search-results.html" rel="alternate" type="text/html" title="Tampermonkey: remove GeeksForGeeks search results" /><published>2025-09-17T15:13:09+00:00</published><updated>2025-09-17T15:13:09+00:00</updated><id>http://www.dingostick.com/code/2025/09/17/tampermonkey-remove-geeksforgeeks-search-results</id><content type="html" xml:base="http://www.dingostick.com/code/2025/09/17/tampermonkey-remove-geeksforgeeks-search-results.html"><![CDATA[<p>I only recently installed <a href="https://www.tampermonkey.net/">Tampermonkey</a> to address a relatively minor annoyance at work. Two days later, I found some spammy search results on DuckDuckGo that were bothering me, so I decided to try my hand at a Tampermonkey script to remove them. Here’s the JavaScript that I wrote to remove results from DDG:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ==UserScript==</span>
<span class="c1">// @name         Delete GeeksForGeeks from DDG</span>
<span class="c1">// @namespace    http://tampermonkey.net/</span>
<span class="c1">// @version      2025-09-17</span>
<span class="c1">// @description  Remove GeeksForGeeks (+other!) from Duck Duck Go</span>
<span class="c1">// @author       Adam Shirey</span>
<span class="c1">// @match        http://duckduckgo.com/*</span>
<span class="c1">// @match        https://duckduckgo.com/*</span>
<span class="c1">// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==</span>
<span class="c1">// @grant        none</span>
<span class="c1">// ==/UserScript==</span>

<span class="nb">window</span><span class="p">.</span><span class="nx">setTimeout</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">root</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">'</span><span class="s1">ol.react-results--main</span><span class="dl">'</span><span class="p">);</span>

    <span class="c1">// Add other terms as you see fit</span>
    <span class="kd">const</span> <span class="nx">matches</span> <span class="o">=</span> <span class="p">[</span>
        <span class="dl">'</span><span class="s1">geeksforgeeks</span><span class="dl">'</span>
    <span class="p">];</span>

    <span class="kd">const</span> <span class="nx">articles</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="k">from</span><span class="p">(</span><span class="nx">root</span><span class="p">.</span><span class="nx">querySelectorAll</span><span class="p">(</span><span class="dl">'</span><span class="s1">article</span><span class="dl">'</span><span class="p">));</span>

    <span class="kd">const</span> <span class="nx">matchAny</span> <span class="o">=</span> <span class="p">(</span><span class="nx">text</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">text</span><span class="p">)</span> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
        <span class="kd">const</span> <span class="nx">lower</span> <span class="o">=</span> <span class="nx">text</span><span class="p">.</span><span class="nx">toLowerCase</span><span class="p">();</span>
        <span class="k">return</span> <span class="nx">matches</span><span class="p">.</span><span class="nx">some</span><span class="p">(</span><span class="nx">m</span> <span class="o">=&gt;</span> <span class="nx">lower</span><span class="p">.</span><span class="nx">includes</span><span class="p">(</span><span class="nx">m</span><span class="p">.</span><span class="nx">toLowerCase</span><span class="p">()));</span>
    <span class="p">};</span>

    <span class="nx">articles</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">article</span> <span class="o">=&gt;</span> <span class="p">{</span>
        <span class="kd">const</span> <span class="nx">anchors</span> <span class="o">=</span> <span class="nx">article</span><span class="p">.</span><span class="nx">querySelectorAll</span><span class="p">(</span><span class="dl">'</span><span class="s1">a[title]</span><span class="dl">'</span><span class="p">);</span>
        <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="nx">a</span> <span class="k">of</span> <span class="nx">anchors</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="nx">matchAny</span><span class="p">(</span><span class="nx">a</span><span class="p">.</span><span class="nx">title</span><span class="p">))</span> <span class="p">{</span>
                <span class="nx">article</span><span class="p">.</span><span class="nx">remove</span><span class="p">();</span>
                <span class="k">break</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">});</span>
<span class="p">},</span> <span class="mi">1000</span><span class="p">)</span>
</code></pre></div></div>]]></content><author><name></name></author><category term="code" /><category term="javascript" /><summary type="html"><![CDATA[I only recently installed Tampermonkey to address a relatively minor annoyance at work. Two days later, I found some spammy search results on DuckDuckGo that were bothering me, so I decided to try my hand at a Tampermonkey script to remove them. Here’s the JavaScript that I wrote to remove results from DDG:]]></summary></entry><entry><title type="html">Simple axum server with TLS</title><link href="http://www.dingostick.com/code/2024/12/01/simple-axum-server-with-tls.html" rel="alternate" type="text/html" title="Simple axum server with TLS" /><published>2024-12-01T18:57:09+00:00</published><updated>2024-12-01T18:57:09+00:00</updated><id>http://www.dingostick.com/code/2024/12/01/simple-axum-server-with-tls</id><content type="html" xml:base="http://www.dingostick.com/code/2024/12/01/simple-axum-server-with-tls.html"><![CDATA[<p>I’ve begun moving from <a href="https://aeshirey.github.io/code/2022/01/11/understanding-warp.html"><code class="language-plaintext highlighter-rouge">warp</code></a> to <code class="language-plaintext highlighter-rouge">axum</code> with my Rust projects for various reasons. One project requires TLS, so I had to figure out the appropriate way to use certs in axum. Here’s the simple version, based off of <a href="https://github.com/tokio-rs/axum/blob/main/examples/tls-rustls/src/main.rs">axum’s tls-rustls example</a>. First, add some dependencies to your Cargo.toml:</p>

<div class="language-toml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[dependencies]</span>
<span class="py">axum</span> <span class="p">=</span> <span class="s">"0.7"</span>
<span class="nn">axum-server</span> <span class="o">=</span> <span class="p">{</span> <span class="py">version</span> <span class="p">=</span> <span class="s">"0.7"</span><span class="p">,</span> <span class="py">features</span> <span class="p">=</span> <span class="nn">["tls-rustls"]</span> <span class="p">}</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.40.0"</span><span class="p">,</span> <span class="py">features</span> <span class="p">=</span> <span class="nn">["rt-multi-thread"]</span> <span class="p">}</span>
</code></pre></div></div>

<p>Then, we’ll start with the basic version that does TLS:</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">net</span><span class="p">::</span><span class="n">SocketAddr</span><span class="p">;</span>

<span class="k">use</span> <span class="nn">axum</span><span class="p">::{</span><span class="nn">routing</span><span class="p">::</span><span class="n">get</span><span class="p">,</span> <span class="n">Router</span><span class="p">};</span>

<span class="k">use</span> <span class="nn">axum_server</span><span class="p">::</span><span class="nn">tls_rustls</span><span class="p">::</span><span class="n">RustlsConfig</span><span class="p">;</span>

<span class="k">const</span> <span class="n">HTTPS_PORT</span><span class="p">:</span> <span class="nb">u16</span> <span class="o">=</span> <span class="mi">8443</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="p">{</span>
    <span class="k">let</span> <span class="n">app</span> <span class="o">=</span> <span class="nn">Router</span><span class="p">::</span><span class="nf">new</span><span class="p">()</span><span class="nf">.route</span><span class="p">(</span><span class="s">"/"</span><span class="p">,</span> <span class="nf">get</span><span class="p">(</span><span class="n">root</span><span class="p">));</span>

    <span class="k">let</span> <span class="n">config</span> <span class="o">=</span> <span class="nn">RustlsConfig</span><span class="p">::</span><span class="nf">from_pem_file</span><span class="p">(</span><span class="s">"cert3.pem"</span><span class="p">,</span> <span class="s">"privkey3.pem"</span><span class="p">)</span>
        <span class="k">.await</span>
        <span class="nf">.unwrap</span><span class="p">();</span>

    <span class="k">let</span> <span class="n">addr</span> <span class="o">=</span> <span class="nn">SocketAddr</span><span class="p">::</span><span class="nf">from</span><span class="p">(([</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">],</span> <span class="n">HTTPS_PORT</span><span class="p">));</span>

    <span class="nn">axum_server</span><span class="p">::</span><span class="nf">bind_rustls</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="n">config</span><span class="p">)</span>
        <span class="nf">.serve</span><span class="p">(</span><span class="n">app</span><span class="nf">.into_make_service</span><span class="p">())</span>
        <span class="k">.await</span>
        <span class="nf">.unwrap</span><span class="p">();</span>
<span class="p">}</span>

<span class="k">async</span> <span class="k">fn</span> <span class="nf">root</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="o">&amp;</span><span class="k">'static</span> <span class="nb">str</span> <span class="p">{</span>
    <span class="s">"Hello, World!"</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The only real important parts here are the use of a specific port for HTTPS (see below) and the use of the <a href="https://docs.rs/axum-server/latest/axum_server/tls_rustls/struct.RustlsConfig.html#method.from_pem_file"><code class="language-plaintext highlighter-rouge">from_pem_file</code></a> function. (There are both <a href="https://docs.rs/axum-server/latest/axum_server/tls_rustls/index.html"><code class="language-plaintext highlighter-rouge">tls_rustls</code></a> and <a href="https://docs.rs/axum-server/latest/axum_server/tls_openssl/index.html"><code class="language-plaintext highlighter-rouge">tls_openssl</code></a> modules for your TLS implementation of choice.)</p>

<p>One thing you might want is an HTTP-to-HTTPS redirect: if a user hits your server with the <code class="language-plaintext highlighter-rouge">http</code> scheme, you can automatically redirect to an <code class="language-plaintext highlighter-rouge">https</code> endpoint. This requires a few more imports, a function to do the redirect, and a spawned task. A complete, updated version of the above code:</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">net</span><span class="p">::</span><span class="n">SocketAddr</span><span class="p">;</span>

<span class="k">use</span> <span class="nn">axum</span><span class="p">::{</span>
    <span class="nn">extract</span><span class="p">::</span><span class="n">Host</span><span class="p">,</span>
    <span class="nn">handler</span><span class="p">::</span><span class="n">HandlerWithoutStateExt</span><span class="p">,</span>
    <span class="nn">http</span><span class="p">::{</span><span class="n">StatusCode</span><span class="p">,</span> <span class="n">Uri</span><span class="p">},</span>
    <span class="nn">response</span><span class="p">::</span><span class="n">Redirect</span><span class="p">,</span>
    <span class="nn">routing</span><span class="p">::</span><span class="n">get</span><span class="p">,</span>
    <span class="n">BoxError</span><span class="p">,</span> <span class="n">Router</span><span class="p">,</span>
<span class="p">};</span>

<span class="k">use</span> <span class="nn">axum_server</span><span class="p">::</span><span class="nn">tls_rustls</span><span class="p">::</span><span class="n">RustlsConfig</span><span class="p">;</span>

<span class="k">const</span> <span class="n">HTTP_PORT</span><span class="p">:</span> <span class="nb">u16</span> <span class="o">=</span> <span class="mi">8080</span><span class="p">;</span>
<span class="k">const</span> <span class="n">HTTPS_PORT</span><span class="p">:</span> <span class="nb">u16</span> <span class="o">=</span> <span class="mi">8443</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="p">{</span>
    <span class="k">let</span> <span class="n">app</span> <span class="o">=</span> <span class="nn">Router</span><span class="p">::</span><span class="nf">new</span><span class="p">()</span><span class="nf">.route</span><span class="p">(</span><span class="s">"/"</span><span class="p">,</span> <span class="nf">get</span><span class="p">(</span><span class="n">root</span><span class="p">));</span>

    <span class="nn">tokio</span><span class="p">::</span><span class="nf">spawn</span><span class="p">(</span><span class="nf">redirect_http_to_https</span><span class="p">());</span>

    <span class="k">let</span> <span class="n">config</span> <span class="o">=</span> <span class="nn">RustlsConfig</span><span class="p">::</span><span class="nf">from_pem_file</span><span class="p">(</span><span class="s">"cert3.pem"</span><span class="p">,</span> <span class="s">"privkey3.pem"</span><span class="p">)</span>
        <span class="k">.await</span>
        <span class="nf">.unwrap</span><span class="p">();</span>

    <span class="k">let</span> <span class="n">addr</span> <span class="o">=</span> <span class="nn">SocketAddr</span><span class="p">::</span><span class="nf">from</span><span class="p">(([</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">],</span> <span class="n">HTTPS_PORT</span><span class="p">));</span>

    <span class="nn">axum_server</span><span class="p">::</span><span class="nf">bind_rustls</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="n">config</span><span class="p">)</span>
        <span class="nf">.serve</span><span class="p">(</span><span class="n">app</span><span class="nf">.into_make_service</span><span class="p">())</span>
        <span class="k">.await</span>
        <span class="nf">.unwrap</span><span class="p">();</span>
<span class="p">}</span>

<span class="k">async</span> <span class="k">fn</span> <span class="nf">root</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="o">&amp;</span><span class="k">'static</span> <span class="nb">str</span> <span class="p">{</span>
    <span class="s">"Hello, World!"</span>
<span class="p">}</span>

<span class="k">async</span> <span class="k">fn</span> <span class="nf">redirect_http_to_https</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">make_https</span><span class="p">(</span><span class="n">host</span><span class="p">:</span> <span class="nb">String</span><span class="p">,</span> <span class="n">uri</span><span class="p">:</span> <span class="n">Uri</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="n">Uri</span><span class="p">,</span> <span class="n">BoxError</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="k">let</span> <span class="k">mut</span> <span class="n">parts</span> <span class="o">=</span> <span class="n">uri</span><span class="nf">.into_parts</span><span class="p">();</span>

        <span class="n">parts</span><span class="py">.scheme</span> <span class="o">=</span> <span class="nf">Some</span><span class="p">(</span><span class="nn">axum</span><span class="p">::</span><span class="nn">http</span><span class="p">::</span><span class="nn">uri</span><span class="p">::</span><span class="nn">Scheme</span><span class="p">::</span><span class="n">HTTPS</span><span class="p">);</span>

        <span class="k">if</span> <span class="n">parts</span><span class="py">.path_and_query</span><span class="nf">.is_none</span><span class="p">()</span> <span class="p">{</span>
            <span class="n">parts</span><span class="py">.path_and_query</span> <span class="o">=</span> <span class="nf">Some</span><span class="p">(</span><span class="s">"/"</span><span class="nf">.parse</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">());</span>
        <span class="p">}</span>

        <span class="k">let</span> <span class="n">https_host</span> <span class="o">=</span> <span class="n">host</span><span class="nf">.replace</span><span class="p">(</span><span class="o">&amp;</span><span class="n">HTTP_PORT</span><span class="nf">.to_string</span><span class="p">(),</span> <span class="o">&amp;</span><span class="n">HTTPS_PORT</span><span class="nf">.to_string</span><span class="p">());</span>
        <span class="n">parts</span><span class="py">.authority</span> <span class="o">=</span> <span class="nf">Some</span><span class="p">(</span><span class="n">https_host</span><span class="nf">.parse</span><span class="p">()</span><span class="o">?</span><span class="p">);</span>

        <span class="nf">Ok</span><span class="p">(</span><span class="nn">Uri</span><span class="p">::</span><span class="nf">from_parts</span><span class="p">(</span><span class="n">parts</span><span class="p">)</span><span class="o">?</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">let</span> <span class="n">redirect</span> <span class="o">=</span> <span class="k">move</span> <span class="p">|</span><span class="nf">Host</span><span class="p">(</span><span class="n">host</span><span class="p">):</span> <span class="n">Host</span><span class="p">,</span> <span class="n">uri</span><span class="p">:</span> <span class="n">Uri</span><span class="p">|</span> <span class="k">async</span> <span class="k">move</span> <span class="p">{</span>
        <span class="k">match</span> <span class="nf">make_https</span><span class="p">(</span><span class="n">host</span><span class="p">,</span> <span class="n">uri</span><span class="p">)</span> <span class="p">{</span>
            <span class="nf">Ok</span><span class="p">(</span><span class="n">uri</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="nf">Ok</span><span class="p">(</span><span class="nn">Redirect</span><span class="p">::</span><span class="nf">permanent</span><span class="p">(</span><span class="o">&amp;</span><span class="n">uri</span><span class="nf">.to_string</span><span class="p">())),</span>
            <span class="nf">Err</span><span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="nf">Err</span><span class="p">(</span><span class="nn">StatusCode</span><span class="p">::</span><span class="n">BAD_REQUEST</span><span class="p">),</span>
        <span class="p">}</span>
    <span class="p">};</span>

    <span class="k">let</span> <span class="n">addr</span> <span class="o">=</span> <span class="nn">SocketAddr</span><span class="p">::</span><span class="nf">from</span><span class="p">(([</span><span class="mi">127</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">],</span> <span class="n">HTTP_PORT</span><span class="p">));</span>
    <span class="k">let</span> <span class="n">listener</span> <span class="o">=</span> <span class="nn">tokio</span><span class="p">::</span><span class="nn">net</span><span class="p">::</span><span class="nn">TcpListener</span><span class="p">::</span><span class="nf">bind</span><span class="p">(</span><span class="n">addr</span><span class="p">)</span><span class="k">.await</span><span class="nf">.unwrap</span><span class="p">();</span>

    <span class="nn">axum</span><span class="p">::</span><span class="nf">serve</span><span class="p">(</span><span class="n">listener</span><span class="p">,</span> <span class="n">redirect</span><span class="nf">.into_make_service</span><span class="p">())</span>
        <span class="k">.await</span>
        <span class="nf">.unwrap</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Note that HTTP and HTTPS <em>must</em> use different ports, else you’ll get an “Address already in use” error when trying to bind multiple schemes.</p>]]></content><author><name></name></author><category term="code" /><category term="rust" /><summary type="html"><![CDATA[I’ve begun moving from warp to axum with my Rust projects for various reasons. One project requires TLS, so I had to figure out the appropriate way to use certs in axum. Here’s the simple version, based off of axum’s tls-rustls example. First, add some dependencies to your Cargo.toml:]]></summary></entry><entry><title type="html">Passwordless ssh</title><link href="http://www.dingostick.com/code/2024/06/23/passwordless-ssh.html" rel="alternate" type="text/html" title="Passwordless ssh" /><published>2024-06-23T16:11:37+00:00</published><updated>2024-06-23T16:11:37+00:00</updated><id>http://www.dingostick.com/code/2024/06/23/passwordless-ssh</id><content type="html" xml:base="http://www.dingostick.com/code/2024/06/23/passwordless-ssh.html"><![CDATA[<p>I kept having to re-learn how to do passwordless authentication with ssh in Linux, so here’s the cheat sheet. For the purposes of discussion, <em>client</em> is the machine from which you are connecting, and <em>server</em> is the host machine to which you will connect. That is to say:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>adam@client:~$ ssh server
adam@server's password:
</code></pre></div></div>

<p>On <em>client</em>, generate a key:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>adam@client:~$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/adam/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/adam/.ssh/id_rsa
Your public key has been saved in /home/adam/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:(...) adam@client
The key's randomart image is:
(...)
</code></pre></div></div>

<p>The public key, found in <code class="language-plaintext highlighter-rouge">~/.ssh/id_rsa.pub</code>, will now contain a line that looks like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh-rsa Xy5+In4uXy5+In4uXy5+In4uXy5+In4uX18ufiJ+Ll8ufiJ+Ll8ufiJ+Ll8ufiJ+Ll8ufiJ+Ll8ufiJ+Ll8ufiJ+Ll8ufiJ+Ll8ufiJ+Ll8ufiJ+Ll8ufiJ+Ll8ufiJ+Ll9fLn4ifi5fLn4ifi5fLn4ifi5fLn4ifi5fCl8ufiJ+Ll8ufiJ+Ll8ufiJ+Ll8ufiJ+Ll9fLn4ifi5fLn4ifi5fLn4ifi5fLn4ifi5fICBOb3RoaW5nIHRvIHNlZSBoZXJlICBfLn4ifi5fLn4ifi5fLn4ifi5fLn4ifi5fXy5+In4uXy5+In4uXy5+In4uXy5+In4uXwpfLn4ifi5fLn4ifi5fLn4ifi5fLn4ifi5fXy5+In4uXy5+In4uXy5+In4uXy5+In4uXy5+In4uXy5+In4uXy5+In4uXy5+In4uXy5+In4uXy5+In4uXy5+In4uXy5+In4uX18ufiJ+Ll8ufiJ+Ll8ufiJ+Ll8ufiJ+Ll8K adam@client
</code></pre></div></div>

<p>Copy this line and connect (with your password) to <em>server</em>. If it doesn’t already exist, create <code class="language-plaintext highlighter-rouge">~/.ssh/authorized_keys</code>. Append the above line to that file. You should now be able to connect from <em>client</em> to <em>server</em> without a password.</p>

<p>Note: If you want to connect in the other direction, you will need to perform the same steps: generate a key with <code class="language-plaintext highlighter-rouge">ssh-keygen</code> on <em>server</em> and its id_rsa.pub line to <em>client</em>.</p>

<h2 id="making-this-work-with-vs-code">Making this work with VS Code</h2>
<p>To make your VS Code automatically authenticate to a remote host, you’ll want to copy the newly-created <code class="language-plaintext highlighter-rouge">id_rsa</code> (<em>not</em> the <code class="language-plaintext highlighter-rouge">id_rsa.pub</code>) file to <code class="language-plaintext highlighter-rouge">C:\Users\jquser\.ssh\</code>. For example, from within WSL:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>adam@client:~<span class="nv">$ </span><span class="nb">cp</span> .ssh/id_rsa /mnt/c/Users/adam/.ssh/
</code></pre></div></div>]]></content><author><name></name></author><category term="code" /><summary type="html"><![CDATA[I kept having to re-learn how to do passwordless authentication with ssh in Linux, so here’s the cheat sheet. For the purposes of discussion, client is the machine from which you are connecting, and server is the host machine to which you will connect. That is to say:]]></summary></entry><entry><title type="html">Basic notes on Docker</title><link href="http://www.dingostick.com/code/2024/02/18/basic-notes-on-docker.html" rel="alternate" type="text/html" title="Basic notes on Docker" /><published>2024-02-18T02:30:33+00:00</published><updated>2024-02-18T02:30:33+00:00</updated><id>http://www.dingostick.com/code/2024/02/18/basic-notes-on-docker</id><content type="html" xml:base="http://www.dingostick.com/code/2024/02/18/basic-notes-on-docker.html"><![CDATA[<p>Some notes I took a while back on learning the basics of <a href="https://www.docker.com/">Docker</a>.</p>

<h1 id="managing-images">Managing images</h1>
<p>Lets use <a href="https://hub.docker.com/r/bitnami/minideb">minideb</a>, a small Debian-based linux:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker pull bitnami/minideb
Using default tag: latest
latest: Pulling from bitnami/minideb
ba49d470d895: Pull <span class="nb">complete
</span>Digest: sha256:cbbc1db2617a7e5224f8dc692c990b723e4fe3ef69864544e7c14aa613c0ccb7
Status: Downloaded newer image <span class="k">for </span>bitnami/minideb:latest
docker.io/bitnami/minideb:latest
</code></pre></div></div>

<p>We can see this new image is available locally with <code class="language-plaintext highlighter-rouge">docker images</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker images
REPOSITORY        TAG       IMAGE ID       CREATED      SIZE
bitnami/minideb   latest    c5eecd6244a8   3 days ago   120MB
</code></pre></div></div>

<p>And we can remove it with <code class="language-plaintext highlighter-rouge">docker image rm &lt;id&gt;</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker image <span class="nb">rm </span>c5eecd6244a8
Untagged: bitnami/minideb:latest
Untagged: bitnami/minideb@sha256:cbbc1db2617a7e5224f8dc692c990b723e4fe3ef69864544e7c14aa613c0ccb7
Deleted: sha256:c5eecd6244a829084e2f788e3f877a5ab8ac63f9c8dc55c3cfff4f1d172fc23c
Deleted: sha256:44b47439f86a658d61565e3a9e86c1c9608b2ee8adb4f6e85005634e6f537f43

<span class="nv">$ </span>docker images
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE
</code></pre></div></div>

<h1 id="running-images-in-containers">Running images in containers</h1>

<p>We could run this image in a new container with <code class="language-plaintext highlighter-rouge">docker run c5eecd6244a8</code>, but it would almost immediately return to our console. With <code class="language-plaintext highlighter-rouge">docker container ls -a</code>, we’d see that this container ran and terminated:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker container <span class="nb">ls</span> <span class="nt">-a</span>
CONTAINER ID   IMAGE          COMMAND       CREATED          STATUS                      PORTS     NAMES
ff11c7f3afb8   c5eecd6244a8   <span class="s2">"/bin/bash"</span>   29 seconds ago   Exited <span class="o">(</span>0<span class="o">)</span> 28 seconds ago             clever_franklin

<span class="c"># Delete this terminated container</span>
<span class="nv">$ </span>docker container <span class="nb">rm </span>ff11c7f3afb8
</code></pre></div></div>

<p>What we want is to run <em>interactively</em>, so we’ll use <code class="language-plaintext highlighter-rouge">docker run -it &lt;id&gt;</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># In the host:</span>
<span class="nv">$ </span>docker run <span class="nt">-it</span> c5eecd6244a8

<span class="c"># In the container!</span>
root@25bca2749327:/# <span class="nb">uname</span> <span class="nt">-a</span>
Linux 25bca2749327 5.15.0-1042-azure <span class="c">#49~20.04.1-Ubuntu SMP Wed Jul 12 12:44:56 UTC 2023 x86_64 GNU/Linux</span>

root@25bca2749327:/# <span class="nb">cat</span> /etc/os-release | <span class="nb">grep </span>NAME
<span class="nv">PRETTY_NAME</span><span class="o">=</span><span class="s2">"Debian GNU/Linux 12 (bookworm)"</span>
<span class="nv">NAME</span><span class="o">=</span><span class="s2">"Debian GNU/Linux"</span>
<span class="nv">VERSION_CODENAME</span><span class="o">=</span>bookworm

root@25bca2749327:/# <span class="nb">exit</span>
</code></pre></div></div>

<p>At this point, we’re back in our host. There’s still a terminated container:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker container <span class="nb">ls</span> <span class="nt">-a</span>
CONTAINER ID   IMAGE          COMMAND       CREATED         STATUS                      PORTS     NAMES
25bca2749327   c5eecd6244a8   <span class="s2">"/bin/bash"</span>   2 minutes ago   Exited <span class="o">(</span>0<span class="o">)</span> 49 seconds ago             elegant_saha

<span class="nv">$ </span>docker container <span class="nb">rm </span>25bca2749327
</code></pre></div></div>

<p>To avoid this, use <code class="language-plaintext highlighter-rouge">docker run --rm</code> (NB, it has to be before the container name!):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker container <span class="nb">ls</span> <span class="nt">-a</span>
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

<span class="nv">$ </span>docker run <span class="nt">-it</span> <span class="nt">--rm</span> c5eecd6244a8
root@21735047c8bb:/# <span class="nb">hostname
</span>21735047c8bb

root@21735047c8bb:/# <span class="nb">exit</span>

<span class="nv">$ </span>docker container <span class="nb">ls</span> <span class="nt">-a</span>
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
</code></pre></div></div>

<h1 id="creating-a-container">Creating a container</h1>
<p>Let’s make use of the minideb image as the basis for a derived image. We use a <a href="https://docs.docker.com/engine/reference/builder/">Dockerfile</a> to describe the image we’ll create:</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># The base image for our new image</span>
<span class="k">FROM</span><span class="s"> bitnami/minideb</span>

<span class="c"># For a simple Rust service, see:</span>
<span class="c"># https://aeshirey.github.io/code/2023/02/25/simple-rust-service-in-docker.html</span>
<span class="c"># COPY &lt;host-filename&gt; &lt;docker-filanem&gt;</span>
<span class="k">COPY</span><span class="s"> rust-server my-rust-server</span>

<span class="k">CMD</span><span class="s"> ["./my-rust-server"]</span>
</code></pre></div></div>

<p>To build this, we can use <code class="language-plaintext highlighter-rouge">docker build &lt;path&gt;</code>, where <code class="language-plaintext highlighter-rouge">&lt;path&gt;</code> is the directory in which the Dockerfile lives (eg, <code class="language-plaintext highlighter-rouge">.</code>). Additionally, we’ll use the <code class="language-plaintext highlighter-rouge">-t &lt;name&gt;:&lt;tag&gt;</code> to give our image a name and tag. If the tag is omitted, <em>latest</em> is used.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker images
REPOSITORY        TAG       IMAGE ID       CREATED      SIZE
bitnami/minideb   latest    c5eecd6244a8   3 days ago   120MB

<span class="nv">$ </span>docker build <span class="nb">.</span> <span class="nt">-t</span> my-simple-container
 <span class="o">=&gt;</span> <span class="o">[</span>internal] load .dockerignore                                                                  0.0s
 <span class="o">=&gt;</span> <span class="o">=&gt;</span> transferring context: 2B                                                                    0.0s
 <span class="o">=&gt;</span> <span class="o">[</span>internal] load build definition from Dockerfile                                               0.0s
 <span class="o">=&gt;</span> <span class="o">=&gt;</span> transferring dockerfile: 141B                                                               0.0s
 <span class="o">=&gt;</span> <span class="o">[</span>internal] load metadata <span class="k">for </span>docker.io/bitnami/minideb:latest                                  0.0s
 <span class="o">=&gt;</span> <span class="o">[</span>internal] load build context                                                                  0.0s
 <span class="o">=&gt;</span> <span class="o">=&gt;</span> transferring context: 84B                                                                   0.0s
 <span class="o">=&gt;</span> <span class="o">[</span>1/2] FROM docker.io/bitnami/minideb:latest                                                    0.0s
 <span class="o">=&gt;</span> CACHED <span class="o">[</span>2/2] COPY simple-server/simple-server my-simple-server                                 0.0s
 <span class="o">=&gt;</span> exporting to image                                                                             0.0s
 <span class="o">=&gt;</span> <span class="o">=&gt;</span> exporting layers                                                                            0.0s
 <span class="o">=&gt;</span> <span class="o">=&gt;</span> writing image sha256:61a24712801a996b6ceefb378cd9ebccdb9caae8c58ea7acf17eaff0285666bb       0.0s
 <span class="o">=&gt;</span> <span class="o">=&gt;</span> naming to docker.io/library/my-simple-container                                             0.0s

<span class="nv">$ </span>docker images
REPOSITORY            TAG       IMAGE ID       CREATED              SIZE
my-simple-container   latest    61a24712801a   About a minut
bitnami/minideb       latest    c5eecd6244a8   3 days ago           120MB
</code></pre></div></div>

<p>Because our server exposes port 8080, we want our container to also expose it. Maybe we want to use the same port or maybe we want to remap it. Either way, we’ll use <code class="language-plaintext highlighter-rouge">-p &lt;host-port&gt;:&lt;container-port&gt;</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker run <span class="nt">--rm</span> <span class="nt">--init</span> <span class="nt">-p</span> 8123:8080 fd83da080eab
</code></pre></div></div>

<p>Then we can connect in another shell on our host to communicate with this container:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>curl 127.0.0.1:8123 <span class="nt">-l</span> <span class="nt">-w</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">"</span>
home
</code></pre></div></div>

<h1 id="extras">Extras</h1>
<h2 id="need-to-log-into-a-container-for-an-image-you-built-to-inspect-it">Need to ‘log into’ a container for an image you built to inspect it?</h2>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Specify 'bash' as the process to run</span>
<span class="nv">$ </span>docker run <span class="nt">-p</span> 8123:8080 <span class="nt">--rm</span> <span class="nt">-it</span> 61a24712801a bash
<span class="c">#                        image id ------^        ^-- command to run</span>
</code></pre></div></div>

<h2 id="cant-ctrl-c-from-your-docker-run">Can’t CTRL-C from your <code class="language-plaintext highlighter-rouge">docker run</code>?</h2>

<p>Oops, can’t exit this container:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker run <span class="nt">--rm</span> fd83da080eab
^C
</code></pre></div></div>

<p>From another shell:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker ps
CONTAINER ID   IMAGE          COMMAND               CREATED              STATUS              PORTS     NAMES
260c882a217e   fd83da080eab   <span class="s2">"/my-simple-server"</span>   About a minute ago   Up About a minute             gracious_hawking
<span class="c">#    ^------ this is the container we'll want to kill because oopsie</span>

<span class="nv">$ </span>docker <span class="nb">kill </span>260c882a217e
260c882a217e
</code></pre></div></div>

<p>Avoid this by <a href="https://stackoverflow.com/a/60812082/1191181">including the <code class="language-plaintext highlighter-rouge">--init</code> flag next time you <code class="language-plaintext highlighter-rouge">docker run</code></a>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker run <span class="nt">--rm</span> <span class="nt">--init</span> fd83da080eab
^C<span class="err">$</span>
</code></pre></div></div>

<h2 id="how-about-accessing-the-host-network">How about accessing the host network?</h2>
<p>If you use <code class="language-plaintext highlighter-rouge">docker run --network=host</code>, then the container will be able to access the host network. For example:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># In the host OS:</span>
<span class="nv">$ </span>./rust-server &amp;

<span class="nv">$ </span>curl 127.0.0.1:8080 <span class="nt">-w</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">"</span>
home

<span class="nv">$ </span>docker run <span class="nt">-it</span> <span class="nt">--rm</span> <span class="nt">--network</span><span class="o">=</span>host c5eecd6244a8

<span class="c"># Now in the container</span>
root@hostname:/# curl 127.0.0.1:8080 <span class="nt">-w</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">"</span>
home
<span class="c"># </span>
</code></pre></div></div>

<h2 id="exportingimporting-images">Exporting/importing images</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker images
REPOSITORY            TAG       IMAGE ID       CREATED          SIZE
&lt;none&gt;                &lt;none&gt;    61a24712801a   30 minutes ago   131MB
my-simple-container   latest    fd83da080eab   30 minutes ago   131MB
bitnami/minideb       latest    c5eecd6244a8   3 days ago       120MB

<span class="nv">$ </span>docker save fd83da080eab | <span class="nb">gzip</span> <span class="o">&gt;</span> my-simple-container.tar.gz

<span class="nv">$ </span>file my-simple-container.tar.gz
my-simple-container.tar.gz: <span class="nb">gzip </span>compressed data, from Unix, original size modulo 2^32 135666688 <span class="nb">gzip </span>compressed data, reserved method, ASCII, extra field, encrypted, from FAT filesystem <span class="o">(</span>MS-DOS, OS/2, NT<span class="o">)</span>, original size modulo 2^32 135666688

<span class="nv">$ </span><span class="nb">ls</span> <span class="nt">-lh</span> my-simple-container.tar.gz
<span class="nt">-rw-r--r--</span> 1 root root 41M Feb 17 04:48 my-simple-container.tar.gz

<span class="c"># Later/elsewhere, this can be loaded:</span>
<span class="nv">$ </span>docker load &lt; my-simple-container.tar.gz
Loaded image: my-simple-container:latest
</code></pre></div></div>]]></content><author><name></name></author><category term="code" /><category term="docker" /><summary type="html"><![CDATA[Some notes I took a while back on learning the basics of Docker.]]></summary></entry><entry><title type="html">Calling async Rust code from synchronous</title><link href="http://www.dingostick.com/code/2023/05/13/calling-async-rust-code-from-synchronous.html" rel="alternate" type="text/html" title="Calling async Rust code from synchronous" /><published>2023-05-13T02:42:52+00:00</published><updated>2023-05-13T02:42:52+00:00</updated><id>http://www.dingostick.com/code/2023/05/13/calling-async-rust-code-from-synchronous</id><content type="html" xml:base="http://www.dingostick.com/code/2023/05/13/calling-async-rust-code-from-synchronous.html"><![CDATA[<p>Sometimes I find that I have some <code class="language-plaintext highlighter-rouge">async</code> code that I really want to call from another project, but I don’t want <code class="language-plaintext highlighter-rouge">async</code>/<code class="language-plaintext highlighter-rouge">await</code> to infect my entire codebase. At least using <a href="https://docs.rs/tokio/latest/tokio/"><code class="language-plaintext highlighter-rouge">tokio</code></a>, there’s an easy way to do this. Given some async project:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cargo new <span class="nt">--lib</span> my-async-crate
cargo add tokio
</code></pre></div></div>

<p>Which exposes an <code class="language-plaintext highlighter-rouge">async</code> function:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">async</span> <span class="k">fn</span> <span class="nf">sleep_a_bit</span><span class="p">(</span><span class="n">num_seconds</span><span class="p">:</span> <span class="nb">u64</span><span class="p">)</span> <span class="p">{</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"Hold please..."</span><span class="p">);</span>
    <span class="nn">tokio</span><span class="p">::</span><span class="nn">time</span><span class="p">::</span><span class="nf">sleep</span><span class="p">(</span><span class="nn">std</span><span class="p">::</span><span class="nn">time</span><span class="p">::</span><span class="nn">Duration</span><span class="p">::</span><span class="nf">from_secs</span><span class="p">(</span><span class="n">num_seconds</span><span class="p">))</span><span class="k">.await</span><span class="p">;</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"Thanks for waiting!"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We then have a project which wants to use our cool <code class="language-plaintext highlighter-rouge">sleep_a_bit</code> function:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cargo new my-project
</code></pre></div></div>

<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="nn">my_async_crate</span><span class="p">::</span><span class="nf">sleep_a_bit</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This compiles, but it doesn’t do what we want:</p>

<pre><code class="language-ignore">warning: unused implementer of `Future` that must be used
 --&gt; src/main.rs:2:5
  |
2 |     my_async_crate::do_async_await(5);
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: futures do nothing unless you `.await` or poll them
  = note: `#[warn(unused_must_use)]` on by default
</code></pre>

<p>If we run this project, it will immediately exit. Thus, we need to include the <code class="language-plaintext highlighter-rouge">.await</code> call:</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="c1">// ---- this is not `async`</span>
    <span class="nn">my_async_crate</span><span class="p">::</span><span class="nf">sleep_a_bit</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span><span class="k">.await</span><span class="p">;</span>
    <span class="c1">//                             ^^^^^^ only allowed inside `async` functions and blocks</span>
<span class="p">}</span>
</code></pre></div></div>

<p>What we need is to create a tokio <a href="https://docs.rs/tokio/latest/tokio/runtime/struct.Runtime.html"><code class="language-plaintext highlighter-rouge">Runtime</code></a> that can synchronously block until the inner asynchronous operations complete. To do this, we add tokio with the <code class="language-plaintext highlighter-rouge">rt</code> feature:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cargo add tokio <span class="nt">--features</span> rt
</code></pre></div></div>

<p>The main function then creates the runtime and creates a <code class="language-plaintext highlighter-rouge">Future</code>. For this example, we’ll just <a href="https://docs.rs/tokio/latest/tokio/runtime/struct.Runtime.html#method.block_on"><code class="language-plaintext highlighter-rouge">block_on</code></a>:</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="k">let</span> <span class="n">rt</span> <span class="o">=</span> <span class="nn">tokio</span><span class="p">::</span><span class="nn">runtime</span><span class="p">::</span><span class="nn">Runtime</span><span class="p">::</span><span class="nf">new</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">();</span>

    <span class="n">rt</span><span class="nf">.block_on</span><span class="p">(</span><span class="k">async</span> <span class="p">{</span>
        <span class="nn">my_async_crate</span><span class="p">::</span><span class="nf">sleep_a_bit</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span><span class="k">.await</span><span class="p">;</span>
        <span class="nd">println!</span><span class="p">(</span><span class="s">"And we're back!"</span><span class="p">);</span>
    <span class="p">});</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name></name></author><category term="code" /><category term="rust" /><summary type="html"><![CDATA[Sometimes I find that I have some async code that I really want to call from another project, but I don’t want async/await to infect my entire codebase. At least using tokio, there’s an easy way to do this. Given some async project:]]></summary></entry><entry><title type="html">Visitor Deserialization in Rust</title><link href="http://www.dingostick.com/code/2023/04/04/visitor-deserialization-in-rust.html" rel="alternate" type="text/html" title="Visitor Deserialization in Rust" /><published>2023-04-04T15:21:06+00:00</published><updated>2023-04-04T15:21:06+00:00</updated><id>http://www.dingostick.com/code/2023/04/04/visitor-deserialization-in-rust</id><content type="html" xml:base="http://www.dingostick.com/code/2023/04/04/visitor-deserialization-in-rust.html"><![CDATA[<p>Consider the following input JSON:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
   </span><span class="nl">"documents"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="p">{</span><span class="w"> </span><span class="nl">"foo"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w"> </span><span class="nl">"baz"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w"> </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w"> </span><span class="nl">"bar"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="w"> </span><span class="p">}</span><span class="w">
   </span><span class="p">],</span><span class="w">
   </span><span class="nl">"journal"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2023-04-04T08:28:00"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>If we assume that each inner ‘document’ should be simply treated as an arbitrary JSON <a href="https://docs.rs/serde_json/latest/serde_json/enum.Value.html"><code class="language-plaintext highlighter-rouge">Value</code></a>, we can model and read our input as:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[derive(Deserialize,</span> <span class="nd">Debug)]</span>
<span class="k">struct</span> <span class="n">MyData</span> <span class="p">{</span>
    <span class="n">documents</span><span class="p">:</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">Value</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="n">journal</span><span class="p">:</span> <span class="n">Value</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="k">let</span> <span class="n">json</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="s">"input.json"</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span>
    <span class="k">let</span> <span class="n">mydata</span><span class="p">:</span> <span class="n">MyData</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="o">&amp;</span><span class="n">json</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"{mydata:?}"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>But what if we only need a subset of ‘documents’ and/or need to process each into something else, and they are exceedingly large? This would cause significant memory overhead that we want to avoid. One possibility is to roll your own string reading mechanism, trying to figure out when one document starts and ends, then parsing only <em>that</em> string. This becomes a bit cumbersome, but worse still is that it may be error prone when trying to deal with the arbitrary <code class="language-plaintext highlighter-rouge">journal</code> value: how do we know if we’ve finished reading the last document and have arrived at the journal? What if a document legitimately contains a <code class="language-plaintext highlighter-rouge">"journal"</code> key?</p>

<p>Fortunately, <a href="https://docs.rs/serde/latest/serde/"><code class="language-plaintext highlighter-rouge">serde</code></a> contains the capability to do custom serialization and deserialization <em>and</em> to use a <a href="https://docs.rs/serde/latest/serde/de/trait.Visitor.html">visitor pattern</a>. We can use this approach to handle each document in succession. To do so, we’ll create a new type that represents our documents:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[derive(Debug)]</span>
<span class="k">struct</span> <span class="nf">Documents</span><span class="p">(</span><span class="nb">Vec</span><span class="o">&lt;</span><span class="n">Value</span><span class="o">&gt;</span><span class="p">);</span>

<span class="nd">#[derive(Deserialize,</span> <span class="nd">Debug)]</span>
<span class="k">struct</span> <span class="n">MyData</span> <span class="p">{</span>
    <span class="n">documents</span><span class="p">:</span> <span class="n">Documents</span><span class="p">,</span>
    <span class="n">journal</span><span class="p">:</span> <span class="n">Value</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Structurally, this is the same as before, but it allows us to insert our own, manual deserialization step – note that <code class="language-plaintext highlighter-rouge">MyData</code> implements <code class="language-plaintext highlighter-rouge">Deserialize</code> but <code class="language-plaintext highlighter-rouge">Documents</code> doesn’t. The deserialization implementation stub looks like this:</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="nv">'de</span><span class="o">&gt;</span> <span class="n">Deserialize</span><span class="o">&lt;</span><span class="nv">'de</span><span class="o">&gt;</span> <span class="k">for</span> <span class="n">Documents</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="n">deserialize</span><span class="o">&lt;</span><span class="n">D</span><span class="o">&gt;</span><span class="p">(</span><span class="n">deserializer</span><span class="p">:</span> <span class="n">D</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="k">Self</span><span class="p">,</span> <span class="nn">D</span><span class="p">::</span><span class="n">Error</span><span class="o">&gt;</span>
    <span class="k">where</span>
        <span class="n">D</span><span class="p">:</span> <span class="nn">serde</span><span class="p">::</span><span class="n">Deserializer</span><span class="o">&lt;</span><span class="nv">'de</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="p">}</span>
</code></pre></div></div>

<p>Before implementing this part, we’ll create the visitor. First, the type that will know how to deserialize our documents:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">DocumentVisitor</span><span class="p">;</span>
</code></pre></div></div>

<p>Note that <code class="language-plaintext highlighter-rouge">DocumentVisitor</code> itself doesn’t collect <code class="language-plaintext highlighter-rouge">Value</code>s – it just <em>knows how</em> to deserialize them. Serde’s visitor pattern has an associated type that will be the (collected) result of deserialization. This output is what we will have filtered and/or processed from each raw JSON value from input. Here’s the stub for the visitor:</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="nv">'de</span><span class="o">&gt;</span> <span class="nn">serde</span><span class="p">::</span><span class="nn">de</span><span class="p">::</span><span class="n">Visitor</span><span class="o">&lt;</span><span class="nv">'de</span><span class="o">&gt;</span> <span class="k">for</span> <span class="n">DocumentVisitor</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Value</span><span class="p">;</span>

    <span class="k">fn</span> <span class="nf">expecting</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">,</span> <span class="n">formatter</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="nn">std</span><span class="p">::</span><span class="nn">fmt</span><span class="p">::</span><span class="n">Formatter</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">std</span><span class="p">::</span><span class="nn">fmt</span><span class="p">::</span><span class="nb">Result</span> <span class="p">{</span>
        <span class="nd">todo!</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><a href="https://docs.rs/serde/latest/serde/de/trait.Visitor.html#tymethod.expecting"><code class="language-plaintext highlighter-rouge">expecting</code></a> is a required method that:</p>

<blockquote>
  <p>[Formats] a message stating what data this Visitor expects to receive. … The message should complete the sentence “This Visitor expects to receive …”,</p>
</blockquote>

<p>Because our visitor expects a list of documents, we’ll say that:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">expecting</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">,</span> <span class="n">formatter</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="nn">std</span><span class="p">::</span><span class="nn">fmt</span><span class="p">::</span><span class="n">Formatter</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">std</span><span class="p">::</span><span class="nn">fmt</span><span class="p">::</span><span class="nb">Result</span> <span class="p">{</span>
    <span class="nd">write!</span><span class="p">(</span><span class="n">formatter</span><span class="p">,</span> <span class="s">"a list of JSON values"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Also because we’re expecting a list (or <em>sequence</em>) of items, we’ll override the <a href="https://docs.rs/serde/latest/serde/de/trait.Visitor.html#method.visit_seq"><code class="language-plaintext highlighter-rouge">visit_seq</code> method</a>. We also set the <a href="https://docs.rs/serde/latest/serde/de/trait.Visitor.html#associatedtype.Value">required associated type, <code class="language-plaintext highlighter-rouge">Value</code></a>, indicating what kind of value this visitor will be returning. (Note that here, the associated type <code class="language-plaintext highlighter-rouge">Value</code> is not the same as <code class="language-plaintext highlighter-rouge">serde_json::Value</code>. The former is what we’ll be telling serde that we’ll return, which is a <code class="language-plaintext highlighter-rouge">Vec&lt;Value&gt;</code>. The latter is specific to JSON data.) In <code class="language-plaintext highlighter-rouge">visit_seq</code>, we’ll repeatedly call <code class="language-plaintext highlighter-rouge">seq.next_element()</code>, propagating up any errors that serde gives us. For now, we’ll just push each item onto a vector that we’ll return:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="k">type</span> <span class="n">Value</span> <span class="o">=</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">Value</span><span class="o">&gt;</span><span class="p">;</span>

    <span class="k">fn</span> <span class="n">visit_seq</span><span class="o">&lt;</span><span class="n">A</span><span class="o">&gt;</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="k">mut</span> <span class="n">seq</span><span class="p">:</span> <span class="n">A</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="k">Self</span><span class="p">::</span><span class="n">Value</span><span class="p">,</span> <span class="nn">A</span><span class="p">::</span><span class="n">Error</span><span class="o">&gt;</span>
    <span class="k">where</span>
        <span class="n">A</span><span class="p">:</span> <span class="nn">serde</span><span class="p">::</span><span class="nn">de</span><span class="p">::</span><span class="n">SeqAccess</span><span class="o">&lt;</span><span class="nv">'de</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="p">{</span>
        <span class="k">let</span> <span class="k">mut</span> <span class="n">values</span> <span class="o">=</span> <span class="nn">Vec</span><span class="p">::</span><span class="nf">new</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">item</span><span class="p">)</span> <span class="o">=</span> <span class="n">seq</span><span class="nf">.next_element</span><span class="p">()</span><span class="o">?</span> <span class="p">{</span>
            <span class="nd">println!</span><span class="p">(</span><span class="s">"Read item='{item}'"</span><span class="p">);</span>
            <span class="n">values</span><span class="nf">.push</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="nf">Ok</span><span class="p">(</span><span class="n">values</span><span class="p">)</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>That completes the visitor, and we can now implement <code class="language-plaintext highlighter-rouge">Deserialize for Documents</code>. We’ll instantiate a visitor, which is passed to the <a href="https://docs.rs/serde/latest/serde/de/trait.Deserializer.html#tymethod.deserialize_seq">deserializer’s <code class="language-plaintext highlighter-rouge">deserialize_seq</code> method</a>:</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="nv">'de</span><span class="o">&gt;</span> <span class="n">Deserialize</span><span class="o">&lt;</span><span class="nv">'de</span><span class="o">&gt;</span> <span class="k">for</span> <span class="n">Documents</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="n">deserialize</span><span class="o">&lt;</span><span class="n">D</span><span class="o">&gt;</span><span class="p">(</span><span class="n">deserializer</span><span class="p">:</span> <span class="n">D</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="k">Self</span><span class="p">,</span> <span class="nn">D</span><span class="p">::</span><span class="n">Error</span><span class="o">&gt;</span>
    <span class="k">where</span>
        <span class="n">D</span><span class="p">:</span> <span class="nn">serde</span><span class="p">::</span><span class="n">Deserializer</span><span class="o">&lt;</span><span class="nv">'de</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="p">{</span>
        <span class="k">let</span> <span class="n">visitor</span> <span class="o">=</span> <span class="n">DocumentVisitor</span><span class="p">;</span>
        <span class="k">let</span> <span class="n">docs</span> <span class="o">=</span> <span class="n">deserializer</span><span class="nf">.deserialize_seq</span><span class="p">(</span><span class="n">visitor</span><span class="p">)</span><span class="o">?</span><span class="p">;</span>
        <span class="nf">Ok</span><span class="p">(</span><span class="nf">Documents</span><span class="p">(</span><span class="n">docs</span><span class="p">))</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Note that by passing a <code class="language-plaintext highlighter-rouge">DocumentVisitor</code> to the deserializer, serde knows that it will be returning a <code class="language-plaintext highlighter-rouge">Vec&lt;Value&gt;</code> (by virtue of the associated type). Thus, that is the type of <code class="language-plaintext highlighter-rouge">docs</code>. Our <code class="language-plaintext highlighter-rouge">Deserialize</code> implementation returns a <code class="language-plaintext highlighter-rouge">Documents</code> object, so we wrap <code class="language-plaintext highlighter-rouge">docs</code> in that.</p>

<h2 id="full-implementation">Full implementation</h2>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[derive(Debug)]</span>
<span class="k">struct</span> <span class="nf">Documents</span><span class="p">(</span><span class="nb">Vec</span><span class="o">&lt;</span><span class="n">Value</span><span class="o">&gt;</span><span class="p">);</span>

<span class="nd">#[derive(Deserialize,</span> <span class="nd">Debug)]</span>
<span class="k">struct</span> <span class="n">MyData</span> <span class="p">{</span>
    <span class="n">documents</span><span class="p">:</span> <span class="n">Documents</span><span class="p">,</span>
    <span class="n">journal</span><span class="p">:</span> <span class="n">Value</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">impl</span><span class="o">&lt;</span><span class="nv">'de</span><span class="o">&gt;</span> <span class="n">Deserialize</span><span class="o">&lt;</span><span class="nv">'de</span><span class="o">&gt;</span> <span class="k">for</span> <span class="n">Documents</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="n">deserialize</span><span class="o">&lt;</span><span class="n">D</span><span class="o">&gt;</span><span class="p">(</span><span class="n">deserializer</span><span class="p">:</span> <span class="n">D</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="k">Self</span><span class="p">,</span> <span class="nn">D</span><span class="p">::</span><span class="n">Error</span><span class="o">&gt;</span>
    <span class="k">where</span>
        <span class="n">D</span><span class="p">:</span> <span class="nn">serde</span><span class="p">::</span><span class="n">Deserializer</span><span class="o">&lt;</span><span class="nv">'de</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="p">{</span>
        <span class="k">let</span> <span class="n">visitor</span> <span class="o">=</span> <span class="n">DocumentVisitor</span><span class="p">;</span>
        <span class="k">let</span> <span class="n">docs</span> <span class="o">=</span> <span class="n">deserializer</span><span class="nf">.deserialize_seq</span><span class="p">(</span><span class="n">visitor</span><span class="p">)</span><span class="o">?</span><span class="p">;</span>
        <span class="nf">Ok</span><span class="p">(</span><span class="nf">Documents</span><span class="p">(</span><span class="n">docs</span><span class="p">))</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">struct</span> <span class="n">DocumentVisitor</span><span class="p">;</span>

<span class="k">impl</span><span class="o">&lt;</span><span class="nv">'de</span><span class="o">&gt;</span> <span class="nn">serde</span><span class="p">::</span><span class="nn">de</span><span class="p">::</span><span class="n">Visitor</span><span class="o">&lt;</span><span class="nv">'de</span><span class="o">&gt;</span> <span class="k">for</span> <span class="n">DocumentVisitor</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Value</span> <span class="o">=</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">Value</span><span class="o">&gt;</span><span class="p">;</span>

    <span class="k">fn</span> <span class="nf">expecting</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">,</span> <span class="n">formatter</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="nn">std</span><span class="p">::</span><span class="nn">fmt</span><span class="p">::</span><span class="n">Formatter</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">std</span><span class="p">::</span><span class="nn">fmt</span><span class="p">::</span><span class="nb">Result</span> <span class="p">{</span>
        <span class="nd">write!</span><span class="p">(</span><span class="n">formatter</span><span class="p">,</span> <span class="s">"a list"</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">fn</span> <span class="n">visit_seq</span><span class="o">&lt;</span><span class="n">A</span><span class="o">&gt;</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="k">mut</span> <span class="n">seq</span><span class="p">:</span> <span class="n">A</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="k">Self</span><span class="p">::</span><span class="n">Value</span><span class="p">,</span> <span class="nn">A</span><span class="p">::</span><span class="n">Error</span><span class="o">&gt;</span>
    <span class="k">where</span>
        <span class="n">A</span><span class="p">:</span> <span class="nn">serde</span><span class="p">::</span><span class="nn">de</span><span class="p">::</span><span class="n">SeqAccess</span><span class="o">&lt;</span><span class="nv">'de</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="p">{</span>
        <span class="k">let</span> <span class="k">mut</span> <span class="n">values</span> <span class="o">=</span> <span class="nn">Vec</span><span class="p">::</span><span class="nf">new</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">item</span><span class="p">)</span> <span class="o">=</span> <span class="n">seq</span><span class="nf">.next_element</span><span class="p">()</span><span class="o">?</span> <span class="p">{</span>
            <span class="nd">println!</span><span class="p">(</span><span class="s">"Read item='{item}'"</span><span class="p">);</span>
            <span class="n">values</span><span class="nf">.push</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="nf">Ok</span><span class="p">(</span><span class="n">values</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="filtering-and-processing">Filtering and processing</h2>
<p>The above implementation reads and keeps every value of input. The whole idea here, though, was that we could filter/process our values, so let’s now update our code to do that. We’ll only keep documents that are themselves objects, then we’ll take the first key-value pair (ignoring others), skipping those with null values (eg, <code class="language-plaintext highlighter-rouge">{ "bar": null }"</code>). These first key-value pairs will be aggregated into a single object returned as a vector of one object:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="n">visit_seq</span><span class="o">&lt;</span><span class="n">A</span><span class="o">&gt;</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="k">mut</span> <span class="n">seq</span><span class="p">:</span> <span class="n">A</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="k">Self</span><span class="p">::</span><span class="n">Value</span><span class="p">,</span> <span class="nn">A</span><span class="p">::</span><span class="n">Error</span><span class="o">&gt;</span>
<span class="k">where</span>
    <span class="n">A</span><span class="p">:</span> <span class="nn">serde</span><span class="p">::</span><span class="nn">de</span><span class="p">::</span><span class="n">SeqAccess</span><span class="o">&lt;</span><span class="nv">'de</span><span class="o">&gt;</span><span class="p">,</span>
<span class="p">{</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">agg_map</span> <span class="o">=</span> <span class="nn">serde_json</span><span class="p">::</span><span class="nn">Map</span><span class="p">::</span><span class="nf">new</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">item</span><span class="p">)</span> <span class="o">=</span> <span class="n">seq</span><span class="nf">.next_element</span><span class="p">()</span><span class="o">?</span> <span class="p">{</span>
        <span class="c1">// If `item` isn't a JSON object, we'll skip it:</span>
        <span class="k">let</span> <span class="nn">Value</span><span class="p">::</span><span class="nf">Object</span><span class="p">(</span><span class="n">map</span><span class="p">)</span> <span class="o">=</span> <span class="n">item</span> <span class="k">else</span> <span class="p">{</span> <span class="k">continue</span> <span class="p">};</span>

        <span class="c1">// Get the first element, assuming we have some</span>
        <span class="k">let</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="o">=</span> <span class="k">match</span> <span class="n">map</span><span class="nf">.into_iter</span><span class="p">()</span><span class="nf">.next</span><span class="p">()</span> <span class="p">{</span>
            <span class="nf">Some</span><span class="p">(</span><span class="n">kv</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="n">kv</span><span class="p">,</span>
            <span class="nb">None</span> <span class="k">=&gt;</span> <span class="k">continue</span><span class="p">,</span>
        <span class="p">};</span>

        <span class="c1">// Ignore any null values; aggregate everything into a single map</span>
        <span class="k">if</span> <span class="n">v</span> <span class="o">==</span> <span class="nn">Value</span><span class="p">::</span><span class="n">Null</span> <span class="p">{</span>
            <span class="k">continue</span><span class="p">;</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="nd">println!</span><span class="p">(</span><span class="s">"Keeping {k}={v}"</span><span class="p">);</span>
            <span class="n">agg_map</span><span class="nf">.insert</span><span class="p">(</span><span class="n">k</span><span class="p">,</span><span class="n">v</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">let</span> <span class="n">values</span> <span class="o">=</span> <span class="nn">Value</span><span class="p">::</span><span class="nf">Object</span><span class="p">(</span><span class="n">agg_map</span><span class="p">);</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"Final value is {values}"</span><span class="p">);</span>

    <span class="nf">Ok</span><span class="p">(</span><span class="nd">vec!</span><span class="p">[</span><span class="n">values</span><span class="p">])</span>
<span class="p">}</span>
</code></pre></div></div>

<p>When running this code, the following output is printed to the console:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Keeping foo=1
Keeping baz=true
Final value is {"baz":true,"foo":1}
</code></pre></div></div>]]></content><author><name></name></author><category term="code" /><category term="rust" /><summary type="html"><![CDATA[Consider the following input JSON:]]></summary></entry><entry><title type="html">Simple Rust service in Docker</title><link href="http://www.dingostick.com/code/2023/02/25/simple-rust-service-in-docker.html" rel="alternate" type="text/html" title="Simple Rust service in Docker" /><published>2023-02-25T23:07:00+00:00</published><updated>2023-02-25T23:07:00+00:00</updated><id>http://www.dingostick.com/code/2023/02/25/simple-rust-service-in-docker</id><content type="html" xml:base="http://www.dingostick.com/code/2023/02/25/simple-rust-service-in-docker.html"><![CDATA[<p>At work, I own a Rust service that runs in an Azure Function. Among other things, the Functions runtime handles restarting the service should it be needed; fortunately, the service is incredibly stable and reliable. That said, I have done almost nothing with Docker (since I guess I’m living in the mid 2010s), and I really should learn more about it, as I expect I may need to deploy Rust services through Docker at some point.</p>

<p>I started looking at some simple Docker examples, but they all seem to use Node as a starting point. I don’t want to start there and try to work my way back, so instead, I figured I’d start with a simple Rust service and see if I can start from scratch.</p>

<p>As a total Docker newbie, here’s a fairly brief summary of my misadventures.</p>

<h2 id="build-a-simple-server">Build a Simple Server</h2>
<p>Let’s start with the service itself. Wanting to keep this incredibly simple (in this case, avoiding Rust async), I found <a href="https://github.com/oxigraph/oxhttp">OxHTTP</a>, a very simple synchronous HTTP server. We’ll start with a new project that uses it:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>cargo new rust-server
<span class="nv">$ </span><span class="nb">cd </span>rust-server
<span class="nv">$ </span>cargo add oxhttp
</code></pre></div></div>

<p>The <a href="https://github.com/oxigraph/oxhttp#server">provided example</a> is just about perfect for what we want; I’ll just <em>slightly</em> tweak it by wrapping it in a <code class="language-plaintext highlighter-rouge">main</code> function:</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="k">use</span> <span class="nn">oxhttp</span><span class="p">::</span><span class="n">Server</span><span class="p">;</span>
    <span class="k">use</span> <span class="nn">oxhttp</span><span class="p">::</span><span class="nn">model</span><span class="p">::{</span><span class="n">Response</span><span class="p">,</span> <span class="n">Status</span><span class="p">};</span>
    <span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">time</span><span class="p">::</span><span class="n">Duration</span><span class="p">;</span>
    
    <span class="c1">// Builds a new server that returns a 404 everywhere except for "/" where it returns the body 'home'</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">server</span> <span class="o">=</span> <span class="nn">Server</span><span class="p">::</span><span class="nf">new</span><span class="p">(|</span><span class="n">request</span><span class="p">|</span> <span class="p">{</span>
        <span class="k">if</span> <span class="n">request</span><span class="nf">.url</span><span class="p">()</span><span class="nf">.path</span><span class="p">()</span> <span class="o">==</span> <span class="s">"/"</span> <span class="p">{</span>
            <span class="nn">Response</span><span class="p">::</span><span class="nf">builder</span><span class="p">(</span><span class="nn">Status</span><span class="p">::</span><span class="n">OK</span><span class="p">)</span><span class="nf">.with_body</span><span class="p">(</span><span class="s">"home"</span><span class="p">)</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="nn">Response</span><span class="p">::</span><span class="nf">builder</span><span class="p">(</span><span class="nn">Status</span><span class="p">::</span><span class="n">NOT_FOUND</span><span class="p">)</span><span class="nf">.build</span><span class="p">()</span>
        <span class="p">}</span>
    <span class="p">});</span>
    <span class="c1">// Raise a timeout error if the client does not respond after 10s.</span>
    <span class="n">server</span><span class="nf">.set_global_timeout</span><span class="p">(</span><span class="nn">Duration</span><span class="p">::</span><span class="nf">from_secs</span><span class="p">(</span><span class="mi">10</span><span class="p">));</span>
    <span class="c1">// Listen to localhost:8080</span>
    <span class="n">server</span><span class="nf">.listen</span><span class="p">((</span><span class="s">"localhost"</span><span class="p">,</span> <span class="mi">8080</span><span class="p">))</span><span class="nf">.unwrap</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Build this with <code class="language-plaintext highlighter-rouge">cargo build --release</code> and test it out. I’ve configured my <code class="language-plaintext highlighter-rouge">~/.cargo/config</code> to specify a common build directory:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[build]
target-dir = "/home/adam/cargo-target"
</code></pre></div></div>

<p>This means that I can run my built server with <code class="language-plaintext highlighter-rouge">~/cargo-target/release/rust-server</code>, and when I visit <code class="language-plaintext highlighter-rouge">http://localhost:8080</code> in my browser, I see the HTTP response “home”. The server now works, so I copied the binary into the current working directory.</p>

<h2 id="simple-docker-image">Simple Docker image</h2>
<p>Next, we’ll need to build a Dockerfile. As I said, I know just about nothing about Docker, but I want to avoid the Node route. It seems pretty much everything is built off of <a href="https://www.alpinelinux.org/">Alpine</a>, so I’ll start there:</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> alpine:latest</span>
<span class="k">COPY</span><span class="s"> rust-server rust-server</span>
<span class="k">CMD</span><span class="s"> ["rust-server"]</span>
</code></pre></div></div>

<p>Building this is quick and completes without issue:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker build <span class="nt">-t</span> my-rust-server:latest <span class="nb">.</span>
<span class="o">[</span>+] Building 0.6s <span class="o">(</span>7/7<span class="o">)</span> FINISHED
 <span class="o">=&gt;</span> <span class="o">[</span>internal] load build definition from Dockerfile                                                                                 0.1s
 <span class="o">=&gt;</span> <span class="o">=&gt;</span> transferring dockerfile: 113B                                                                                                 0.0s
 <span class="o">=&gt;</span> <span class="o">[</span>internal] load .dockerignore                                                                                                    0.0s
 <span class="o">=&gt;</span> <span class="o">=&gt;</span> transferring context: 2B                                                                                                      0.0s
 <span class="o">=&gt;</span> <span class="o">[</span>internal] load metadata <span class="k">for </span>docker.io/library/alpine:latest                                                                     0.0s
 <span class="o">=&gt;</span> <span class="o">[</span>internal] load build context                                                                                                    0.1s
 <span class="o">=&gt;</span> <span class="o">=&gt;</span> transferring context: 4.70MB                                                                                                  0.1s
 <span class="o">=&gt;</span> CACHED <span class="o">[</span>1/2] FROM docker.io/library/alpine:latest                                                                                0.0s
 <span class="o">=&gt;</span> <span class="o">[</span>2/2] COPY rust-server rust-server                                                                                               0.2s
 <span class="o">=&gt;</span> exporting to image                                                                                                               0.1s
 <span class="o">=&gt;</span> <span class="o">=&gt;</span> exporting layers                                                                                                              0.1s
 <span class="o">=&gt;</span> <span class="o">=&gt;</span> writing image sha256:108dbb6764b4e6c94cc3bde571eb2157dceb57aab1ac3f393577174c1175a282                                         0.0s
 <span class="o">=&gt;</span> <span class="o">=&gt;</span> naming to docker.io/library/my-rust-server:latest                                                                             0.0s
</code></pre></div></div>

<p>Then I run it:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker run <span class="nt">-t</span> my-rust-server:latest
docker: Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: <span class="nb">exec</span>: <span class="s2">"rust-server"</span>: executable file not found <span class="k">in</span> <span class="nv">$PATH</span>: unknown.
ERRO[0001] error waiting <span class="k">for </span>container: context canceled
</code></pre></div></div>

<p>It seems that <code class="language-plaintext highlighter-rouge">COPY foo foo</code> places <code class="language-plaintext highlighter-rouge">foo</code> into the root directory (ie, <code class="language-plaintext highlighter-rouge">/</code>), which isn’t in <code class="language-plaintext highlighter-rouge">$PATH</code>, I guess? So let’s try putting it into <code class="language-plaintext highlighter-rouge">/bin/</code>:</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> alpine:latest</span>
<span class="k">COPY</span><span class="s"> rust-server /bin/rust-server</span>
<span class="k">CMD</span><span class="s"> ["/bin/rust-server"]</span>
</code></pre></div></div>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker run <span class="nt">-t</span> my-rust-server:latest
<span class="nb">exec</span> /bin/rust-server: no such file or directory
</code></pre></div></div>

<p>This is a different error, so <em>something</em> changed. But it’s still not finding it? Let’s inspect the container:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker run <span class="nt">-it</span> my-rust-server:latest /bin/sh
/ <span class="c"># ls /bin/rust-server</span>
/bin/rust-server
/ <span class="c"># file /bin/rust-server</span>
/bin/sh: file: not found
</code></pre></div></div>

<p>The binary is definitely there. I tried <code class="language-plaintext highlighter-rouge">file</code> to see what the system thinks the binary is, but Alpine doesn’t have it. Instead, we can try <code class="language-plaintext highlighter-rouge">ldd</code> to get details:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/ <span class="c"># ldd /bin/rust-server</span>
        /lib64/ld-linux-x86-64.so.2 <span class="o">(</span>0x7fa7b4984000<span class="o">)</span>
Error loading shared library libgcc_s.so.1: No such file or directory <span class="o">(</span>needed by /bin/rust-server<span class="o">)</span>
        librt.so.1 <span class="o">=&gt;</span> /lib64/ld-linux-x86-64.so.2 <span class="o">(</span>0x7fa7b4984000<span class="o">)</span>
        libpthread.so.0 <span class="o">=&gt;</span> /lib64/ld-linux-x86-64.so.2 <span class="o">(</span>0x7fa7b4984000<span class="o">)</span>
        libdl.so.2 <span class="o">=&gt;</span> /lib64/ld-linux-x86-64.so.2 <span class="o">(</span>0x7fa7b4984000<span class="o">)</span>
        libc.so.6 <span class="o">=&gt;</span> /lib64/ld-linux-x86-64.so.2 <span class="o">(</span>0x7fa7b4984000<span class="o">)</span>
Error loading shared library ld-linux-x86-64.so.2: No such file or directory <span class="o">(</span>needed by /bin/rust-server<span class="o">)</span>
Error relocating /bin/rust-server: _Unwind_Resume: symbol not found
Error relocating /bin/rust-server: _Unwind_Backtrace: symbol not found
</code></pre></div></div>

<p>Ohh, so it’s not that my Docker image can’t find <em>my binary</em> but that when trying to run my binary, it can’t find the <em>dynamically-linked libgcc</em>. A quick search on how to install packages in Alpine (since it’s not Debian-based, I can’t use <code class="language-plaintext highlighter-rouge">apt</code>) shows that it uses <code class="language-plaintext highlighter-rouge">apk</code>, and <a href="https://pkgs.alpinelinux.org/packages?name=libgcc&amp;branch=edge&amp;repo=&amp;arch=&amp;maintainer="><code class="language-plaintext highlighter-rouge">libgcc</code> exists in Alpine’s package repository</a>. Adding this to the Dockerfile:</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> alpine:latest</span>
<span class="k">COPY</span><span class="s"> rust-server /bin/rust-server</span>

<span class="c"># These are new:</span>
<span class="k">RUN </span>apk update
<span class="k">RUN </span>apk add libgcc

<span class="k">CMD</span><span class="s"> ["/bin/rust-server"]</span>
</code></pre></div></div>

<p>Running this still gives the <code class="language-plaintext highlighter-rouge">no such file or directory</code> error. So let’s inspect with <code class="language-plaintext highlighter-rouge">ldd</code> again:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/ <span class="c"># ldd /bin/rust-server</span>
        /lib64/ld-linux-x86-64.so.2 <span class="o">(</span>0x7f1e6d596000<span class="o">)</span>
        libgcc_s.so.1 <span class="o">=&gt;</span> /usr/lib/libgcc_s.so.1 <span class="o">(</span>0x7f1e6d2bc000<span class="o">)</span>
        librt.so.1 <span class="o">=&gt;</span> /lib64/ld-linux-x86-64.so.2 <span class="o">(</span>0x7f1e6d596000<span class="o">)</span>
        libpthread.so.0 <span class="o">=&gt;</span> /lib64/ld-linux-x86-64.so.2 <span class="o">(</span>0x7f1e6d596000<span class="o">)</span>
        libdl.so.2 <span class="o">=&gt;</span> /lib64/ld-linux-x86-64.so.2 <span class="o">(</span>0x7f1e6d596000<span class="o">)</span>
        libc.so.6 <span class="o">=&gt;</span> /lib64/ld-linux-x86-64.so.2 <span class="o">(</span>0x7f1e6d596000<span class="o">)</span>
Error loading shared library ld-linux-x86-64.so.2: No such file or directory <span class="o">(</span>needed by /bin/rust-server<span class="o">)</span>
Error relocating /bin/rust-server: __res_init: symbol not found
Error relocating /bin/rust-server: gnu_get_libc_version: symbol not found
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">libgcc</code> is no longer a problem, but <code class="language-plaintext highlighter-rouge">ld-linux</code> still is. And it appears that <code class="language-plaintext highlighter-rouge">ld-linux</code> is part of <a href="https://pkgs.alpinelinux.org/package/edge/community/x86_64/gcompat"><code class="language-plaintext highlighter-rouge">gcompat</code></a>. After adding <code class="language-plaintext highlighter-rouge">RUN apk add gcompat</code>, rebuilding, and rerunning, the message “Error loading shared library ld-linux-x86-64.so.2” goes away, but the “__res_init” and “gnu_get_libc_version” errors remain.</p>

<p>I did some further sleuthing and found <a href="https://www.reddit.com/r/rust/comments/o8gxzn/understanding_why_a_rust_app_fails_within_an/">a suggestion on Reddit</a> to use <a href="https://github.com/TobiasDeBruijn/SkinFixer-API/blob/a24da0657f222101864cf5f500e81628b90eec18/Dockerfile">this hack</a> to make it work, but instead of continuing down this rabbit hole, I decided to try another approach I saw: <a href="https://en.wikipedia.org/wiki/Musl"><code class="language-plaintext highlighter-rouge">musl</code></a>.</p>

<h2 id="static-linking-with-musl">Static linking with musl</h2>

<p>Rust can compile to a number of <a href="https://doc.rust-lang.org/rustc/targets/built-in.html">build targets</a>; in my dev environment (Ubuntu in WSL2), the default is:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>rustc <span class="nt">-vV</span> | <span class="nb">grep </span>host
host: x86_64-unknown-linux-gnu
</code></pre></div></div>

<p>We can find supported targets with <code class="language-plaintext highlighter-rouge">rustup target list</code>. Doing this shows that there’s <em>x86_64-unknown-linux-musl</em>. Let’s install this toolchain and compile the server:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>rustup target add x86_64-unknown-linux-musl 
<span class="o">(</span>...<span class="o">)</span>
<span class="nv">$ </span>cargo build <span class="nt">--target</span><span class="o">=</span>x86_64-unknown-linux-musl <span class="nt">--release</span>
<span class="nv">$ </span><span class="nb">mv </span>rust-server rust-server-old
<span class="nv">$ </span><span class="nb">cp</span> ~/cargo-target/x86_64-unknown-linux-musl/release/rust-server <span class="nb">.</span>
</code></pre></div></div>

<p>We can compare the old binary to the new one:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>file rust-server-old rust-server
rust-server-old: ELF 64-bit LSB shared object, x86-64, version 1 <span class="o">(</span>SYSV<span class="o">)</span>, dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, <span class="k">for </span>GNU/Linux 3.2.0, BuildID[sha1]<span class="o">=</span>523b84a693e7b90bcf8332d2eecd51cc9bfbe45a, with debug_info, not stripped
rust-server:     ELF 64-bit LSB shared object, x86-64, version 1 <span class="o">(</span>SYSV<span class="o">)</span>, dynamically linked, with debug_info, not stripped

<span class="nv">$ </span>ldd rust-server-old
        linux-vdso.so.1 <span class="o">(</span>0x00007ffea2be5000<span class="o">)</span>
        libgcc_s.so.1 <span class="o">=&gt;</span> /lib/x86_64-linux-gnu/libgcc_s.so.1 <span class="o">(</span>0x00007fd1fa870000<span class="o">)</span>
        librt.so.1 <span class="o">=&gt;</span> /lib/x86_64-linux-gnu/librt.so.1 <span class="o">(</span>0x00007fd1fa668000<span class="o">)</span>
        libpthread.so.0 <span class="o">=&gt;</span> /lib/x86_64-linux-gnu/libpthread.so.0 <span class="o">(</span>0x00007fd1fa449000<span class="o">)</span>
        libdl.so.2 <span class="o">=&gt;</span> /lib/x86_64-linux-gnu/libdl.so.2 <span class="o">(</span>0x00007fd1fa245000<span class="o">)</span>
        libc.so.6 <span class="o">=&gt;</span> /lib/x86_64-linux-gnu/libc.so.6 <span class="o">(</span>0x00007fd1f9e54000<span class="o">)</span>
        /lib64/ld-linux-x86-64.so.2 <span class="o">(</span>0x00007fd1fad44000<span class="o">)</span>

<span class="nv">$ </span>ldd rust-server
        statically linked
</code></pre></div></div>

<p><em>(Side note: the old and new binaries are 4.5Mb and 4.9Mb, respectively, showing the cost of statically linking. However, if we <code class="language-plaintext highlighter-rouge">strip</code> both binaries, their sizes – and the marginal difference – drop: 751Kb and 865Kb, respectively.)</em></p>

<h2 id="running-the-musl-binary">Running the <code class="language-plaintext highlighter-rouge">musl</code> binary</h2>

<p>Since the new binary is statically linked, we don’t need to install extra apk packages, so the Dockerfile is now back to:</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> alpine:latest</span>
<span class="k">COPY</span><span class="s"> rust-server /bin/rust-server</span>
<span class="k">CMD</span><span class="s"> ["/bin/rust-server"]</span>
</code></pre></div></div>

<p>This builds very quickly, and calling <code class="language-plaintext highlighter-rouge">docker run</code> now has a service running. Going to <a href="http://localhost:8080">http://localhost:8080</a> should work, right?</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>curl localhost:8080
curl: <span class="o">(</span>7<span class="o">)</span> Failed to connect to localhost port 8080: Connection refused
</code></pre></div></div>

<p>Ah, but we need to publish the container’s port to the host:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker run <span class="nt">-p</span> 8080:8080 <span class="nt">-t</span> my-rust-server:latest

<span class="c"># In another terminal (because `docker run` is blocking):</span>
<span class="nv">$ </span>curl localhost:8080
curl: <span class="o">(</span>52<span class="o">)</span> Empty reply from server
</code></pre></div></div>

<p>So it’s connecting but not getting any data?</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is <span class="s1">'^]'</span><span class="nb">.</span>
Connection closed by foreign host.

<span class="nv">$ </span>telnet localhost 8081
Trying 127.0.0.1...
telnet: Unable to connect to remote host: Connection refused
</code></pre></div></div>

<p>The connection on 8080 is opened but immediately closed; the attempt on 8081 fails, as expected, because there’s nothing on that port – it’s showing that there’s something different about 8080. So Docker <em>is</em> forwarding the port, and something <em>is</em> listening. Surely that’s our server.</p>

<p>Looking at the Rust code, we notice that we’re listening on localhost, port8080:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">server</span><span class="nf">.listen</span><span class="p">((</span><span class="s">"localhost"</span><span class="p">,</span> <span class="mi">8080</span><span class="p">))</span><span class="nf">.unwrap</span><span class="p">();</span>
</code></pre></div></div>

<p>But wait: <a href="https://serverfault.com/a/876703">it turns out</a> that there’s a difference:</p>

<blockquote>
  <p>127.0.0.1:xxxx is the normal loopback address, and localhost:xxxx is the hostname for 127.0.0.1:xxxx.</p>

  <p>0.0.0.0 is slightly different, it’s an address used to refer to all IP addresses on the same machine. Or no specific IP address.</p>
</blockquote>

<p>Simply changing from “localhost” to “0.0.0.0”, recompiling, rebuilding the image, and rerunning does the trick</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>curl localhost:8080
home
</code></pre></div></div>

<h2 id="bonus-saving-the-image">Bonus: Saving the image:</h2>

<p>I am familiar (but have no experience) with Docker Hub, and I have only briefly played with <a href="https://azure.microsoft.com/en-us/products/container-registry/">Azure Container Registry</a>, I thought I’d first start with the simplest option: saving the image to a file:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker save my-rust-server:latest | <span class="nb">gzip</span> <span class="o">&gt;</span> my-rust-server.tar.gz

<span class="nv">$ </span><span class="nb">ls</span> <span class="nt">-lh</span> my-rust-server.tar.gz
<span class="nt">-rw-r--r--</span> 1 adam adam 4.5M Feb 24 16:30 my-rust-server.tar.gz
</code></pre></div></div>

<p>Now let’s remove the image from Docker, make sure we can re-load it, and run it again:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker image <span class="nb">rm </span>my-rust-server:latest
Untagged: my-rust-server:latest
Deleted: sha256:0ed0fda582a3c568fdb8f4a313a464ce3244442d1f1d36934be9bb29e8b9e4fd

<span class="nv">$ </span>docker images | <span class="nb">grep </span>my-rust

<span class="nv">$ </span>docker load &lt; my-rust-server.tar.gz
Loaded image: my-rust-server:latest

<span class="nv">$ </span>docker images | <span class="nb">grep </span>my-rust
my-rust-server   latest    732da9278f98   18 minutes ago   12.1MB

<span class="nv">$ </span>docker run <span class="nt">-it</span> my-rust-server:latest /bin/sh
/ <span class="c"># ls /bin/rust-server</span>
/bin/rust-server
</code></pre></div></div>]]></content><author><name></name></author><category term="code" /><category term="rust" /><category term="docker" /><summary type="html"><![CDATA[At work, I own a Rust service that runs in an Azure Function. Among other things, the Functions runtime handles restarting the service should it be needed; fortunately, the service is incredibly stable and reliable. That said, I have done almost nothing with Docker (since I guess I’m living in the mid 2010s), and I really should learn more about it, as I expect I may need to deploy Rust services through Docker at some point.]]></summary></entry><entry><title type="html">Rayon thread pools in Rust</title><link href="http://www.dingostick.com/code/2023/02/10/rayon-thread-pools-in-rust.html" rel="alternate" type="text/html" title="Rayon thread pools in Rust" /><published>2023-02-10T17:02:17+00:00</published><updated>2023-02-10T17:02:17+00:00</updated><id>http://www.dingostick.com/code/2023/02/10/rayon-thread-pools-in-rust</id><content type="html" xml:base="http://www.dingostick.com/code/2023/02/10/rayon-thread-pools-in-rust.html"><![CDATA[<p><a href="https://docs.rs/rayon/latest/rayon/index.html"><code class="language-plaintext highlighter-rouge">rayon</code></a> provides an incredibly simple <a href="https://en.wikipedia.org/wiki/Work_stealing">work stealing</a> framework that, in my experience, requires only two lines of code that can dramatically improve processing throughput. To use, you’ll need to add it to your Cargo.toml with <code class="language-plaintext highlighter-rouge">cargo add rayon</code>.</p>

<p>Consider some function that does some intensive work:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cd">/// Do some number of iterations of work</span>
<span class="k">fn</span> <span class="nf">do_work</span><span class="p">(</span><span class="n">worker</span><span class="p">:</span> <span class="nb">usize</span><span class="p">,</span> <span class="n">iterations</span><span class="p">:</span> <span class="nb">usize</span><span class="p">)</span> <span class="p">{</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"Worker {worker} doing work"</span><span class="p">);</span>

    <span class="k">if</span> <span class="n">iterations</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">{</span>
        <span class="c1">// simulate long-running work with 'sleep'</span>
        <span class="c1">// we might do different kinds of work depending on the worker,</span>
        <span class="c1">// eg, open a different file of input.</span>
        <span class="nn">std</span><span class="p">::</span><span class="nn">thread</span><span class="p">::</span><span class="nf">sleep</span><span class="p">(</span><span class="nn">std</span><span class="p">::</span><span class="nn">time</span><span class="p">::</span><span class="nn">Duration</span><span class="p">::</span><span class="nf">from_secs</span><span class="p">(</span><span class="mi">1</span><span class="p">));</span>
        <span class="nf">do_work</span><span class="p">(</span><span class="n">worker</span><span class="p">,</span> <span class="n">iterations</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Doing this serially might look like this:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">const</span> <span class="n">NUM_WORKERS</span><span class="p">:</span> <span class="nb">usize</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
<span class="k">const</span> <span class="n">NUM_ITERATIONS</span><span class="p">:</span> <span class="nb">usize</span> <span class="o">=</span> <span class="mi">4</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">s</span> <span class="o">=</span> <span class="nn">std</span><span class="p">::</span><span class="nn">time</span><span class="p">::</span><span class="nn">Instant</span><span class="p">::</span><span class="nf">now</span><span class="p">();</span>
    <span class="p">(</span><span class="mi">1</span><span class="o">..=</span><span class="n">NUM_WORKERS</span><span class="p">)</span><span class="nf">.for_each</span><span class="p">(|</span><span class="n">worker</span><span class="p">|</span> <span class="nf">do_work</span><span class="p">(</span><span class="n">worker</span><span class="p">,</span> <span class="n">NUM_ITERATIONS</span><span class="p">));</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"Work took {:?}"</span><span class="p">,</span> <span class="n">s</span><span class="nf">.elapsed</span><span class="p">());</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This produces the very boring output:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Worker 1 doing work
Worker 1 doing work
Worker 1 doing work
Worker 1 doing work
Worker 1 doing work
Worker 2 doing work
Worker 2 doing work
Worker 2 doing work
Worker 2 doing work
Worker 2 doing work
Worker 3 doing work
Worker 3 doing work
Worker 3 doing work
Worker 3 doing work
Worker 3 doing work
Worker 4 doing work
Worker 4 doing work
Worker 4 doing work
Worker 4 doing work
Worker 4 doing work
Worker 5 doing work
Worker 5 doing work
Worker 5 doing work
Worker 5 doing work
Worker 5 doing work
Work took 20.007646351s
</code></pre></div></div>

<p>This might be rather inefficient, especially if we have many CPU cores sitting idle. Instead, we can use rayon and use one of the <code class="language-plaintext highlighter-rouge">*par_iter</code> variations:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">rayon</span><span class="p">::</span><span class="nn">prelude</span><span class="p">::</span><span class="o">*</span><span class="p">;</span> <span class="c1">// this is new</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">s</span> <span class="o">=</span> <span class="nn">std</span><span class="p">::</span><span class="nn">time</span><span class="p">::</span><span class="nn">Instant</span><span class="p">::</span><span class="nf">now</span><span class="p">();</span>
    <span class="p">(</span><span class="mi">1</span><span class="o">..=</span><span class="n">NUM_WORKERS</span><span class="p">)</span>
        <span class="nf">.into_par_iter</span><span class="p">()</span> <span class="c1">// and this is new</span>
        <span class="nf">.for_each</span><span class="p">(|</span><span class="n">worker</span><span class="p">|</span> <span class="nf">do_work</span><span class="p">(</span><span class="n">worker</span><span class="p">,</span> <span class="n">NUM_ITERATIONS</span><span class="p">));</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"Work took {:?}"</span><span class="p">,</span> <span class="n">s</span><span class="nf">.elapsed</span><span class="p">());</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This is much faster, as it will parallelize the work according to <a href="https://github.com/rayon-rs/rayon/blob/master/FAQ.md#how-many-threads-will-rayon-spawn">the number of CPUs available</a>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Worker 1 doing work
Worker 3 doing work
Worker 2 doing work
Worker 4 doing work
Worker 1 doing work
Worker 3 doing work
Worker 2 doing work
Worker 4 doing work
Worker 1 doing work
Worker 3 doing work
Worker 2 doing work
Worker 4 doing work
Worker 1 doing work
Worker 3 doing work
Worker 2 doing work
Worker 4 doing work
Worker 1 doing work
Worker 5 doing work
Worker 3 doing work
Worker 2 doing work
Worker 4 doing work
Worker 5 doing work
Worker 5 doing work
Worker 5 doing work
Worker 5 doing work
Work took 8.011677642s
</code></pre></div></div>

<p>Two things to note here:</p>

<ol>
  <li>Because this is now multithreading our work, the order of individual steps – in this case, 1, 3, 2, then 4 – isn’t exactly what we expect.</li>
  <li>We had five units of work (ie, five workers), but Rayon parallelized across four threads. In other words, it ran steps 1-4 in parallel, but step 5 ran after the others. This may be sub-optimal, so you can use a <a href="https://docs.rs/rayon/1.6.1/rayon/struct.ThreadPool.html"><code class="language-plaintext highlighter-rouge">ThreadPool</code></a> to configure the number of threads:</li>
</ol>

<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="k">let</span> <span class="n">s</span> <span class="o">=</span> <span class="nn">std</span><span class="p">::</span><span class="nn">time</span><span class="p">::</span><span class="nn">Instant</span><span class="p">::</span><span class="nf">now</span><span class="p">();</span>
    <span class="k">let</span> <span class="n">pool</span> <span class="o">=</span> <span class="nn">rayon</span><span class="p">::</span><span class="nn">ThreadPoolBuilder</span><span class="p">::</span><span class="nf">new</span><span class="p">()</span>
        <span class="nf">.num_threads</span><span class="p">(</span><span class="n">NUM_WORKERS</span><span class="p">)</span> <span class="c1">// use one thread per work slice</span>
        <span class="nf">.build</span><span class="p">()</span>
        <span class="nf">.unwrap</span><span class="p">();</span>
    
    <span class="n">pool</span><span class="nf">.install</span><span class="p">(||</span> <span class="p">{</span>
        <span class="p">(</span><span class="mi">1</span><span class="o">..=</span><span class="n">NUM_WORKERS</span><span class="p">)</span>
            <span class="nf">.into_par_iter</span><span class="p">()</span>
            <span class="nf">.for_each</span><span class="p">(|</span><span class="n">worker</span><span class="p">|</span> <span class="nf">do_work</span><span class="p">(</span><span class="n">worker</span><span class="p">,</span> <span class="n">NUM_ITERATIONS</span><span class="p">));</span>
    <span class="p">});</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"Work took {:?}"</span><span class="p">,</span> <span class="n">s</span><span class="nf">.elapsed</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>Worker 1 doing work
Worker 3 doing work
Worker 2 doing work
Worker 4 doing work
Worker 5 doing work
Worker 1 doing work
Worker 3 doing work
Worker 4 doing work
Worker 2 doing work
Worker 5 doing work
Worker 1 doing work
Worker 3 doing work
Worker 4 doing work
Worker 2 doing work
Worker 5 doing work
Worker 1 doing work
Worker 3 doing work
Worker 4 doing work
Worker 2 doing work
Worker 5 doing work
Worker 1 doing work
Worker 3 doing work
Worker 4 doing work
Worker 2 doing work
Worker 5 doing work
Work took 4.002877261s
</code></pre></div></div>

<p>Alternately, you may want to limit your parallelism to leave compute available to other tasks:</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="k">let</span> <span class="n">s</span> <span class="o">=</span> <span class="nn">std</span><span class="p">::</span><span class="nn">time</span><span class="p">::</span><span class="nn">Instant</span><span class="p">::</span><span class="nf">now</span><span class="p">();</span>
    <span class="k">let</span> <span class="n">pool</span> <span class="o">=</span> <span class="nn">rayon</span><span class="p">::</span><span class="nn">ThreadPoolBuilder</span><span class="p">::</span><span class="nf">new</span><span class="p">()</span>
        <span class="nf">.num_threads</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="c1">// use only two threads</span>
        <span class="nf">.build</span><span class="p">()</span>
        <span class="nf">.unwrap</span><span class="p">();</span>

    <span class="n">pool</span><span class="nf">.install</span><span class="p">(||</span> <span class="p">{</span>
        <span class="p">(</span><span class="mi">1</span><span class="o">..=</span><span class="n">NUM_WORKERS</span><span class="p">)</span>
            <span class="nf">.into_par_iter</span><span class="p">()</span>
            <span class="nf">.for_each</span><span class="p">(|</span><span class="n">worker</span><span class="p">|</span> <span class="nf">do_work</span><span class="p">(</span><span class="n">worker</span><span class="p">,</span> <span class="n">NUM_ITERATIONS</span><span class="p">));</span>
    <span class="p">});</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"Work took {:?}"</span><span class="p">,</span> <span class="n">s</span><span class="nf">.elapsed</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>Worker 1 doing work
Worker 3 doing work
Worker 3 doing work
Worker 1 doing work
Worker 1 doing work
Worker 3 doing work
Worker 3 doing work
Worker 1 doing work
Worker 3 doing work
Worker 4 doing work
Worker 1 doing work
Worker 2 doing work
Worker 4 doing work
Worker 2 doing work
Worker 4 doing work
Worker 2 doing work
Worker 4 doing work
Worker 2 doing work
Worker 4 doing work
Worker 5 doing work
Worker 2 doing work
Worker 5 doing work
Worker 5 doing work
Worker 5 doing work
Worker 5 doing work
Work took 12.004555134s
</code></pre></div></div>]]></content><author><name></name></author><category term="code" /><category term="rust" /><summary type="html"><![CDATA[rayon provides an incredibly simple work stealing framework that, in my experience, requires only two lines of code that can dramatically improve processing throughput. To use, you’ll need to add it to your Cargo.toml with cargo add rayon.]]></summary></entry><entry><title type="html">Joinable traits</title><link href="http://www.dingostick.com/code/2022/11/28/joinable-traits.html" rel="alternate" type="text/html" title="Joinable traits" /><published>2022-11-28T14:35:54+00:00</published><updated>2022-11-28T14:35:54+00:00</updated><id>http://www.dingostick.com/code/2022/11/28/joinable-traits</id><content type="html" xml:base="http://www.dingostick.com/code/2022/11/28/joinable-traits.html"><![CDATA[<p>I just released an update to my <a href="https://crates.io/crates/joinable/"><code class="language-plaintext highlighter-rouge">joinable</code> crate</a> (<a href="https://github.com/aeshirey/joinable">source code here</a>) as well as a new <a href="https://crates.io/crates/irisdata"><code class="language-plaintext highlighter-rouge">irisdata</code> crate</a> (<a href="https://github.com/aeshirey/irisdata">source code</a>) well-known in the data science field.</p>

<p>This update to <code class="language-plaintext highlighter-rouge">joinable</code> renames the <code class="language-plaintext highlighter-rouge">Joinable</code> trait to <code class="language-plaintext highlighter-rouge">JoinableGrouped</code> to reflect that the results (at least of inner- and outer-joins) group the right-hand side. It also adds a new trait with the <code class="language-plaintext highlighter-rouge">Joinable</code> name that behaves perhaps more intuitively – each left-hand record can be yielded multiple times (as matches are found).</p>

<p><code class="language-plaintext highlighter-rouge">Joinable</code> only defines <code class="language-plaintext highlighter-rouge">inner_join</code> and <code class="language-plaintext highlighter-rouge">outer_join</code> methods. <code class="language-plaintext highlighter-rouge">JoinableGrouped</code> defines <code class="language-plaintext highlighter-rouge">inner_join_grouped</code>, <code class="language-plaintext highlighter-rouge">outer_join_grouped</code>, <code class="language-plaintext highlighter-rouge">semi_join</code>, and <code class="language-plaintext highlighter-rouge">anti_join</code>.</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">cmp</span><span class="p">::</span><span class="n">Ordering</span><span class="p">;</span>

<span class="k">use</span> <span class="nn">irisdata</span><span class="p">::{</span><span class="n">Species</span><span class="p">,</span> <span class="n">IRIS_DATA</span><span class="p">};</span>
<span class="k">use</span> <span class="nn">joinable</span><span class="p">::{</span><span class="n">JoinableGrouped</span><span class="p">,</span> <span class="n">RHS</span><span class="p">};</span>

<span class="nd">#[derive(Debug)]</span>
<span class="k">struct</span> <span class="n">IrisData</span> <span class="p">{</span>
    <span class="n">species</span><span class="p">:</span> <span class="n">Species</span><span class="p">,</span>
    <span class="n">common_name</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">'static</span> <span class="nb">str</span><span class="p">,</span>
    <span class="n">average_sepal_length</span><span class="p">:</span> <span class="nb">f32</span><span class="p">,</span>
    <span class="n">average_sepal_width</span><span class="p">:</span> <span class="nb">f32</span><span class="p">,</span>
    <span class="n">average_petal_length</span><span class="p">:</span> <span class="nb">f32</span><span class="p">,</span>
    <span class="n">average_petal_width</span><span class="p">:</span> <span class="nb">f32</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="k">let</span> <span class="n">common_names</span> <span class="o">=</span> <span class="p">[</span>
        <span class="p">(</span><span class="nn">Species</span><span class="p">::</span><span class="n">IrisVersicolor</span><span class="p">,</span> <span class="s">"blue flag"</span><span class="p">),</span>
        <span class="p">(</span><span class="nn">Species</span><span class="p">::</span><span class="n">IrisVersicolor</span><span class="p">,</span> <span class="s">"harlequin blueflag"</span><span class="p">),</span>
        <span class="p">(</span><span class="nn">Species</span><span class="p">::</span><span class="n">IrisVersicolor</span><span class="p">,</span> <span class="s">"larger blue flag"</span><span class="p">),</span>
        <span class="p">(</span><span class="nn">Species</span><span class="p">::</span><span class="n">IrisVersicolor</span><span class="p">,</span> <span class="s">"northern blue flag"</span><span class="p">),</span>
        <span class="p">(</span><span class="nn">Species</span><span class="p">::</span><span class="n">IrisVersicolor</span><span class="p">,</span> <span class="s">"poison flag"</span><span class="p">),</span>
        <span class="p">(</span><span class="nn">Species</span><span class="p">::</span><span class="n">IrisVirginica</span><span class="p">,</span> <span class="s">"Virginia blueflag"</span><span class="p">),</span>
        <span class="p">(</span><span class="nn">Species</span><span class="p">::</span><span class="n">IrisVirginica</span><span class="p">,</span> <span class="s">"Virginia iris"</span><span class="p">),</span>
        <span class="p">(</span><span class="nn">Species</span><span class="p">::</span><span class="n">IrisVirginica</span><span class="p">,</span> <span class="s">"great blue flag"</span><span class="p">),</span>
        <span class="p">(</span><span class="nn">Species</span><span class="p">::</span><span class="n">IrisVirginica</span><span class="p">,</span> <span class="s">"southern blue flag"</span><span class="p">),</span>
    <span class="p">];</span>

    <span class="k">let</span> <span class="n">joined</span> <span class="o">=</span> <span class="n">common_names</span>
        <span class="nf">.iter</span><span class="p">()</span>
        <span class="nf">.inner_join_grouped</span><span class="p">(</span><span class="nn">RHS</span><span class="p">::</span><span class="nf">new_unsorted</span><span class="p">(</span><span class="o">&amp;</span><span class="n">IRIS_DATA</span><span class="p">[</span><span class="o">..</span><span class="p">]),</span> <span class="p">|(</span><span class="n">lhs_species</span><span class="p">,</span> <span class="n">_</span><span class="p">),</span> <span class="n">r</span><span class="p">|</span> <span class="p">{</span>
            <span class="k">if</span> <span class="o">*</span><span class="n">lhs_species</span> <span class="o">==</span> <span class="n">r</span><span class="py">.species</span> <span class="p">{</span>
                <span class="nn">Ordering</span><span class="p">::</span><span class="n">Equal</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="nn">Ordering</span><span class="p">::</span><span class="n">Less</span>
            <span class="p">}</span>
        <span class="p">})</span>
        <span class="nf">.map</span><span class="p">(|(</span><span class="n">lhs</span><span class="p">,</span> <span class="n">grp</span><span class="p">)|</span> <span class="n">IrisData</span> <span class="p">{</span>
            <span class="n">species</span><span class="p">:</span> <span class="n">lhs</span><span class="na">.0</span><span class="p">,</span>
            <span class="n">common_name</span><span class="p">:</span> <span class="n">lhs</span><span class="na">.1</span><span class="p">,</span>
            <span class="n">average_sepal_length</span><span class="p">:</span> <span class="n">grp</span><span class="nf">.iter</span><span class="p">()</span><span class="nf">.map</span><span class="p">(|</span><span class="n">i</span><span class="p">|</span> <span class="n">i</span><span class="py">.sepal_length</span><span class="p">)</span><span class="py">.sum</span><span class="p">::</span><span class="o">&lt;</span><span class="nb">f32</span><span class="o">&gt;</span><span class="p">()</span> <span class="o">/</span> <span class="n">grp</span><span class="nf">.len</span><span class="p">()</span> <span class="k">as</span> <span class="nb">f32</span><span class="p">,</span>
            <span class="n">average_sepal_width</span><span class="p">:</span> <span class="n">grp</span><span class="nf">.iter</span><span class="p">()</span><span class="nf">.map</span><span class="p">(|</span><span class="n">i</span><span class="p">|</span> <span class="n">i</span><span class="py">.sepal_width</span><span class="p">)</span><span class="py">.sum</span><span class="p">::</span><span class="o">&lt;</span><span class="nb">f32</span><span class="o">&gt;</span><span class="p">()</span> <span class="o">/</span> <span class="n">grp</span><span class="nf">.len</span><span class="p">()</span> <span class="k">as</span> <span class="nb">f32</span><span class="p">,</span>
            <span class="n">average_petal_length</span><span class="p">:</span> <span class="n">grp</span><span class="nf">.iter</span><span class="p">()</span><span class="nf">.map</span><span class="p">(|</span><span class="n">i</span><span class="p">|</span> <span class="n">i</span><span class="py">.petal_length</span><span class="p">)</span><span class="py">.sum</span><span class="p">::</span><span class="o">&lt;</span><span class="nb">f32</span><span class="o">&gt;</span><span class="p">()</span> <span class="o">/</span> <span class="n">grp</span><span class="nf">.len</span><span class="p">()</span> <span class="k">as</span> <span class="nb">f32</span><span class="p">,</span>
            <span class="n">average_petal_width</span><span class="p">:</span> <span class="n">grp</span><span class="nf">.iter</span><span class="p">()</span><span class="nf">.map</span><span class="p">(|</span><span class="n">i</span><span class="p">|</span> <span class="n">i</span><span class="py">.petal_width</span><span class="p">)</span><span class="py">.sum</span><span class="p">::</span><span class="o">&lt;</span><span class="nb">f32</span><span class="o">&gt;</span><span class="p">()</span> <span class="o">/</span> <span class="n">grp</span><span class="nf">.len</span><span class="p">()</span> <span class="k">as</span> <span class="nb">f32</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="nd">println!</span><span class="p">(</span><span class="s">"{joined:#?}"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name></name></author><category term="code" /><category term="rust" /><summary type="html"><![CDATA[I just released an update to my joinable crate (source code here) as well as a new irisdata crate (source code) well-known in the data science field.]]></summary></entry><entry><title type="html">SMT for scheduling scouts in tents</title><link href="http://www.dingostick.com/code/2022/07/08/smt-for-scheduling-scouts-in-tents.html" rel="alternate" type="text/html" title="SMT for scheduling scouts in tents" /><published>2022-07-08T05:43:17+00:00</published><updated>2022-07-08T05:43:17+00:00</updated><id>http://www.dingostick.com/code/2022/07/08/smt-for-scheduling-scouts-in-tents</id><content type="html" xml:base="http://www.dingostick.com/code/2022/07/08/smt-for-scheduling-scouts-in-tents.html"><![CDATA[<p>Yesterday, the adults of my kids’ scout troop had a video call to discuss the upcoming week-long camp many of the scouts will attend. One of the mundane tasks is to figure out which scouts will bunk with which other scouts. There are maybe 20 or so scouts of different ages, genders, and personalities, and they need to be placed into a limited number of tents or cabins.</p>

<p>I wasn’t personally involved in the phone call, but I was call-adjacent and aghast at the pen-and-paper approach to figuring out who should be where. Parents expressed their interest in having their kids <em>with</em> this scout but <em>not with</em> that scout. Boys and girls can’t share a tent. Scouts may only share a tent with other scouts within three years of age (ie, no 17 year-old scouts bunking with 12 year-olds). The mental effort and time that went into that work annoyed my inner geek, so I proceeded to spend the next several hours solving the general case. It was a good excuse to play around with SMT again.</p>

<p>Rather than dive into all the details, I’ll simply share <a href="https://gist.github.com/aeshirey/0d8f4d2081217ded2ca6f5888e1f894f">the public Gist with my v1 implementation</a>.</p>

<p>The solution is a Python script that generates <a href="http://smtlib.cs.uiowa.edu/">SMT-LIB</a> code that is evaluated by <a href="https://github.com/Z3Prover/z3">Z3</a>. After a few configurations (such as <code class="language-plaintext highlighter-rouge">NUM_TENTS</code> to identify how many tents are available), you specify the set of scouts with their age and gender:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">scouts</span> <span class="o">=</span> <span class="p">[</span>
        <span class="p">(</span><span class="s">'Abe'</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="s">'m'</span><span class="p">),</span>     <span class="c1"># 0
</span>        <span class="p">(</span><span class="s">'Brian'</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="s">'m'</span><span class="p">),</span>   <span class="c1"># 1
</span>        <span class="p">(</span><span class="s">'Charlie'</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="s">'m'</span><span class="p">),</span> <span class="c1"># 2
</span>        <span class="p">(</span><span class="s">'Dave'</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="s">'m'</span><span class="p">),</span>    <span class="c1"># 3
</span>        <span class="p">(</span><span class="s">'Eddie'</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="s">'m'</span><span class="p">),</span>   <span class="c1"># 4
</span>        <span class="p">(</span><span class="s">'Lily'</span><span class="p">,</span> <span class="mi">15</span><span class="p">,</span> <span class="s">'f'</span><span class="p">),</span>    <span class="c1"># 5
</span>        <span class="p">(</span><span class="s">'Megan'</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="s">'f'</span><span class="p">),</span>   <span class="c1"># 6
</span>        <span class="p">]</span>
</code></pre></div></div>

<p>The output is a model that tells you who is in which tent. In this example, tent0 contains scouts 5 (Lily) and 6 (Megan):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  (define-fun tent0 ((x!0 Int)) Int
    (ite (= x!0 2) 5
    (ite (= x!0 3) 6
      (- 1))))
</code></pre></div></div>]]></content><author><name></name></author><category term="code" /><category term="smt" /><summary type="html"><![CDATA[Yesterday, the adults of my kids’ scout troop had a video call to discuss the upcoming week-long camp many of the scouts will attend. One of the mundane tasks is to figure out which scouts will bunk with which other scouts. There are maybe 20 or so scouts of different ages, genders, and personalities, and they need to be placed into a limited number of tents or cabins.]]></summary></entry></feed>