<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Anton Zhiyanov</title><description>Everything about Go, SQL, and software in general.</description><link>https://antonz.org/</link><image><url>https://antonz.org/assets/favicon/favicon.png</url><title>Anton Zhiyanov</title><link>https://antonz.org/</link></image><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Sat, 11 Jul 2026 12:30:00 +0000</lastBuildDate><atom:link href="https://antonz.org/index.xml" rel="self" type="application/rss+xml"/><item><title>On interactive Go tours</title><link>https://antonz.org/on-go-tours/</link><pubDate>Sat, 11 Jul 2026 12:30:00 +0000</pubDate><guid>https://antonz.org/on-go-tours/</guid><description>Wrapping up the series.</description><content:encoded><![CDATA[<p>Over the past two years, I've published interactive tours for five Go releases, from 1.22 to 1.26.</p>
<p>I know some of you have read them, and I've received a lot of kind words from you (even some core Go team members reached out) — thank you so much for that!</p>
<blockquote>
<p>Tour history:
<a href="/go-1-22/">Go 1.22</a> •
<a href="/go-1-23/">1.23</a> •
<a href="/go-1-24/">1.24</a> •
<a href="/go-1-25/">1.25</a> •
<a href="/go-1-26/">1.26</a> +
<a href="/which-go/">Go features by version</a></p>
</blockquote>
<p>Unfortunately, at some point, writing these tours stopped being fun and started to feel like a part-time job. I'm not really excited about that, so I've decided to stop.</p>
<p>I still like Go (well, most of it). I read a lot of Go code, I write some Go code, and I write <a href="/solod/">Solod</a> code, which is also Go 🙂 (Solod is a systems language with Go syntax and a Go-like stdlib).</p>
<p>I'm still pretty close to the language and will probably continue to write about it.</p>
<p>But the interactive tours story is over.</p>
]]></content:encoded></item><item><title>Go-flavored concurrency in C</title><link>https://antonz.org/concurrency-in-c/</link><pubDate>Fri, 10 Jul 2026 12:00:00 +0000</pubDate><guid>https://antonz.org/concurrency-in-c/</guid><description>Worker pools, channels, and mutexes - backed by pthreads.</description><content:encoded><![CDATA[<p>Go's concurrency is one of the main reasons people like the language. You write <code>go f()</code>, send values through channels, and the runtime scheduler runs thousands of goroutines on just a few OS threads. It feels effortless.</p>
<p>None of that machinery exists in C. Which made me wonder: how close can you get to Go's concurrency model using only POSIX threads? Obviously, native OS threads can't match the efficiency of lightweight goroutines, but what is the actual cost, when does it become a problem, and is there any way to at least partially avoid it?</p>
<p>I ran into these questions while adding concurrency to <a href="/solod/">Solod</a> (So), a strict subset of Go that translates to plain C, with no runtime and no garbage collector. In the end, I came to the conclusion that you can do quite a lot with pthreads — as long as you're honest about the tradeoffs.</p>
<p>This post is about the POSIX threads-based concurrency model I chose, the benefits it offers, and its limitations.</p>
<p><a href="#mutexcond">Mutex/Cond</a> •
<a href="#atomics">Atomics</a> •
<a href="#worker-pool">Pool</a> •
<a href="#channel">Channel</a> •
<a href="#performance">Performance</a> •
<a href="#design-decisions">Design</a> •
<a href="#wrapping-up">Wrapping up</a></p>
<h2 id="mutexcond">Mutex/Cond</h2>
<p>Everything in So's concurrency stack is built on two basic POSIX primitives: the mutex and the condition variable. <code>sync.Mutex</code> is a thin wrapper around <code>pthread_mutex_t</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Extracted from So&#39;s stdlib source code.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Mutex</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">mu</span> <span class="nx">pthread_mutex_t</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">Mutex</span><span class="p">)</span> <span class="nf">Lock</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">rc</span> <span class="o">:=</span> <span class="nf">pthread_mutex_lock</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">m</span><span class="p">.</span><span class="nx">mu</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">rc</span> <span class="o">!=</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nb">panic</span><span class="p">(</span><span class="s">&#34;sync: Mutex.Lock failed&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Since So translates to C, this is basically a struct that holds a <code>pthread_mutex_t</code> and a function that calls <code>pthread_mutex_lock</code>. Here's the transpiler output:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// The translated C code.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="n">sync_Mutex</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">pthread_mutex_t</span> <span class="n">mu</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">sync_Mutex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">sync_Mutex_Lock</span><span class="p">(</span><span class="n">sync_Mutex</span><span class="o">*</span> <span class="n">m</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rc</span> <span class="o">=</span> <span class="nf">pthread_mutex_lock</span><span class="p">(</span><span class="o">&amp;</span><span class="n">m</span><span class="o">-&gt;</span><span class="n">mu</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">rc</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">so_panic</span><span class="p">(</span><span class="s">&#34;sync: Mutex.Lock failed&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><blockquote>
<p>That is the whole translation — the generated C is a near-mechanical mirror of the So code, only noisier. From here on, I'll mainly show the So version, but I'll also provide the C code for those who are interested.</p>
</blockquote>
<p>There's nothing exciting here: <code>sync.Mutex</code> is a pthread mutex wrapper that panics if something goes wrong (which is rare).</p>
<p>The companion primitive is <code>sync.Cond</code>, a wrapper around <code>pthread_cond_t</code>. It's the standard &quot;wait until a condition holds&quot; tool, associated with a mutex:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Cond</span> <span class="kd">struct</span>           <span class="c1">// wraps pthread_cond_t + pthread_mutex_t
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">Cond</span><span class="p">)</span> <span class="nf">Wait</span><span class="p">()</span>      <span class="c1">// wraps pthread_cond_wait
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">Cond</span><span class="p">)</span> <span class="nf">Signal</span><span class="p">()</span>    <span class="c1">// wraps pthread_cond_signal
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">Cond</span><span class="p">)</span> <span class="nf">Broadcast</span><span class="p">()</span> <span class="c1">// wraps pthread_cond_broadcast
</span></span></span></code></pre></div><details>
    <summary>Show the translated C code</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">sync_Cond</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">pthread_cond_t</span> <span class="n">cond</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">sync_Mutex</span><span class="o">*</span>    <span class="n">mu</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">sync_Cond</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">sync_Cond_Wait</span><span class="p">(</span><span class="n">sync_Cond</span><span class="o">*</span> <span class="n">c</span><span class="p">);</span>      <span class="c1">// wraps pthread_cond_wait
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">sync_Cond_Signal</span><span class="p">(</span><span class="n">sync_Cond</span><span class="o">*</span> <span class="n">c</span><span class="p">);</span>    <span class="c1">// wraps pthread_cond_signal
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">sync_Cond_Broadcast</span><span class="p">(</span><span class="n">sync_Cond</span><span class="o">*</span> <span class="n">c</span><span class="p">);</span> <span class="c1">// wraps pthread_cond_broadcast
</span></span></span></code></pre></div></details>
<p>These two types — <code>Mutex</code> and <code>Cond</code> — are the foundation. Other concurrency tools — <code>Once</code>, the thread pool, channels — are built using a mutex and one or more condition variables. This has several effects on performance, as we'll see later.</p>
<h2 id="atomics">Atomics</h2>
<p>Not everything needs a lock. So's <code>sync/atomic</code> mirrors Go's: <code>Bool</code>, <code>Int32</code>, <code>Int64</code>, <code>Uint32</code>, <code>Uint64</code>, and a generic <code>Pointer[T]</code>, all with <code>Load</code>, <code>Store</code>, <code>Swap</code>, and <code>CompareAndSwap</code> methods.</p>
<p>The nice thing is that these don't need pthreads at all. They map directly to the C compiler's <code>__atomic</code> builtins — the same hardware instructions that Go's compiler emits. So there's no reason for them to be any slower, and they're not:</p>
<table>
<thead>
<tr>
<th>Atomic op</th>
<th style="text-align:right">Go</th>
<th style="text-align:right">So</th>
<th>Winner</th>
</tr>
</thead>
<tbody>
<tr>
<td>Load</td>
<td style="text-align:right">2ns</td>
<td style="text-align:right">2ns</td>
<td>~same</td>
</tr>
<tr>
<td>Store</td>
<td style="text-align:right">2ns</td>
<td style="text-align:right">2ns</td>
<td>~same</td>
</tr>
<tr>
<td>CompareAndSwap</td>
<td style="text-align:right">13ns</td>
<td style="text-align:right">13ns</td>
<td>~same</td>
</tr>
</tbody>
</table>
<blockquote>
<p>Each number is the cost of one operation on a single thread.</p>
</blockquote>
<p><code>sync.Once</code> is a good example of using atomics effectively. Its fast path only needs a single atomic load — after the given function runs, every future call to <code>Do</code> checks a flag and returns:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Once</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">mu</span>   <span class="nx">Mutex</span>
</span></span><span class="line"><span class="cl">    <span class="nx">done</span> <span class="nx">atomic</span><span class="p">.</span><span class="nx">Bool</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Do calls f if and only if Do is being called
</span></span></span><span class="line"><span class="cl"><span class="c1">// for the first time for this o.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">o</span> <span class="o">*</span><span class="nx">Once</span><span class="p">)</span> <span class="nf">Do</span><span class="p">(</span><span class="nx">f</span> <span class="kd">func</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">o</span><span class="p">.</span><span class="nx">done</span><span class="p">.</span><span class="nf">Load</span><span class="p">()</span> <span class="p">{</span> <span class="c1">// lock-free fast path
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">return</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// slow path...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><details>
    <summary>Show the translated C code</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">sync_Once</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">sync_Mutex</span> <span class="n">mu</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">atomic_Bool</span> <span class="n">done</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">sync_Once</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Do calls f if and only if Do is being called
</span></span></span><span class="line"><span class="cl"><span class="c1">// for the first time for this o.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">sync_Once_Do</span><span class="p">(</span><span class="n">sync_Once</span><span class="o">*</span> <span class="n">o</span><span class="p">,</span> <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">f</span><span class="p">)())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nf">atomic_Bool_Load</span><span class="p">(</span><span class="o">&amp;</span><span class="n">o</span><span class="o">-&gt;</span><span class="n">done</span><span class="p">))</span> <span class="p">{</span> <span class="c1">// lock-free fast path
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// slow path...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div></details>
<h2 id="worker-pool">Worker pool</h2>
<p>To actually run code concurrently, you need threads. The <code>conc.Thread</code> type wraps <code>pthread_t</code> and its related functions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Thread</span> <span class="kd">struct</span>          <span class="c1">// wraps pthread_t
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">th</span> <span class="nx">Thread</span><span class="p">)</span> <span class="nf">Wait</span><span class="p">()</span> <span class="nx">any</span> <span class="c1">// wraps pthread_join
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">th</span> <span class="nx">Thread</span><span class="p">)</span> <span class="nf">Detach</span><span class="p">()</span>   <span class="c1">// wraps pthread_detach
</span></span></span></code></pre></div><details>
    <summary>Show the translated C code</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">conc_Thread</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">pthread_t</span> <span class="n">t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">conc_Thread</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span><span class="o">*</span> <span class="nf">conc_Thread_Wait</span><span class="p">(</span><span class="n">conc_Thread</span> <span class="n">th</span><span class="p">);</span>   <span class="c1">// wraps pthread_join
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span>  <span class="nf">conc_Thread_Detach</span><span class="p">(</span><span class="n">conc_Thread</span> <span class="n">th</span><span class="p">);</span> <span class="c1">// wraps pthread_detach
</span></span></span></code></pre></div></details>
<p>Consider this <code>conc.Go</code> function:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Go launches an OS thread that runs fn(arg) and returns a handle to it.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Go</span><span class="p">(</span><span class="nx">entry</span> <span class="kd">func</span><span class="p">(</span><span class="nx">any</span><span class="p">)</span> <span class="nx">any</span><span class="p">,</span> <span class="nx">arg</span> <span class="nx">any</span><span class="p">)</span> <span class="nx">Thread</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">th</span> <span class="nx">Thread</span>
</span></span><span class="line"><span class="cl">    <span class="nx">rc</span> <span class="o">:=</span> <span class="nf">pthread_create</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">th</span><span class="p">.</span><span class="nx">t</span><span class="p">,</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">entry</span><span class="p">,</span> <span class="nx">arg</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><details>
    <summary>Show the translated C code</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Go launches an OS thread that runs fn(arg) and returns a handle to it.
</span></span></span><span class="line"><span class="cl"><span class="c1">// `any` in So translates to `void*` in C.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">conc_Thread</span> <span class="nf">conc_Go</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="p">(</span><span class="o">*</span><span class="n">entry</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span><span class="p">),</span> <span class="kt">void</span><span class="o">*</span> <span class="n">arg</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">conc_Thread</span> <span class="n">th</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rc</span> <span class="o">=</span> <span class="nf">pthread_create</span><span class="p">(</span><span class="o">&amp;</span><span class="n">th</span><span class="p">.</span><span class="n">t</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">entry</span><span class="p">,</span> <span class="n">arg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div></details>
<p>Usage example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">work</span><span class="p">(</span><span class="nx">arg</span> <span class="nx">any</span><span class="p">)</span> <span class="nx">any</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">acc</span> <span class="o">:=</span> <span class="nx">arg</span><span class="p">.(</span><span class="o">*</span><span class="nx">Account</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">acc</span> <span class="nx">Account</span>
</span></span><span class="line"><span class="cl">    <span class="nx">th</span> <span class="o">:=</span> <span class="nx">conc</span><span class="p">.</span><span class="nf">Go</span><span class="p">(</span><span class="nx">work</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">acc</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ... do other work concurrently ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">th</span><span class="p">.</span><span class="nf">Wait</span><span class="p">()</span> <span class="c1">// work is complete once Wait returns
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><details>
    <summary>Show the translated C code</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span><span class="o">*</span> <span class="nf">work</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">arg</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">main_Account</span><span class="o">*</span> <span class="n">acc</span> <span class="o">=</span> <span class="p">(</span><span class="n">main_Account</span><span class="o">*</span><span class="p">)</span><span class="n">arg</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">main_Account</span> <span class="n">acc</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="n">conc_Thread</span> <span class="n">th</span> <span class="o">=</span> <span class="nf">conc_Go</span><span class="p">(</span><span class="n">work</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">acc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ... do other work concurrently ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">conc_Thread_Wait</span><span class="p">(</span><span class="n">th</span><span class="p">);</span> <span class="c1">// work is complete once Wait returns
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div></details>
<p>It might look like <code>go work(&amp;acc)</code>, but that's just on the surface. <code>conc.Go</code> starts an actual OS thread, not a goroutine. You have to eventually call <code>Wait</code> to join or <code>Detach</code> it, or else its resources will leak. Also, OS threads are expensive to create — they're nothing like Go's goroutines, which only need a few kilobytes of stack and start up in nanoseconds.</p>
<p>That's exactly why you usually don't want to call <code>Go</code> inside a loop. For tasks that are short-lived or happen often, it's better to use a pool of long-lived worker threads and send tasks to them.</p>
<p><code>conc.Pool</code> to the rescue:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">     Worker thread pool in So
</span></span><span class="line"><span class="cl">┌────────┐ ┌────────┐   ┌────────┐
</span></span><span class="line"><span class="cl">│ Task 1 │ │ Task 2 │...│ Task M │  M tasks
</span></span><span class="line"><span class="cl">└────────┘ └────────┘   └────────┘
</span></span><span class="line"><span class="cl">┌────────────────────────────────┐
</span></span><span class="line"><span class="cl">│           conc.Pool            │  coordinator
</span></span><span class="line"><span class="cl">└────────────────────────────────┘
</span></span><span class="line"><span class="cl">┌────────┐ ┌────────┐   ┌────────┐
</span></span><span class="line"><span class="cl">│ Thrd 1 │ │ Thrd 2 │...│ Thrd N │  N threads, N &lt;&lt; M
</span></span><span class="line"><span class="cl">└────────┘ └────────┘   └────────┘
</span></span><span class="line"><span class="cl">┌────────────────────────────────┐
</span></span><span class="line"><span class="cl">│          OS scheduler          │
</span></span><span class="line"><span class="cl">└────────────────────────────────┘
</span></span></code></pre></div><p>Usage example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Task</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">in</span>  <span class="kt">int</span>
</span></span><span class="line"><span class="cl">    <span class="nx">out</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">square</span><span class="p">(</span><span class="nx">arg</span> <span class="nx">any</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">task</span> <span class="o">:=</span> <span class="nx">arg</span><span class="p">.(</span><span class="o">*</span><span class="nx">Task</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">task</span><span class="p">.</span><span class="nx">out</span> <span class="p">=</span> <span class="nx">task</span><span class="p">.</span><span class="nx">in</span> <span class="o">*</span> <span class="nx">task</span><span class="p">.</span><span class="nx">in</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">tasks</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="nx">Task</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">opts</span> <span class="o">:=</span> <span class="nx">conc</span><span class="p">.</span><span class="nx">PoolOpts</span><span class="p">{</span><span class="nx">NumThreads</span><span class="p">:</span> <span class="mi">2</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nx">pool</span> <span class="o">:=</span> <span class="nx">conc</span><span class="p">.</span><span class="nf">NewPool</span><span class="p">(</span><span class="nx">mem</span><span class="p">.</span><span class="nx">System</span><span class="p">,</span> <span class="nx">opts</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">defer</span> <span class="nx">pool</span><span class="p">.</span><span class="nf">Free</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">tasks</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">tasks</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">in</span> <span class="p">=</span> <span class="nx">i</span>
</span></span><span class="line"><span class="cl">        <span class="nx">pool</span><span class="p">.</span><span class="nf">Go</span><span class="p">(</span><span class="nx">square</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">tasks</span><span class="p">[</span><span class="nx">i</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nx">pool</span><span class="p">.</span><span class="nf">Wait</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><details>
    <summary>Show the translated C code</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">main_Task</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">in</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">out</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">main_Task</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">square</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">arg</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">main_Task</span><span class="o">*</span> <span class="n">task</span> <span class="o">=</span> <span class="p">(</span><span class="n">main_Task</span><span class="o">*</span><span class="p">)</span><span class="n">arg</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">task</span><span class="o">-&gt;</span><span class="n">out</span> <span class="o">=</span> <span class="n">task</span><span class="o">-&gt;</span><span class="n">in</span> <span class="o">*</span> <span class="n">task</span><span class="o">-&gt;</span><span class="n">in</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_Slice</span> <span class="n">tasks</span> <span class="o">=</span> <span class="nf">so_make_slice</span><span class="p">(</span><span class="n">main_Task</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">conc_PoolOpts</span> <span class="n">opts</span> <span class="o">=</span> <span class="p">(</span><span class="n">conc_PoolOpts</span><span class="p">){.</span><span class="n">NumThreads</span> <span class="o">=</span> <span class="mi">2</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="n">conc_Pool</span><span class="o">*</span> <span class="n">pool</span> <span class="o">=</span> <span class="nf">conc_NewPool</span><span class="p">(</span><span class="n">mem_System</span><span class="p">,</span> <span class="n">opts</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">so_int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="nf">so_len</span><span class="p">(</span><span class="n">tasks</span><span class="p">);</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// so_at is a generic macro to get the i-th element of a
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="c1">// specific type (main_Task here) from a type-erased slice.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="c1">// Here we&#39;re getting the i-th task from the tasks slice.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">so_at</span><span class="p">(</span><span class="n">main_Task</span><span class="p">,</span> <span class="n">tasks</span><span class="p">,</span> <span class="n">i</span><span class="p">).</span><span class="n">in</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">conc_Pool_Go</span><span class="p">(</span><span class="n">pool</span><span class="p">,</span> <span class="n">square</span><span class="p">,</span> <span class="o">&amp;</span><span class="nf">so_at</span><span class="p">(</span><span class="n">main_Task</span><span class="p">,</span> <span class="n">tasks</span><span class="p">,</span> <span class="n">i</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nf">conc_Pool_Wait</span><span class="p">(</span><span class="n">pool</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">conc_Pool_Free</span><span class="p">(</span><span class="n">pool</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></details>
<blockquote>
<p>The first argument to <code>NewPool</code>, <code>mem.System</code>, is a memory allocator. Solod avoids hidden allocations, so anything that needs memory takes an allocator explicitly — here it backs the pool's task queue.</p>
</blockquote>
<p>Under the hood, a <code>Pool</code> is a fixed group of worker threads that pull tasks from a shared queue (a ring buffer). It uses one mutex and a few condition variables:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Pool is a bounded pool of worker threads with a wait queue
</span></span></span><span class="line"><span class="cl"><span class="c1">// which execute tasks of the form func(any).
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Pool</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">alloc</span> <span class="nx">mem</span><span class="p">.</span><span class="nx">Allocator</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">mu</span>       <span class="nx">sync</span><span class="p">.</span><span class="nx">Mutex</span>
</span></span><span class="line"><span class="cl">    <span class="nx">notEmpty</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">Cond</span> <span class="c1">// signaled when a task is enqueued
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">notFull</span>  <span class="nx">sync</span><span class="p">.</span><span class="nx">Cond</span> <span class="c1">// signaled when a slot frees
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">allDone</span>  <span class="nx">sync</span><span class="p">.</span><span class="nx">Cond</span> <span class="c1">// broadcast when no task is in flight
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">    <span class="nx">workers</span> <span class="p">[]</span><span class="nx">Thread</span>
</span></span><span class="line"><span class="cl">    <span class="nx">queue</span>   <span class="p">[]</span><span class="nx">task</span> <span class="c1">// ring buffer of submitted tasks
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">active</span>  <span class="kt">int</span>    <span class="c1">// tasks submitted but not yet finished
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">stopped</span> <span class="kt">bool</span>   <span class="c1">// set by Free to drain and exit
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// NewPool creates a pool with a given number
</span></span></span><span class="line"><span class="cl"><span class="c1">// of worker threads and starts them.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">NewPool</span><span class="p">(</span><span class="nx">alloc</span> <span class="nx">mem</span><span class="p">.</span><span class="nx">Allocator</span><span class="p">,</span> <span class="nx">opts</span> <span class="nx">PoolOpts</span><span class="p">)</span> <span class="o">*</span><span class="nx">Pool</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Go submits a task for execution, blocking while the queue is full.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="o">*</span><span class="nx">Pool</span><span class="p">)</span> <span class="nf">Go</span><span class="p">(</span><span class="nx">fn</span> <span class="kd">func</span><span class="p">(</span><span class="nx">any</span><span class="p">),</span> <span class="nx">arg</span> <span class="nx">any</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Wait blocks until all submitted tasks finish.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="o">*</span><span class="nx">Pool</span><span class="p">)</span> <span class="nf">Wait</span><span class="p">()</span>
</span></span></code></pre></div><details>
    <summary>Show the translated C code</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Pool is a bounded pool of worker threads with a wait queue
</span></span></span><span class="line"><span class="cl"><span class="c1">// which execute tasks of the form func(any).
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="n">conc_Pool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">mem_Allocator</span> <span class="n">alloc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">sync_Mutex</span> <span class="n">mu</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">sync_Cond</span>  <span class="n">notEmpty</span><span class="p">;</span> <span class="c1">// signaled when a task is enqueued
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">sync_Cond</span>  <span class="n">notFull</span><span class="p">;</span>  <span class="c1">// signaled when a slot frees
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">sync_Cond</span>  <span class="n">allDone</span><span class="p">;</span>  <span class="c1">// broadcast when no task is in flight
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">    <span class="n">so_Slice</span> <span class="n">workers</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_Slice</span> <span class="n">queue</span><span class="p">;</span>      <span class="c1">// ring buffer of submitted tasks
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">so_int</span>   <span class="n">active</span><span class="p">;</span>     <span class="c1">// tasks submitted but not yet finished
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">bool</span>     <span class="n">stopped</span><span class="p">;</span>    <span class="c1">// set by Free to drain and exit
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="n">conc_Pool</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">conc_Pool</span><span class="o">*</span> <span class="nf">conc_NewPool</span><span class="p">(</span><span class="n">mem_Allocator</span> <span class="n">alloc</span><span class="p">,</span> <span class="n">conc_PoolOpts</span> <span class="n">opts</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span>       <span class="nf">conc_Pool_Go</span><span class="p">(</span><span class="n">conc_Pool</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">fn</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span><span class="p">),</span> <span class="kt">void</span><span class="o">*</span> <span class="n">arg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span>       <span class="nf">conc_Pool_Wait</span><span class="p">(</span><span class="n">conc_Pool</span><span class="o">*</span> <span class="n">p</span><span class="p">);</span>
</span></span></code></pre></div></details>
<p><code>notEmpty</code> wakes up a worker when there are tasks to do, <code>notFull</code> applies back-pressure when the queue is full, and <code>allDone</code> lets <code>Wait</code> know when everything is finished. It's a classic producer-consumer setup, about <a href="https://github.com/solod-dev/solod/blob/main/so/conc/pool.go">200 lines of code</a>, and there's nothing fancy about it.</p>
<p>The heart of the pool is the worker loop. Each thread blocks until a task appears, runs it outside the lock so workers execute in parallel, then records that it finished:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// workerMain runs on every pool thread: pull a task, run it, repeat.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">workerMain</span><span class="p">(</span><span class="nx">arg</span> <span class="nx">any</span><span class="p">)</span> <span class="nx">any</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">p</span> <span class="o">:=</span> <span class="nx">arg</span><span class="p">.(</span><span class="o">*</span><span class="nx">Pool</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">p</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="nx">p</span><span class="p">.</span><span class="nf">qempty</span><span class="p">()</span> <span class="o">&amp;&amp;</span> <span class="p">!</span><span class="nx">p</span><span class="p">.</span><span class="nx">stopped</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">p</span><span class="p">.</span><span class="nx">notEmpty</span><span class="p">.</span><span class="nf">Wait</span><span class="p">()</span> <span class="c1">// sleep until a task is enqueued
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nx">p</span><span class="p">.</span><span class="nf">qempty</span><span class="p">()</span> <span class="o">&amp;&amp;</span> <span class="nx">p</span><span class="p">.</span><span class="nx">stopped</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">p</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span> <span class="c1">// queue drained and pool shutting down
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nx">t</span> <span class="o">:=</span> <span class="nx">p</span><span class="p">.</span><span class="nf">qpop</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="nx">p</span><span class="p">.</span><span class="nx">notFull</span><span class="p">.</span><span class="nf">Signal</span><span class="p">()</span> <span class="c1">// a slot freed for a waiting submitter
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nx">p</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nx">t</span><span class="p">.</span><span class="nf">fn</span><span class="p">(</span><span class="nx">t</span><span class="p">.</span><span class="nx">arg</span><span class="p">)</span> <span class="c1">// run the task with the lock released
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">        <span class="nx">p</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="nx">p</span><span class="p">.</span><span class="nx">active</span><span class="o">--</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nx">p</span><span class="p">.</span><span class="nx">active</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">p</span><span class="p">.</span><span class="nx">allDone</span><span class="p">.</span><span class="nf">Broadcast</span><span class="p">()</span> <span class="c1">// wake anyone parked in Wait
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nx">p</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><details>
    <summary>Show the translated C code</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// workerMain runs on every pool thread: pull a task, run it, repeat.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">void</span><span class="o">*</span> <span class="nf">workerMain</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">arg</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">conc_Pool</span><span class="o">*</span> <span class="n">p</span> <span class="o">=</span> <span class="p">(</span><span class="n">conc_Pool</span><span class="o">*</span><span class="p">)</span><span class="n">arg</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">sync_Mutex_Lock</span><span class="p">(</span><span class="o">&amp;</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">mu</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="p">(;</span> <span class="nf">conc_Pool_qempty</span><span class="p">(</span><span class="n">p</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">stopped</span><span class="p">;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">sync_Cond_Wait</span><span class="p">(</span><span class="o">&amp;</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">notEmpty</span><span class="p">);</span> <span class="c1">// sleep until a task is enqueued
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nf">conc_Pool_qempty</span><span class="p">(</span><span class="n">p</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">p</span><span class="o">-&gt;</span><span class="n">stopped</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">sync_Mutex_Unlock</span><span class="p">(</span><span class="o">&amp;</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">mu</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span><span class="p">;</span> <span class="c1">// queue drained and pool shutting down
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="n">task</span> <span class="n">t</span> <span class="o">=</span> <span class="nf">conc_Pool_qpop</span><span class="p">(</span><span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">sync_Cond_Signal</span><span class="p">(</span><span class="o">&amp;</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">notFull</span><span class="p">);</span> <span class="c1">// a slot freed for a waiting submitter
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">sync_Mutex_Unlock</span><span class="p">(</span><span class="o">&amp;</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">mu</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">t</span><span class="p">.</span><span class="nf">fn</span><span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">arg</span><span class="p">);</span> <span class="c1">// run the task with the lock released
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">        <span class="nf">sync_Mutex_Lock</span><span class="p">(</span><span class="o">&amp;</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">mu</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">p</span><span class="o">-&gt;</span><span class="n">active</span><span class="o">--</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">active</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">sync_Cond_Broadcast</span><span class="p">(</span><span class="o">&amp;</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">allDone</span><span class="p">);</span> <span class="c1">// wake anyone parked in Wait
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nf">sync_Mutex_Unlock</span><span class="p">(</span><span class="o">&amp;</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">mu</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></details>
<p>This is what separates a pool from a plain queue. <code>Pool.Go</code> bumps <code>active</code> as it enqueues; each worker decrements it after running a task, and the last one out broadcasts <code>allDone</code>.</p>
<p><code>Pool.Wait</code> sleeps until the count hits zero:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Wait blocks until every submitted task has finished.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="o">*</span><span class="nx">Pool</span><span class="p">)</span> <span class="nf">Wait</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">p</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="nx">p</span><span class="p">.</span><span class="nx">active</span> <span class="o">!=</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">p</span><span class="p">.</span><span class="nx">allDone</span><span class="p">.</span><span class="nf">Wait</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nx">p</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><details>
    <summary>Show the translated C code</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Wait blocks until every submitted task has finished.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">conc_Pool_Wait</span><span class="p">(</span><span class="n">conc_Pool</span><span class="o">*</span> <span class="n">p</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">sync_Mutex_Lock</span><span class="p">(</span><span class="o">&amp;</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">mu</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;</span> <span class="n">p</span><span class="o">-&gt;</span><span class="n">active</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">sync_Cond_Wait</span><span class="p">(</span><span class="o">&amp;</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">allDone</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nf">sync_Mutex_Unlock</span><span class="p">(</span><span class="o">&amp;</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">mu</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></details>
<p>The tradeoff is that the number of worker threads is fixed. In Go, a program can handle thousands of concurrent I/O waits because blocked goroutines use very little memory. A So pool can't do this — if all N workers are parked on a blocking syscall, the pool is stalled until one returns. You have to set the pool size based on the workload, instead of letting the runtime manage it for you.</p>
<h2 id="channel">Channel</h2>
<p>Channels are an important part of Go's concurrency model, and So's <code>conc.Chan[T]</code> gives you something quite similar. Just like in Go, it passes values by copy and comes in buffered and unbuffered flavors:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">ch</span> <span class="o">:=</span> <span class="nx">conc</span><span class="p">.</span><span class="nx">NewChan</span><span class="p">[</span><span class="kt">int</span><span class="p">](</span><span class="nx">mem</span><span class="p">.</span><span class="nx">System</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="c1">// buffered, capacity 2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">defer</span> <span class="nx">ch</span><span class="p">.</span><span class="nf">Free</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Producer on its own thread.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">prod</span> <span class="o">:=</span> <span class="nx">producer</span><span class="p">{</span><span class="nx">ch</span><span class="p">:</span> <span class="o">&amp;</span><span class="nx">ch</span><span class="p">,</span> <span class="nx">n</span><span class="p">:</span> <span class="mi">5</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">thr</span> <span class="o">:=</span> <span class="nx">conc</span><span class="p">.</span><span class="nf">Go</span><span class="p">(</span><span class="nx">produce</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">prod</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">defer</span> <span class="nx">thr</span><span class="p">.</span><span class="nf">Wait</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Consume until the channel is closed and drained.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">v</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="nx">ch</span><span class="p">.</span><span class="nf">Recv</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">v</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;received %d\n&#34;</span><span class="p">,</span> <span class="nx">v</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><details>
    <summary>Show the translated C code</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// conc_NewChan, conc_Chan_Recv, and friends are generic macros:
</span></span></span><span class="line"><span class="cl"><span class="c1">// the element type (so_int here) is passed as the first argument.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">conc_Chan</span> <span class="n">ch</span> <span class="o">=</span> <span class="nf">conc_NewChan</span><span class="p">(</span><span class="n">so_int</span><span class="p">,</span> <span class="n">mem_System</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span> <span class="c1">// buffered, capacity 2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// Producer on its own thread.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">producer</span> <span class="n">prod</span> <span class="o">=</span> <span class="p">(</span><span class="n">producer</span><span class="p">){.</span><span class="n">ch</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">ch</span><span class="p">,</span> <span class="p">.</span><span class="n">n</span> <span class="o">=</span> <span class="mi">5</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="n">conc_Thread</span> <span class="n">thr</span> <span class="o">=</span> <span class="nf">conc_Go</span><span class="p">(</span><span class="n">produce</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">prod</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Consume until the channel is closed and drained.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">so_int</span> <span class="n">v</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(;</span> <span class="nf">conc_Chan_Recv</span><span class="p">(</span><span class="n">so_int</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ch</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">v</span><span class="p">);)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">fmt_Printf</span><span class="p">(</span><span class="s">&#34;received %d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">conc_Thread_Wait</span><span class="p">(</span><span class="n">thr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">conc_Chan_Free</span><span class="p">(</span><span class="n">so_int</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ch</span><span class="p">);</span>
</span></span></code></pre></div></details>
<p><code>Chan[T]</code> is a thin generic shell over one of two engines, picked at creation time:</p>
<p><strong>Buffered</strong> (<code>n &gt; 0</code>) is a mutex-guarded ring buffer with <code>notEmpty</code> and <code>notFull</code> condition variables — like the <code>Pool</code> queue. Senders block when it's full, receivers block when it's empty.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Buffer</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">alloc</span> <span class="nx">mem</span><span class="p">.</span><span class="nx">Allocator</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">mu</span>       <span class="nx">sync</span><span class="p">.</span><span class="nx">Mutex</span>
</span></span><span class="line"><span class="cl">    <span class="nx">notEmpty</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">Cond</span> <span class="c1">// signaled when an item becomes available
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">notFull</span>  <span class="nx">sync</span><span class="p">.</span><span class="nx">Cond</span> <span class="c1">// signaled when a slot frees
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">    <span class="nx">buf</span>    <span class="nx">mem</span><span class="p">.</span><span class="nx">Array</span>   <span class="c1">// ring buffer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">closed</span> <span class="kt">bool</span>        <span class="c1">// true after Close
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Send copies v into the ring, blocking while it is full.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">ch</span> <span class="o">*</span><span class="nx">Buffer</span><span class="p">)</span> <span class="nf">Send</span><span class="p">(</span><span class="nx">v</span> <span class="nx">any</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">ch</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="nx">ch</span><span class="p">.</span><span class="nf">bfull</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">ch</span><span class="p">.</span><span class="nx">notFull</span><span class="p">.</span><span class="nf">Wait</span><span class="p">()</span> <span class="c1">// back-pressure until a slot frees
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nx">ch</span><span class="p">.</span><span class="nf">bpush</span><span class="p">(</span><span class="nx">v</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">ch</span><span class="p">.</span><span class="nx">notEmpty</span><span class="p">.</span><span class="nf">Signal</span><span class="p">()</span> <span class="c1">// wake one waiting receiver
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">ch</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><details>
    <summary>Show the translated C code</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">conc_Buffer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">mem_Allocator</span> <span class="n">alloc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">sync_Mutex</span> <span class="n">mu</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">sync_Cond</span>  <span class="n">notEmpty</span><span class="p">;</span> <span class="c1">// signaled when an item becomes available
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">sync_Cond</span>  <span class="n">notFull</span><span class="p">;</span>  <span class="c1">// signaled when a slot frees
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">    <span class="n">mem_Array</span> <span class="n">buf</span><span class="p">;</span>       <span class="c1">// ring buffer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">bool</span> <span class="n">closed</span><span class="p">;</span>         <span class="c1">// true after Close
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="n">conc_Buffer</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Send copies v into the ring, blocking while it is full.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">conc_Buffer_Send</span><span class="p">(</span><span class="n">conc_Buffer</span><span class="o">*</span> <span class="n">ch</span><span class="p">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">sync_Mutex_Lock</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ch</span><span class="o">-&gt;</span><span class="n">mu</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;</span> <span class="nf">conc_Buffer_bfull</span><span class="p">(</span><span class="n">ch</span><span class="p">);)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">sync_Cond_Wait</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ch</span><span class="o">-&gt;</span><span class="n">notFull</span><span class="p">);</span> <span class="c1">// back-pressure until a slot frees
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nf">conc_Buffer_bpush</span><span class="p">(</span><span class="n">ch</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">sync_Cond_Signal</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ch</span><span class="o">-&gt;</span><span class="n">notEmpty</span><span class="p">);</span> <span class="c1">// wake one waiting receiver
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">sync_Mutex_Unlock</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ch</span><span class="o">-&gt;</span><span class="n">mu</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></details>
<blockquote>
<p>The full implementation also checks for <code>closed</code>, but I left it out for brevity.</p>
</blockquote>
<p><code>Recv</code> is the mirror method: block while empty, pop the next value, signal <code>notFull</code> to wake a sender. It also handles the closed channel, returning <code>false</code> once the buffer is closed and drained. The rest is this lock-wait-signal core.</p>
<p><a href="https://github.com/solod-dev/solod/blob/main/so/conc/buffer.go">Buffer source code</a></p>
<p><strong>Unbuffered</strong> (<code>n == 0</code>) is a rendezvous: each send blocks until a receiver takes the value, copying <code>vsize</code> bytes directly from the sender's stack to the receiver's destination without using an intermediate buffer.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Rendezvous</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">alloc</span> <span class="nx">mem</span><span class="p">.</span><span class="nx">Allocator</span>
</span></span><span class="line"><span class="cl">    <span class="nx">vsize</span> <span class="kt">int</span> <span class="c1">// size in bytes of a handed-off value
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">    <span class="nx">mu</span>   <span class="nx">sync</span><span class="p">.</span><span class="nx">Mutex</span>
</span></span><span class="line"><span class="cl">    <span class="nx">cond</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">Cond</span> <span class="c1">// broadcast on every slot state change
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">    <span class="nx">src</span>     <span class="nx">any</span>  <span class="c1">// the sender&#39;s published value (valid while full)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">full</span>    <span class="kt">bool</span> <span class="c1">// a value is published and not yet freed
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">claimed</span> <span class="kt">bool</span> <span class="c1">// the published value has been taken by a receiver
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">closed</span>  <span class="kt">bool</span> <span class="c1">// true after Close
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Send publishes v and waits for a receiver to take it.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">ch</span> <span class="o">*</span><span class="nx">Rendezvous</span><span class="p">)</span> <span class="nf">Send</span><span class="p">(</span><span class="nx">v</span> <span class="nx">any</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">ch</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="nx">ch</span><span class="p">.</span><span class="nx">full</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">ch</span><span class="p">.</span><span class="nx">cond</span><span class="p">.</span><span class="nf">Wait</span><span class="p">()</span>  <span class="c1">// wait for the previous hand-off to finish
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nx">ch</span><span class="p">.</span><span class="nx">src</span><span class="p">,</span> <span class="nx">ch</span><span class="p">.</span><span class="nx">full</span><span class="p">,</span> <span class="nx">ch</span><span class="p">.</span><span class="nx">claimed</span> <span class="p">=</span> <span class="nx">v</span><span class="p">,</span> <span class="kc">true</span><span class="p">,</span> <span class="kc">false</span> <span class="c1">// publish
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">ch</span><span class="p">.</span><span class="nx">cond</span><span class="p">.</span><span class="nf">Broadcast</span><span class="p">()</span> <span class="c1">// wakeup #1: wake a receiver
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">for</span> <span class="p">!</span><span class="nx">ch</span><span class="p">.</span><span class="nx">claimed</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">ch</span><span class="p">.</span><span class="nx">cond</span><span class="p">.</span><span class="nf">Wait</span><span class="p">()</span>  <span class="c1">// wait until the value is taken
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nx">ch</span><span class="p">.</span><span class="nx">src</span><span class="p">,</span> <span class="nx">ch</span><span class="p">.</span><span class="nx">full</span> <span class="p">=</span> <span class="kc">nil</span><span class="p">,</span> <span class="kc">false</span> <span class="c1">// free the slot
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">ch</span><span class="p">.</span><span class="nx">cond</span><span class="p">.</span><span class="nf">Broadcast</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="nx">ch</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><details>
    <summary>Show the translated C code</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">conc_Rendezvous</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">mem_Allocator</span> <span class="n">alloc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">vsize</span><span class="p">;</span> <span class="c1">// size in bytes of a handed-off value
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">    <span class="n">sync_Mutex</span> <span class="n">mu</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">sync_Cond</span>  <span class="n">cond</span><span class="p">;</span> <span class="c1">// broadcast on every slot state change
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">src</span><span class="p">;</span>     <span class="c1">// the sender&#39;s published value (valid while full)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">bool</span>  <span class="n">full</span><span class="p">;</span>    <span class="c1">// a value is published and not yet freed
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">bool</span>  <span class="n">claimed</span><span class="p">;</span> <span class="c1">// the published value has been taken by a receiver
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">bool</span>  <span class="n">closed</span><span class="p">;</span>  <span class="c1">// true after Close
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="n">conc_Rendezvous</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Send publishes v and waits for a receiver to take it.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">conc_Rendezvous_Send</span><span class="p">(</span><span class="n">conc_Rendezvous</span><span class="o">*</span> <span class="n">ch</span><span class="p">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">sync_Mutex_Lock</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ch</span><span class="o">-&gt;</span><span class="n">mu</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;</span> <span class="n">ch</span><span class="o">-&gt;</span><span class="n">full</span><span class="p">;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">sync_Cond_Wait</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ch</span><span class="o">-&gt;</span><span class="n">cond</span><span class="p">);</span>  <span class="c1">// wait for the previous hand-off to finish
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">ch</span><span class="o">-&gt;</span><span class="n">src</span> <span class="o">=</span> <span class="n">v</span><span class="p">;</span>                    <span class="c1">// publish
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">ch</span><span class="o">-&gt;</span><span class="n">full</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">ch</span><span class="o">-&gt;</span><span class="n">claimed</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">sync_Cond_Broadcast</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ch</span><span class="o">-&gt;</span><span class="n">cond</span><span class="p">);</span> <span class="c1">// wakeup #1: wake a receiver
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">for</span> <span class="p">(;</span> <span class="o">!</span><span class="n">ch</span><span class="o">-&gt;</span><span class="n">claimed</span><span class="p">;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">sync_Cond_Wait</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ch</span><span class="o">-&gt;</span><span class="n">cond</span><span class="p">);</span>  <span class="c1">// wait until the value is taken
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">ch</span><span class="o">-&gt;</span><span class="n">full</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>               <span class="c1">// free the slot
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">ch</span><span class="o">-&gt;</span><span class="n">src</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">sync_Cond_Broadcast</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ch</span><span class="o">-&gt;</span><span class="n">cond</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">sync_Mutex_Unlock</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ch</span><span class="o">-&gt;</span><span class="n">mu</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></details>
<p><code>Recv</code> is the other half: it waits for a published, unclaimed value, copies <code>vsize</code> bytes straight from the sender's stack into <code>dst</code> (no intermediate buffer), marks it as claimed, and broadcasts to wake the sender back, creating wakeup #2. One hand-off, two wakeups.</p>
<p>Copying directly from the sender's stack is safe because of that second wakeup. <code>src</code> is a pointer to <code>v</code>, which lives on the sender's stack. While the receiver is reading it, the sender is parked in <code>for !ch.claimed { ch.cond.Wait() }</code>, so its stack frame stays alive. The sender only returns (and reclaims that memory) after the receiver sets <code>claimed</code> and wakes it up. There's no need to copy into a shared buffer because the source is guaranteed to outlive the read.</p>
<p><a href="https://github.com/solod-dev/solod/blob/main/so/conc/rendezvous.go">Rendezvous source code</a></p>
<p>As you can see, the API is pretty similar to Go. Now let's look at the numbers.</p>
<h2 id="performance">Performance</h2>
<p>Here's the main tradeoff: pthread-based concurrency primitives are fast when no one has to block, but they get slow when someone does. And it's always for the same reason.</p>
<p>Go schedules goroutines in userspace. When one goroutine blocks on a channel and another wakes it up, the runtime moves them between its own queues — no kernel involved. POSIX threads, on the other hand, don't provide a userland scheduler. When a thread blocks on a condition variable, it parks in the kernel, and waking it up requires a syscall. Every hand-off between threads that actually parks pays the cost of a syscall on both ends.</p>
<p>You can clearly see the difference in the mutex benchmarks. With 8 competing threads, it all comes down to whether the waiting threads have to park or not:</p>
<table>
<thead>
<tr>
<th>Mutex benchmark</th>
<th style="text-align:right">Go</th>
<th style="text-align:right">So</th>
<th>Winner</th>
</tr>
</thead>
<tbody>
<tr>
<td>Uncontended, 1 thread</td>
<td style="text-align:right">14ns</td>
<td style="text-align:right">9ns</td>
<td>So - 1.6x</td>
</tr>
<tr>
<td>Contended spin, 8 threads</td>
<td style="text-align:right">75ns</td>
<td style="text-align:right">27ns</td>
<td>So - 2.8x</td>
</tr>
<tr>
<td>Contended work, 8 threads</td>
<td style="text-align:right">1.1µs</td>
<td style="text-align:right">2.0µs</td>
<td>Go - 1.8x</td>
</tr>
</tbody>
</table>
<blockquote>
<p>Each number is the average time for a single <code>Lock</code>/<code>Unlock</code> pair. The uncontended benchmark runs on one thread, while the contended benchmarks have multiple threads fighting over the same mutex.</p>
</blockquote>
<p>Notice that So actually wins the first two benchmarks, and for good reason. So's <code>Lock</code> is a plain <code>pthread_mutex_lock</code> call with nothing extra, while Go's <code>sync.Mutex</code> adds more overhead — like starvation-mode tracking and a runtime that stays involved because a goroutine can be preempted in the middle of a critical section.</p>
<p>When nobody parks, that overhead is the main cost, and the thinner wrapper is closer to the hardware. With an empty critical section (the <em>spin</em> benchmark), a waiting thread grabs the lock while still spinning and almost never parks — So wins by 2.8x. The uncontended benchmark (a single thread, no contention) shows the same thing: less code between the call and the lock, so 9ns versus 14ns.</p>
<p>The picture flips the moment threads have to park. Give the critical section about a microsecond of real work (the <em>work</em> benchmark) and waiters exhaust their spin budget and park. Now every hand-off costs a wakeup syscall, and So drops to half of Go's throughput. The work is identical in both cases — the difference comes from the parking cost.</p>
<p>Condition variables demonstrate this clearly because they <em>always</em> park:</p>
<table>
<thead>
<tr>
<th>Cond benchmark</th>
<th style="text-align:right">Go</th>
<th style="text-align:right">So</th>
<th>Winner</th>
</tr>
</thead>
<tbody>
<tr>
<td>1 waiter</td>
<td style="text-align:right">150ns</td>
<td style="text-align:right">1.5µs</td>
<td>Go - 10x</td>
</tr>
<tr>
<td>8 waiters</td>
<td style="text-align:right">2.0µs</td>
<td style="text-align:right">14µs</td>
<td>Go - 7.0x</td>
</tr>
<tr>
<td>32 waiters</td>
<td style="text-align:right">9.0µs</td>
<td style="text-align:right">60µs</td>
<td>Go - 6.7x</td>
</tr>
</tbody>
</table>
<blockquote>
<p>Each number is the cost of one rendezvous round: a single broadcast that wakes every waiter and hands control back, with N waiters plus one broadcaster.</p>
</blockquote>
<p>Pthread-based condition variable is consistently 7-10 times slower. There's no trick to close this gap — it's just the cost of waking up a real OS thread instead of a goroutine.</p>
<p>Channels have the same issue because they're built using mutexes and condition variables:</p>
<table>
<thead>
<tr>
<th>Chan benchmark</th>
<th style="text-align:right">Go</th>
<th style="text-align:right">So</th>
<th>Winner</th>
</tr>
</thead>
<tbody>
<tr>
<td>Uncontended, 1 thread</td>
<td style="text-align:right">24ns</td>
<td style="text-align:right">21ns</td>
<td>So - 1.1x</td>
</tr>
<tr>
<td>Unbuffered, 2 threads</td>
<td style="text-align:right">130ns</td>
<td style="text-align:right">3.0µs</td>
<td>Go - 23x</td>
</tr>
<tr>
<td>Buffered (10), 2 threads</td>
<td style="text-align:right">44ns</td>
<td style="text-align:right">400ns</td>
<td>Go - 9.1x</td>
</tr>
<tr>
<td>Buffered (100), 2 threads</td>
<td style="text-align:right">33ns</td>
<td style="text-align:right">70ns</td>
<td>Go - 2.1x</td>
</tr>
</tbody>
</table>
<blockquote>
<p>Each number is the cost of moving one value through the channel (send plus its matching receive). The number in parentheses is the buffer capacity.</p>
</blockquote>
<p>The uncontended case fills and drains a buffer from a single thread, so nothing ever blocks — it's just a lock plus a copy, which gives So a slight advantage. But the moment a producer and consumer actually start handing off work, So has to wake up a thread for every transfer that gets parked. It's worst for the unbuffered channel, where every value is a rendezvous with two wakeups: 23x slower. A larger buffer helps a lot — with room for 100 items, most sends go through without waking anyone, and the gap narrows to about 2x.</p>
<p>The consequence is that the larger your tasks are, the better pthread-based concurrency works. If you use a channel for fine-grained, value-at-a-time streaming between threads, performance will suffer. But if you use a channel to pass whole work items to a pool, where each item takes tens of microseconds to process, the wakeup cost becomes negligible. The pool benchmarks on realistic workloads confirms this:</p>
<table>
<thead>
<tr>
<th>Pool benchmark</th>
<th style="text-align:right">Go</th>
<th style="text-align:right">So</th>
<th>Winner</th>
</tr>
</thead>
<tbody>
<tr>
<td>1000 CPU tasks (~40µs each)</td>
<td style="text-align:right">7ms</td>
<td style="text-align:right">8ms</td>
<td>Go - 1.1x</td>
</tr>
<tr>
<td>64 IO tasks (1ms block each)</td>
<td style="text-align:right">9ms</td>
<td style="text-align:right">10ms</td>
<td>Go - 1.1x</td>
</tr>
</tbody>
</table>
<blockquote>
<p>Each number is the wall-clock time for 8 workers to process the whole batch.</p>
</blockquote>
<p>Here, So is within 1.1x of Go. The per-task dispatch cost is still present, but it's spread out over real work, and the performance penalty is pretty small.</p>
<div class="boxed">
<p><strong>Benchmarking</strong></p>
<p>All benchmarks were run on an Apple M1 CPU running macOS. The C code was compiled with Clang 16 using these CFLAGS and mimalloc as the system allocator:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">-Ofast -march=native -flto -funroll-loops -DNDEBUG
</span></span></code></pre></div><p>The results shown are the medians from several benchmark runs. Each benchmark ran many iterations, following the same logic as Go's own benchmarking.</p>
<p>The Go benchmarks used Go 1.26 and <code>go test -bench=.</code>.</p>
<p>Source code for both So's and Go's benchmarks:
<a href="https://github.com/solod-dev/solod/tree/main/so/conc/bench">conc</a> •
<a href="https://github.com/solod-dev/solod/tree/main/so/sync/bench">sync</a></p>
</div>
<p>Here's a summary of the strengths and weaknesses of the pthread-based approach:</p>
<ul>
<li>➕ Coarse-grained pooled workloads are within about 10% of Go's performance.</li>
<li>➕ Uncontended locks and spin-friendly critical sections perform quite well.</li>
<li>➕ Atomic operations are as fast as in Go.</li>
<li>➕ The implementation is 100x simpler.</li>
<li>➖ Anything that needs to park and wake an OS thread is <em>much</em> slower than Go's userspace scheduler.</li>
<li>➖ The pool can't handle thousands of blocked waiters like goroutines can.</li>
</ul>
<p>If you're looking for &quot;thousands of cheap goroutines&quot;, the pthread-based approach will let you down. But if you're fine with &quot;a few worker threads handling lots of tasks&quot;, it holds up well.</p>
<h2 id="design-decisions">Design decisions</h2>
<p>Three decisions influenced the way I implemented concurrency in Solod.</p>
<p><strong>Pthreads, not fibers</strong>. I know there are coroutine/fiber libraries for C that avoid the kernel wakeup cost — single-threaded ones like <a href="https://github.com/tidwall/neco">neco</a>, and multi-threaded ones like <a href="https://github.com/iqiyi/libfiber">libfiber</a>. A userspace scheduler is exactly what would help to match Go in the benchmarks above.</p>
<p>I decided not to use one. I wanted something dead simple — an approach I could explain in a paragraph, using tools every C programmer already knows. The trade-off is that you lose some performance with fine-grained blocking, but in many real-world situations, pthreads work fine if you use a worker pool. For me, keeping things simple is more important than saving a few microseconds during task hand-offs. For now, at least.</p>
<p><strong>Standard library, not language</strong>. Go bakes goroutines, channels, and select right into the language. I decided to keep everything in the stdlib for two reasons.</p>
<p>➀ It follows So's &quot;no hidden allocations&quot; rule. In Go, <code>go f()</code> quietly allocates a goroutine stack, and <code>make(chan T, n)</code> allocates a buffer. In So, all allocations are explicit: you pass an allocator to <code>NewChan</code> and <code>NewPool</code>, and you always know exactly where the memory comes from — whether it's the system allocator, an arena, or something else.</p>
<p>➁ A library is more flexible. Since a pool is a regular value, you can have as many as you need, each sized for its specific purpose. In a multi-stage pipeline where each stage needs a different capacity, you can start one pool per stage, each with its own <code>NumThreads</code> and <code>QueueSize</code>, instead of being given a single global scheduler. The language stays simple, and the flexibility is in code you can easily read.</p>
<p><strong>Timeouts, not select</strong>. Go's <code>select</code> waits on several channel operations at once and proceeds with whichever is ready first. Implementing it would require a lot of work — a thread has to register interest on multiple channels, block once, and then wake up when any of them is ready — so I left it out. Instead, <code>Chan</code> offers <code>SendTimeout</code> and <code>RecvTimeout</code>, which cover two common uses of <code>select</code> with a single channel:</p>
<ul>
<li>&quot;Do this, but give up after a while&quot; (Go's <code>case &lt;-time.After(...)</code> idiom).</li>
<li>&quot;Do this only if it won't block&quot; (Go's non-blocking <code>default</code> branch).</li>
</ul>
<p>What's missing is the ability to block on multiple channels at once and continue with whichever one is ready first, as well as the option to mix sends and receives in the same selection.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>How close can you get to Go's concurrency using only pthreads? Close enough to be useful, but not enough to really match Go. You can wrap real OS threads with familiar APIs — mutexes, condition variables, pools, channels — and the code will look and act a lot like Go, at least until a thread needs to block. But there's no scheduler underneath, so when a thread blocks, it's an actual thread waiting in the kernel, not a goroutine that's paused for free. That's the main limitation of this approach.</p>
<p>What you get in return is brutal simplicity. Every primitive is a thin wrapper with no runtime hiding behind it, so the performance is exactly what the OS gives you: fast atomics, fast uncontended locks, and pooled throughput within ~10% of Go on coarse-grained work. But as soon as you switch to fine-grained, one-value-at-a-time hand-offs, the cost of kernel wakeups becomes the main factor, and you'll notice the slowdown.</p>
<p>If you think the pthread approach might work for you, I invite you to try <a href="https://github.com/solod-dev/solod">Solod</a>. It includes the <code>sync</code> and <code>conc</code> packages, along with many others ported from Go's standard library.</p>
]]></content:encoded></item><item><title>Solod v0.2: Networking, new targets, friendlier interop</title><link>https://antonz.org/solod-v0-2/</link><pubDate>Fri, 26 Jun 2026 12:30:00 +0000</pubDate><guid>https://antonz.org/solod-v0-2/</guid><description>A system-level language with Go syntax and a familiar standard library.</description><content:encoded><![CDATA[<p>Solod (<strong>So</strong>) is a system-level language with Go syntax, zero runtime, and a familiar standard library. It's designed for two main audiences:</p>
<ul>
<li>Go developers who want low-level control and zero-cost C interop without having to learn Zig or Odin.</li>
<li>C developers who like Go's style.</li>
</ul>
<p>The <a href="/solod-v0-1">previous version</a> (v0.1) focused on porting core Go stdlib packages and providing convenient C interop. At the end of that post, I said the next release would focus on networking, concurrency, or both. Now, networking is here — the v0.2 release I'm sharing today includes support for TCP, UDP, and Unix domain sockets. Concurrency is still planned for the future, so for now, servers handle one connection at a time.</p>
<p>This release also lets you compile So to more targets, like 32-bit platforms, WebAssembly, and bare metal. And C interop even smoother!</p>
<p><a href="#networking">Networking</a> •
<a href="#tcp-server">TCP server</a> •
<a href="#tcp-client">TCP client</a> •
<a href="#deadlines">Deadlines</a> •
<a href="#ip-addresses">IP addresses</a> •
<a href="#new-targets">Targets</a> •
<a href="#friendlier-interop">Interop</a> •
<a href="#more-stdlib">Stdlib</a> •
<a href="#wrapping-up">Wrapping up</a></p>
<h2 id="networking">Networking</h2>
<p>The main feature in v0.2 is the <code>net</code> package. It's a simplified version of Go's <code>net</code> package which supports the three most commonly used transports:</p>
<ul>
<li><strong>TCP</strong> (networks <code>tcp</code>, <code>tcp4</code>, <code>tcp6</code>) via <code>ResolveTCPAddr</code>, <code>DialTCP</code>, and <code>ListenTCP</code>, with the <code>TCPConn</code> and <code>TCPListener</code> types.</li>
<li><strong>UDP</strong> (networks <code>udp</code>, <code>udp4</code>, <code>udp6</code>) via <code>ResolveUDPAddr</code>, <code>DialUDP</code> (a connected socket), and <code>ListenUDP</code> (an unconnected socket with <code>ReadFrom</code>/<code>WriteTo</code>).</li>
<li><strong>Unix domain sockets</strong> (<code>unix</code> for streams, <code>unixgram</code> for datagrams) via <code>ResolveUnixAddr</code>, <code>DialUnix</code>, <code>ListenUnix</code>, and <code>ListenUnixgram</code>.</li>
</ul>
<p>The API mirrors Go closely, so most of it will feel familiar. The big difference is that So has no goroutines, so there's no concurrent server support — you accept and serve connections sequentially. More on that in a moment.</p>
<h2 id="tcp-server">TCP server</h2>
<p>Let's build a classic: an echo server that accepts a connection, reads a message, and sends it back.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">&#34;solod.dev/so/net&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Resolve the local address to listen on.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">laddr</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">net</span><span class="p">.</span><span class="nf">ResolveTCPAddr</span><span class="p">(</span><span class="s">&#34;tcp&#34;</span><span class="p">,</span> <span class="s">&#34;127.0.0.1:8080&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Start listening on the local address.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">ln</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">net</span><span class="p">.</span><span class="nf">ListenTCP</span><span class="p">(</span><span class="s">&#34;tcp&#34;</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">laddr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">defer</span> <span class="nx">ln</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="nb">println</span><span class="p">(</span><span class="s">&#34;listening on&#34;</span><span class="p">,</span> <span class="s">&#34;127.0.0.1:8080&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Accept connections and serve them in a loop.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">conn</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">ln</span><span class="p">.</span><span class="nf">Accept</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nf">serve</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">conn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// serve reads one message from the connection, echoes it back,
</span></span></span><span class="line"><span class="cl"><span class="c1">// and closes the connection.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">serve</span><span class="p">(</span><span class="nx">conn</span> <span class="o">*</span><span class="nx">net</span><span class="p">.</span><span class="nx">TCPConn</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">defer</span> <span class="nx">conn</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">buf</span> <span class="p">[</span><span class="mi">256</span><span class="p">]</span><span class="kt">byte</span>
</span></span><span class="line"><span class="cl">    <span class="nx">n</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">conn</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="nx">buf</span><span class="p">[:])</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nx">conn</span><span class="p">.</span><span class="nf">Write</span><span class="p">(</span><span class="nx">buf</span><span class="p">[:</span><span class="nx">n</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">listening on 127.0.0.1:8080
</span></span></code></pre></div><p>If you've written a TCP server in Go, this should look familiar — <code>ListenTCP</code>, an <code>Accept</code> loop, and <code>Read</code>/<code>Write</code> on the connection. The only thing missing is a <code>go serve(conn)</code>: without goroutines, each connection is handled to completion before moving on to the next <code>Accept</code>.</p>
<h2 id="tcp-client">TCP client</h2>
<p>The client starts the connection using <code>DialTCP</code>, then uses <code>Write</code> to send a request and <code>Read</code> to get the reply:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">&#34;solod.dev/so/net&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Resolve the server address.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">raddr</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">net</span><span class="p">.</span><span class="nf">ResolveTCPAddr</span><span class="p">(</span><span class="s">&#34;tcp&#34;</span><span class="p">,</span> <span class="s">&#34;127.0.0.1:8080&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// A nil laddr lets the system choose the local address.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">conn</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">net</span><span class="p">.</span><span class="nf">DialTCP</span><span class="p">(</span><span class="s">&#34;tcp&#34;</span><span class="p">,</span> <span class="kc">nil</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">raddr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">defer</span> <span class="nx">conn</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Send a request and read the reply.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">conn</span><span class="p">.</span><span class="nf">Write</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="s">&#34;hello&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">buf</span> <span class="p">[</span><span class="mi">256</span><span class="p">]</span><span class="kt">byte</span>
</span></span><span class="line"><span class="cl">    <span class="nx">n</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">conn</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="nx">buf</span><span class="p">[:])</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nb">println</span><span class="p">(</span><span class="nb">string</span><span class="p">(</span><span class="nx">buf</span><span class="p">[:</span><span class="nx">n</span><span class="p">]))</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">hello
</span></span></code></pre></div><p>UDP and Unix domain sockets work in a similar way. For UDP, an unconnected <code>ListenUDP</code> socket uses <code>ReadFrom</code> to get data and the sender's address, and <code>WriteTo</code> to send a reply. For Unix sockets, there are <code>ListenUnix</code> (stream) and <code>ListenUnixgram</code> (datagram).</p>
<h2 id="deadlines">Deadlines</h2>
<p>By default, <code>Accept</code>, <code>Read</code>, and <code>Write</code> are blocking. In Go, you'd typically use goroutines and contexts to prevent getting stuck forever. Since that's not available in So (yet), every connection and listener supports deadlines instead:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Give the client 5 seconds to send something.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">conn</span><span class="p">.</span><span class="nf">SetReadDeadline</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">Add</span><span class="p">(</span><span class="mi">5</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">n</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">conn</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="nx">buf</span><span class="p">[:])</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">==</span> <span class="nx">net</span><span class="p">.</span><span class="nx">ErrTimeout</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// The client went quiet; drop the connection.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">return</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>SetDeadline</code>, <code>SetReadDeadline</code>, and <code>SetWriteDeadline</code> are available on <code>TCPConn</code>, <code>UDPConn</code>, <code>UnixConn</code>, and listener types. When the deadline passes, any pending call fails with <code>net.ErrTimeout</code>. If you don't set a deadline, a blocked call will wait forever. This isn't concurrency, but it's enough to keep a single-threaded server responsive.</p>
<h2 id="ip-addresses">IP addresses</h2>
<p>Along with <code>net</code>, v0.2 ports Go's <code>net/netip</code> package, which provides small, allocation-free value types for IP addresses. <code>Addr</code> represents an IP address, <code>AddrPort</code> combines an IP address with a port, and <code>Prefix</code> is an IP with a prefix length (a CIDR block):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">addr</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">netip</span><span class="p">.</span><span class="nf">ParseAddr</span><span class="p">(</span><span class="s">&#34;192.168.1.10&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nb">println</span><span class="p">(</span><span class="nx">addr</span><span class="p">.</span><span class="nf">Is4</span><span class="p">())</span>            <span class="c1">// true
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nx">ap</span> <span class="o">:=</span> <span class="nx">netip</span><span class="p">.</span><span class="nf">AddrPortFrom</span><span class="p">(</span><span class="nx">addr</span><span class="p">,</span> <span class="mi">8080</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">println</span><span class="p">(</span><span class="nx">ap</span><span class="p">.</span><span class="nf">Port</span><span class="p">())</span>             <span class="c1">// 8080
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nx">prefix</span> <span class="o">:=</span> <span class="nx">netip</span><span class="p">.</span><span class="nf">MustParsePrefix</span><span class="p">(</span><span class="s">&#34;192.168.1.0/24&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">println</span><span class="p">(</span><span class="nx">prefix</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">addr</span><span class="p">))</span> <span class="c1">// true
</span></span></span></code></pre></div><p>These are simple value types that don't use any heap allocation, which fits well with So's explicit-memory approach. The <code>net</code> package also provides <code>SplitHostPort</code> and <code>JoinHostPort</code> functions to help you work with <code>host:port</code> strings.</p>
<h2 id="new-targets">New targets</h2>
<p>Solod compiles to plain C, which (in theory) means it can target anything a C compiler can. Because of this, v0.2 adds new targets:</p>
<ul>
<li><strong>32-bit platforms</strong>. The compiler and stdlib now work correctly on 32-bit platforms, where <code>int</code> and pointers are narrower.</li>
<li><strong>WebAssembly (WASI)</strong>. You can compile a So program to <code>wasm32-wasi</code> and run it under any WASI runtime.</li>
<li><strong>Freestanding mode</strong>. So programs can run on bare-metal systems without any C standard library. No libc means no malloc, but you can use <code>mem.Arena</code> instead.</li>
</ul>
<p>Here's the complete toolchain you need to build a freestanding <code>wasm32</code> binary using <code>zig cc</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CC</span><span class="o">=</span><span class="s2">&#34;zig cc&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CFLAGS</span><span class="o">=</span><span class="s2">&#34;-Oz --target=wasm32-freestanding -nostdlib -Wl,--no-entry -Wl,--export=main&#34;</span>
</span></span><span class="line"><span class="cl">so build -o main.wasm .
</span></span></code></pre></div><p>A large part of the standard library (<code>bytes</code>, <code>strings</code>, <code>strconv</code>, <code>slices</code>, <code>maps</code>, <code>math</code>, <code>encoding/binary</code>, and more) works just fine in freestanding mode. For more details, check out the <a href="https://github.com/solod-dev/solod/blob/main/doc/freestanding.md">freestanding guide</a>.</p>
<h2 id="friendlier-interop">Friendlier interop</h2>
<p>A bunch of smaller changes make Solod nicer to write.</p>
<p><strong>Three new directives</strong> for low-level work, all documented in the <a href="https://github.com/solod-dev/solod/blob/main/doc/interop.md">interop guide</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//so:volatile
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">counter</span> <span class="kt">int</span>       <span class="c1">// emits a C volatile
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">//so:thread_local
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">perThread</span> <span class="kt">int</span>     <span class="c1">// emits C11 _Thread_local
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">//so:attr packed
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">header</span> <span class="kd">struct</span> <span class="p">{</span>  <span class="c1">// emits __attribute__((packed))
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">version</span> <span class="kt">byte</span>
</span></span><span class="line"><span class="cl">    <span class="nx">length</span>  <span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>so:attr</code> works with variables, constants, types, and functions. You can use it on multiple lines, and the attributes will stack. For example, <code>//so:attr aligned(16)</code> will combine with <code>//so:attr packed</code>.</p>
<p><strong>Type aliases</strong>. So now supports Go-style type aliases:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Byte</span> <span class="p">=</span> <span class="kt">uint8</span>
</span></span></code></pre></div><p><strong>Numeric C types</strong>. The <code>so/c</code> package now includes named types for C's numeric types — <code>Int</code>, <code>UInt</code>, <code>Long</code>, <code>Short</code>, <code>UChar</code>, <code>LongLong</code>, and others. When you declare an extern function, you can use the actual C types in its signature instead of trying to guess the correct fixed-width Go type for your platform.</p>
<p><strong>Third-party packages</strong>. You can now add external So packages using <code>go install</code> or by vendoring, and you can organize your own code into multiple modules. So doesn't have a real package ecosystem yet, but it's a good start.</p>
<p><strong>Better diagnostics</strong>. By default, panic messages report the C file and line. Pass <code>--track-source</code> to report the original So source location instead:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">so run --track-source .
</span></span></code></pre></div><p>There's also an optional <code>--check-nil</code> flag that adds nil-pointer checks when accessing struct fields and calling interface methods. This way, if there's a bad dereference, the program will panic cleanly instead of causing a segmentation fault. Both options are off by default to keep the generated code more readable.</p>
<h2 id="more-stdlib">More stdlib</h2>
<p>Beyond <code>net</code> and <code>net/netip</code>, v0.2 adds a few more packages:</p>
<ul>
<li><code>encoding/hex</code> — hex encoding and decoding, including <code>Dump</code> for hexdump-style output.</li>
<li><code>uuid</code> — generating and parsing UUIDs (v4 and v7), with random components from a cryptographically secure source.</li>
</ul>
<p>And a small but handy update to memory management: <code>mem.Arena.Free</code> now reclaims the last allocation if you give it the matching pointer. It's a minor optimization, but it means a quick alloc/free pair on an arena no longer wastes space.</p>
<p><a href="https://github.com/solod-dev/solod/blob/main/doc/stdlib.md">Stdlib documentation</a></p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>With v0.2, Solod has evolved from just &quot;command-line tools and C glue&quot; into something you can actually use on a network — like a TCP or UDP server, a small protocol client, or a Unix-socket daemon. The new targets (32-bit, WASM, freestanding) mean the same code can now run in more places, even down to bare metal.</p>
<p>The big thing that's still missing is concurrency. A server that handles requests one at a time works for some tasks, but a real network service needs to manage many connections at once. That's the obvious goal for v0.3 — adding some kind of concurrency, along with the stdlib packages that support it.</p>
<p>If you're interested, take a look at So's <a href="https://github.com/solod-dev/solod#readme">readme</a> — it has everything you need to get started. Or <a href="https://codapi.org/so">try So online</a> without installing anything.</p>
]]></content:encoded></item><item><title>Solod v0.1: Go ergonomics, practical stdlib, native C interop</title><link>https://antonz.org/solod-v0-1/</link><pubDate>Wed, 06 May 2026 11:00:00 +0000</pubDate><guid>https://antonz.org/solod-v0-1/</guid><description>A system-level language with Go syntax and zero runtime.</description><content:encoded><![CDATA[<p>Solod (<strong>So</strong>) is a system-level language with Go syntax and zero runtime. It's designed for two main audiences:</p>
<ul>
<li>Go developers who want low-level control and zero-cost C interop, without having to learn a new language or standard library.</li>
<li>C developers who like Go's style.</li>
</ul>
<p>The <a href="/solod">initial version</a> (let's call it v0) was focused on picking a subset of Go and translating it to C. The next logical step was to port Go's standard library and make it easier to interop with C. That's what the v0.1 release I'm presenting today is all about.</p>
<p><a href="#standard-library">Standard library</a> •
<a href="#sqlite-bindings">SQLite bindings</a> •
<a href="#persistent-map">Persistent map</a> •
<a href="#store-and-retrieve">Store and retrieve</a> •
<a href="#command-line-interface">Command-line interface</a> •
<a href="#performance">Performance</a> •
<a href="#wrapping-up">Wrapping up</a></p>
<h2 id="standard-library">Standard library</h2>
<p>Solod v0.1 ships with the following stdlib packages ported from Go:</p>
<ul>
<li><code>io</code>, <code>bufio</code>, and <code>fmt</code> — Abstractions and types for general-purpose I/O.</li>
<li><code>bytes</code>, <code>strings</code>, <code>strconv</code>, and <code>unicode/utf8</code> — Common byte and text operations.</li>
<li><code>slices</code> and <code>maps</code> — Generic heap-allocated data structures.</li>
<li><code>crypto/rand</code> and <code>math/rand</code> — Generating random data.</li>
<li><code>flag</code>, <code>os</code>, and <code>path</code> — Working with the command line and files.</li>
<li><code>log/slog</code> — Structured logging.</li>
<li><code>time</code> — Measuring and displaying time.</li>
</ul>
<p>And a couple of its own packages:</p>
<ul>
<li><code>mem</code> — Memory allocation with a pluggable allocator interface.</li>
<li><code>c</code> — Low-level C interop helpers.</li>
</ul>
<p><a href="https://github.com/solod-dev/solod/blob/main/doc/stdlib.md">Stdlib documentation</a></p>
<p>In the following sections, I'll demonstrate some of the v0.1 features using a simple example: a persistent key-value store backed by SQLite.</p>
<h2 id="sqlite-bindings">SQLite bindings</h2>
<p>Since So doesn't provide <code>database/sql</code> yet, we'll call SQLite directly through its C API. To do this, let's import the necessary headers with the <code>so:include</code> directive and generate extern declarations using the <a href="https://github.com/solod-dev/sobind">sobind</a> tool:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">&#34;solod.dev/so/c&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//so:include &lt;sqlite3.h&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// SQLite constants.
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">//so:extern SQLITE_OK
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">const</span> <span class="nx">sqliteOK</span> <span class="p">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"><span class="c1">//so:extern SQLITE_ROW
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">const</span> <span class="nx">sqliteRow</span> <span class="p">=</span> <span class="mi">100</span>
</span></span><span class="line"><span class="cl"><span class="c1">//so:extern SQLITE_DONE
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">const</span> <span class="nx">sqliteDone</span> <span class="p">=</span> <span class="mi">101</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// SQLite types.
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">//so:extern
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">sqlite3</span> <span class="kd">struct</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="c1">//so:extern
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">sqlite3_stmt</span> <span class="kd">struct</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="c1">//so:extern
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">sqlite3_value</span> <span class="kd">struct</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="c1">//so:extern
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">sqlite3_callback</span> <span class="kd">func</span><span class="p">(</span><span class="nx">any</span><span class="p">,</span> <span class="kt">int32</span><span class="p">,</span> <span class="o">**</span><span class="nx">c</span><span class="p">.</span><span class="nx">Char</span><span class="p">,</span> <span class="o">**</span><span class="nx">c</span><span class="p">.</span><span class="nx">Char</span><span class="p">)</span> <span class="kt">int32</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// SQLite functions.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">sqlite3_open</span><span class="p">(</span><span class="nx">filename</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">ppDb</span> <span class="o">**</span><span class="nx">sqlite3</span><span class="p">)</span> <span class="kt">int32</span>
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">sqlite3_prepare_v2</span><span class="p">(</span><span class="nx">db</span> <span class="o">*</span><span class="nx">sqlite3</span><span class="p">,</span> <span class="nx">zSql</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">nByte</span> <span class="kt">int32</span><span class="p">,</span> <span class="nx">ppStmt</span> <span class="o">**</span><span class="nx">sqlite3_stmt</span><span class="p">,</span> <span class="nx">pzTail</span> <span class="o">**</span><span class="nx">c</span><span class="p">.</span><span class="nx">ConstChar</span><span class="p">)</span> <span class="kt">int32</span>
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">sqlite3_step</span><span class="p">(</span><span class="nx">arg0</span> <span class="o">*</span><span class="nx">sqlite3_stmt</span><span class="p">)</span> <span class="kt">int32</span>
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">sqlite3_finalize</span><span class="p">(</span><span class="nx">pStmt</span> <span class="o">*</span><span class="nx">sqlite3_stmt</span><span class="p">)</span> <span class="kt">int32</span>
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">sqlite3_close</span><span class="p">(</span><span class="nx">arg0</span> <span class="o">*</span><span class="nx">sqlite3</span><span class="p">)</span> <span class="kt">int32</span>
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">sqlite3_exec</span><span class="p">(</span><span class="nx">arg0</span> <span class="o">*</span><span class="nx">sqlite3</span><span class="p">,</span> <span class="nx">sql</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">callback</span> <span class="nx">sqlite3_callback</span><span class="p">,</span> <span class="nx">arg3</span> <span class="nx">any</span><span class="p">,</span> <span class="nx">errmsg</span> <span class="o">**</span><span class="nx">c</span><span class="p">.</span><span class="nx">Char</span><span class="p">)</span> <span class="kt">int32</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// more declarations...
</span></span></span></code></pre></div><p>The <code>so:extern</code> directive is required for constants (<code>sqliteOK</code>) and types (<code>sqlite3_stmt</code>). As for functions (<code>sqlite3_prepare_v2</code>), we can just declare them without a body — the transpiler will treat them as extern declarations even without <code>so:extern</code>.</p>
<h2 id="persistent-map">Persistent map</h2>
<p>With the SQLite API in place, let's implement a key-value type that wraps the database connection:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// SQLMap is a simple key-value store backed by an SQLite database.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">SQLMap</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">db</span> <span class="o">*</span><span class="nx">sqlite3</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Add a constructor that connects to an SQLite database and creates a table to store the items:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">ErrCreate</span> <span class="p">=</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;sqlmap: create schema failed&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kd">const</span> <span class="nx">sqlCreate</span> <span class="p">=</span> <span class="s">&#34;create table if not exists kv (key text primary key, val)&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// NewSQLMap creates a new SQLMap using the provided connection string.
</span></span></span><span class="line"><span class="cl"><span class="c1">// It opens a connection to the SQLite database and creates the underlying
</span></span></span><span class="line"><span class="cl"><span class="c1">// key-value table if it does not already exist.
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// The caller is responsible for calling Close on the returned SQLMap
</span></span></span><span class="line"><span class="cl"><span class="c1">// when it is no longer needed.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">NewSQLMap</span><span class="p">(</span><span class="nx">connStr</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="nx">SQLMap</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">db</span> <span class="o">*</span><span class="nx">sqlite3</span>
</span></span><span class="line"><span class="cl">    <span class="nx">rc</span> <span class="o">:=</span> <span class="nf">sqlite3_open</span><span class="p">(</span><span class="nx">connStr</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">db</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">rc</span> <span class="o">!=</span> <span class="nx">sqliteOK</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">SQLMap</span><span class="p">{},</span> <span class="nx">ErrCreate</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">rc</span> <span class="p">=</span> <span class="nf">sqlite3_exec</span><span class="p">(</span><span class="nx">db</span><span class="p">,</span> <span class="nx">sqlCreate</span><span class="p">,</span> <span class="kc">nil</span><span class="p">,</span> <span class="kc">nil</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">rc</span> <span class="o">!=</span> <span class="nx">sqliteOK</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">sqlite3_close</span><span class="p">(</span><span class="nx">db</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">SQLMap</span><span class="p">{},</span> <span class="nx">ErrCreate</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">SQLMap</span><span class="p">{</span><span class="nx">db</span><span class="p">},</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Close releases resources associated with the SQLMap.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">SQLMap</span><span class="p">)</span> <span class="nf">Close</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">sqlite3_close</span><span class="p">(</span><span class="nx">m</span><span class="p">.</span><span class="nx">db</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>As you can see, this So code looks a lot like regular Go code. However, there are some key differences:</p>
<ul>
<li>When compiled, the code is first translated to plain C, then compiled into a native binary using GCC or Clang.</li>
<li>Unlike Go, there is no runtime (no automatic heap memory allocation, no garbage collection, no goroutine scheduler).</li>
<li>There is no overhead when calling C functions, unlike Go's Cgo.</li>
<li>The interop syntax is a bit cleaner. For example, Go's <code>string</code> (<code>sqlCreate</code> in the <code>sqlite3_exec</code> call) automatically decays to C's <code>const char*</code>.</li>
</ul>
<h2 id="store-and-retrieve">Store and retrieve</h2>
<p>First, let's implement the <code>Set</code> method:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nx">ErrPrepare</span> <span class="p">=</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;sqlmap: prepare failed&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">ErrExec</span>    <span class="p">=</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;sqlmap: exec failed&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">const</span> <span class="nx">sqlSet</span> <span class="p">=</span> <span class="s">&#34;insert or replace into kv (key, val) values (?, ?)&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Set stores a string value for the specified key.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">SQLMap</span><span class="p">)</span> <span class="nf">Set</span><span class="p">(</span><span class="nx">key</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">val</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">stmt</span> <span class="o">*</span><span class="nx">sqlite3_stmt</span>
</span></span><span class="line"><span class="cl">    <span class="nx">rc</span> <span class="o">:=</span> <span class="nf">sqlite3_prepare_v2</span><span class="p">(</span><span class="nx">m</span><span class="p">.</span><span class="nx">db</span><span class="p">,</span> <span class="nx">sqlSet</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">stmt</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">rc</span> <span class="o">!=</span> <span class="nx">sqliteOK</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">ErrPrepare</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">defer</span> <span class="nf">sqlite3_finalize</span><span class="p">(</span><span class="nx">stmt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">sqlite3_bind_text</span><span class="p">(</span><span class="nx">stmt</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">key</span><span class="p">,</span> <span class="nb">int32</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="nx">key</span><span class="p">)),</span> <span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nf">sqlite3_bind_text</span><span class="p">(</span><span class="nx">stmt</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="nx">val</span><span class="p">,</span> <span class="nb">int32</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="nx">val</span><span class="p">)),</span> <span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">rc</span> <span class="p">=</span> <span class="nf">sqlite3_step</span><span class="p">(</span><span class="nx">stmt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">rc</span> <span class="o">!=</span> <span class="nx">sqliteDone</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">ErrExec</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>No surprises here, just a bunch of SQLite API calls.</p>
<p>The <code>Get</code> method is more interesting:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">ErrNotFound</span> <span class="p">=</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;sqlmap: not found&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kd">const</span> <span class="nx">sqlGet</span> <span class="p">=</span> <span class="s">&#34;select val from kv where key = ?&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Get returns the value associated with the specified key.
</span></span></span><span class="line"><span class="cl"><span class="c1">// The caller owns the returned string and must free it with mem.FreeString.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">SQLMap</span><span class="p">)</span> <span class="nf">Get</span><span class="p">(</span><span class="nx">a</span> <span class="nx">mem</span><span class="p">.</span><span class="nx">Allocator</span><span class="p">,</span> <span class="nx">key</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="kt">string</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">stmt</span> <span class="o">*</span><span class="nx">sqlite3_stmt</span>
</span></span><span class="line"><span class="cl">    <span class="nx">rc</span> <span class="o">:=</span> <span class="nf">sqlite3_prepare_v2</span><span class="p">(</span><span class="nx">m</span><span class="p">.</span><span class="nx">db</span><span class="p">,</span> <span class="nx">sqlGet</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">stmt</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">rc</span> <span class="o">!=</span> <span class="nx">sqliteOK</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s">&#34;&#34;</span><span class="p">,</span> <span class="nx">ErrPrepare</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">defer</span> <span class="nf">sqlite3_finalize</span><span class="p">(</span><span class="nx">stmt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">sqlite3_bind_text</span><span class="p">(</span><span class="nx">stmt</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">key</span><span class="p">,</span> <span class="nb">int32</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="nx">key</span><span class="p">)),</span> <span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">rc</span> <span class="p">=</span> <span class="nf">sqlite3_step</span><span class="p">(</span><span class="nx">stmt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">rc</span> <span class="o">==</span> <span class="nx">sqliteDone</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s">&#34;&#34;</span><span class="p">,</span> <span class="nx">ErrNotFound</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">rc</span> <span class="o">!=</span> <span class="nx">sqliteRow</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s">&#34;&#34;</span><span class="p">,</span> <span class="nx">ErrExec</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">text</span> <span class="o">:=</span> <span class="nf">sqlite3_column_text</span><span class="p">(</span><span class="nx">stmt</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">tmp</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="nx">text</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">result</span> <span class="o">:=</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Clone</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">tmp</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">result</span><span class="p">,</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The pointer returned by <code>sqlite3_column_text</code> is managed by SQLite. It becomes invalid after calling <code>sqlite3_finalize</code> (which <code>Get</code> does before returning). Because of this, we need to allocate a copy of the returned value, using <code>strings.Clone</code> in this case.</p>
<p>So's approach to memory allocation is similar to Zig's — all heap allocations must be done explicitly by providing a specific instance of the <code>mem.Allocator</code> interface.</p>
<p>The caller, of course, must free the allocated string:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">m</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">NewSQLMap</span><span class="p">(</span><span class="s">&#34;:memory:&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">defer</span> <span class="nx">m</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">m</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;name&#34;</span><span class="p">,</span> <span class="s">&#34;Alice&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">m</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="nx">mem</span><span class="p">.</span><span class="nx">System</span><span class="p">,</span> <span class="s">&#34;name&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nb">println</span><span class="p">(</span><span class="s">&#34;name =&#34;</span><span class="p">,</span> <span class="nx">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">mem</span><span class="p">.</span><span class="nf">FreeString</span><span class="p">(</span><span class="nx">mem</span><span class="p">.</span><span class="nx">System</span><span class="p">,</span> <span class="nx">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">name = Alice
</span></span></code></pre></div><p>Here, <code>mem.System</code> is a specific allocator that uses libc's <code>malloc</code> and <code>free</code>. Alternatively, we could use <code>mem.Arena</code> or any other implementation of the <code>mem.Allocator</code> interface:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">buf</span> <span class="p">[</span><span class="mi">1024</span><span class="p">]</span><span class="kt">byte</span> <span class="c1">// stack-allocated
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">arena</span> <span class="o">:=</span> <span class="nx">mem</span><span class="p">.</span><span class="nf">NewArena</span><span class="p">(</span><span class="nx">buf</span><span class="p">[:])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">name</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">m</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">arena</span><span class="p">,</span> <span class="s">&#34;name&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">mem</span><span class="p">.</span><span class="nf">FreeString</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">arena</span><span class="p">,</span> <span class="nx">name</span><span class="p">)</span> <span class="c1">// no-op for arena; can be omitted
</span></span></span></code></pre></div><h2 id="command-line-interface">Command-line interface</h2>
<p>With the <code>SQLMap</code> type in place, let's create a simple CLI using the <code>flag</code> package:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nx">opFlag</span>  <span class="kt">string</span>
</span></span><span class="line"><span class="cl">    <span class="nx">keyFlag</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl">    <span class="nx">valFlag</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">parseFlags</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">flag</span><span class="p">.</span><span class="nf">StringVar</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">opFlag</span><span class="p">,</span> <span class="s">&#34;op&#34;</span><span class="p">,</span> <span class="s">&#34;&#34;</span><span class="p">,</span> <span class="s">&#34;operation: get, set, or del&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">flag</span><span class="p">.</span><span class="nf">StringVar</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">keyFlag</span><span class="p">,</span> <span class="s">&#34;key&#34;</span><span class="p">,</span> <span class="s">&#34;&#34;</span><span class="p">,</span> <span class="s">&#34;key name&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">flag</span><span class="p">.</span><span class="nf">StringVar</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">valFlag</span><span class="p">,</span> <span class="s">&#34;val&#34;</span><span class="p">,</span> <span class="s">&#34;&#34;</span><span class="p">,</span> <span class="s">&#34;value (for set operation)&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">flag</span><span class="p">.</span><span class="nf">Parse</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">parseFlags</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>Then add command routing:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">m</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">NewSQLMap</span><span class="p">(</span><span class="s">&#34;sqlmap.db&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">check</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">defer</span> <span class="nx">m</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">switch</span> <span class="nx">opFlag</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="s">&#34;set&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nx">err</span> <span class="p">=</span> <span class="nx">m</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="nx">keyFlag</span><span class="p">,</span> <span class="nx">valFlag</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nf">check</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="s">&#34;get&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nx">val</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">m</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="nx">mem</span><span class="p">.</span><span class="nx">System</span><span class="p">,</span> <span class="nx">keyFlag</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nf">check</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">println</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">mem</span><span class="p">.</span><span class="nf">FreeString</span><span class="p">(</span><span class="nx">mem</span><span class="p">.</span><span class="nx">System</span><span class="p">,</span> <span class="nx">val</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="s">&#34;del&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nx">err</span> <span class="p">=</span> <span class="nx">m</span><span class="p">.</span><span class="nf">Delete</span><span class="p">(</span><span class="nx">keyFlag</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nf">check</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">default</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nx">flag</span><span class="p">.</span><span class="nf">Usage</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="nx">os</span><span class="p">.</span><span class="nf">Exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">sqlmap -op=set -key=name -val=alice
</span></span><span class="line"><span class="cl">sqlmap -op=get -key=name
</span></span><span class="line"><span class="cl">alice
</span></span></code></pre></div><p>Again, no surprises here — the <code>flag</code> package works just as it does in Go.</p>
<h2 id="performance">Performance</h2>
<p>Solod isn't trying to outperform hand-tuned C. Still, performance matters: the code is benchmarked and optimized to run reasonably fast. Since So compiles to plain C and then to native code with full optimizations, the results are sometimes better than Go's.</p>
<p>Here are some highlights from the benchmarks:</p>
<ul>
<li>Buffered I/O is 3x faster than Go.</li>
<li>String and byte operations are up to 2.5x faster.</li>
<li>Maps are 1.5x faster for modifications.</li>
<li>Integer formatting is 2x faster.</li>
</ul>
<p>There're no GC pauses and no Cgo bridge cost when calling C libraries. The tradeoff is that you have to handle memory yourself, but as the SQLite example above shows, So's allocator interface makes that pretty manageable.</p>
<p><a href="https://github.com/solod-dev/solod/blob/main/bench/README.md">Solod vs. Go benchmarks</a></p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Solod is still in its early days, but with the v0.1 release, it's ready for hobby projects. The already-ported parts of the Go standard library make it easy to write command-line tools (check out the <code>cat</code>, <code>head</code>, <code>sort</code>, and <code>wc</code> <a href="https://github.com/solod-dev/example">examples</a>). Plus, with native C interop, you can build just about anything else you need.</p>
<p>The next release (v0.2) will likely focus on networking, concurrency, or both — along with more stdlib packages.</p>
<p>If you're interested, take a look at So's <a href="https://github.com/solod-dev/solod#readme">readme</a> — it has all the information you need to get started. Or <a href="https://codapi.org/so">try So online</a> without installing anything.</p>
]]></content:encoded></item><item><title>Porting Go's strings package to C</title><link>https://antonz.org/porting-go-strings/</link><pubDate>Fri, 03 Apr 2026 13:00:00 +0000</pubDate><guid>https://antonz.org/porting-go-strings/</guid><description>With allocators, benchmarks, and some optimizations.</description><content:encoded><![CDATA[<p>Creating a subset of Go that <a href="/solod/">translates to C</a> was never my end goal. I liked writing C code with Go, but without the standard library it felt pretty limited. So, the next logical step was to port Go's stdlib to C.</p>
<p>Of course, this isn't something I could do all at once. I started with the <a href="/porting-go-io/">io package</a>, which provides core abstractions like <code>Reader</code> and <code>Writer</code>, as well as general-purpose functions like <code>Copy</code>. But <code>io</code> isn't very interesting on its own, since it doesn't include specific reader or writer implementations. So my next choices were naturally <code>bytes</code> and <code>strings</code> — the workhorses of almost every Go program. This post is about how the porting process went.</p>
<p><a href="#bits-and-utf-8">Bits and UTF-8</a> •
<a href="#bytes">Bytes</a> •
<a href="#allocators">Allocators</a> •
<a href="#buffers-and-builders">Buffers and builders</a> •
<a href="#benchmarks">Benchmarks</a> •
<a href="#optimizing-search">Optimizing search</a> •
<a href="#optimizing-builder">Optimizing builder</a> •
<a href="#wrapping-up">Wrapping up</a></p>
<h2 id="bits-and-utf-8">Bits and UTF-8</h2>
<p>Before I could start porting <code>bytes</code>, I had to deal with its dependencies first:</p>
<ul>
<li><code>math/bits</code> implements bit counting and manipulation functions.</li>
<li><code>unicode/utf8</code> implements functions for UTF-8 encoded text.</li>
</ul>
<p>Both of these packages are made up of pure functions, so they were pretty easy to port. The only minor challenge was the difference in operator precedence between Go and C — specifically, bit shifts (<code>&lt;&lt;</code>, <code>&gt;&gt;</code>). In Go, bit shifts have higher precedence than addition and subtraction. In C, they have lower precedence:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Go: shift has HIGHER precedence than +
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">x</span> <span class="kt">uint32</span> <span class="p">=</span> <span class="mi">1</span><span class="o">&lt;&lt;</span><span class="mi">2</span> <span class="o">+</span> <span class="mi">3</span>  <span class="c1">// (1 &lt;&lt; 2) + 3 == 7
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// C: shift has LOWER precedence than +
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">uint32_t</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">2</span> <span class="o">+</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 1 &lt;&lt; (2 + 3) == 32
</span></span></span></code></pre></div><p>The simplest solution was to just use parentheses everywhere shifts are involved:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Go: Mul64 returns the 128-bit product of x and y: (hi, lo) = x * y
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Mul64</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span> <span class="kt">uint64</span><span class="p">)</span> <span class="p">(</span><span class="nx">hi</span><span class="p">,</span> <span class="nx">lo</span> <span class="kt">uint64</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">const</span> <span class="nx">mask32</span> <span class="p">=</span> <span class="mi">1</span><span class="o">&lt;&lt;</span><span class="mi">32</span> <span class="o">-</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">    <span class="nx">x0</span> <span class="o">:=</span> <span class="nx">x</span> <span class="o">&amp;</span> <span class="nx">mask32</span>
</span></span><span class="line"><span class="cl">    <span class="nx">x1</span> <span class="o">:=</span> <span class="nx">x</span> <span class="o">&gt;&gt;</span> <span class="mi">32</span>
</span></span><span class="line"><span class="cl">    <span class="nx">y0</span> <span class="o">:=</span> <span class="nx">y</span> <span class="o">&amp;</span> <span class="nx">mask32</span>
</span></span><span class="line"><span class="cl">    <span class="nx">y1</span> <span class="o">:=</span> <span class="nx">y</span> <span class="o">&gt;&gt;</span> <span class="mi">32</span>
</span></span><span class="line"><span class="cl">    <span class="nx">w0</span> <span class="o">:=</span> <span class="nx">x0</span> <span class="o">*</span> <span class="nx">y0</span>
</span></span><span class="line"><span class="cl">    <span class="nx">t</span> <span class="o">:=</span> <span class="nx">x1</span><span class="o">*</span><span class="nx">y0</span> <span class="o">+</span> <span class="nx">w0</span><span class="o">&gt;&gt;</span><span class="mi">32</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// C: Mul64 returns the 128-bit product of x and y: (hi, lo) = x * y
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">so_Result</span> <span class="nf">bits_Mul64</span><span class="p">(</span><span class="kt">uint64_t</span> <span class="n">x</span><span class="p">,</span> <span class="kt">uint64_t</span> <span class="n">y</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="n">so_int</span> <span class="n">mask32</span> <span class="o">=</span> <span class="p">((</span><span class="n">so_int</span><span class="p">)</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">32</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint64_t</span> <span class="n">x0</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span> <span class="o">&amp;</span> <span class="n">mask32</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint64_t</span> <span class="n">x1</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span> <span class="o">&gt;&gt;</span> <span class="mi">32</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint64_t</span> <span class="n">y0</span> <span class="o">=</span> <span class="p">(</span><span class="n">y</span> <span class="o">&amp;</span> <span class="n">mask32</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint64_t</span> <span class="n">y1</span> <span class="o">=</span> <span class="p">(</span><span class="n">y</span> <span class="o">&gt;&gt;</span> <span class="mi">32</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint64_t</span> <span class="n">w0</span> <span class="o">=</span> <span class="n">x0</span> <span class="o">*</span> <span class="n">y0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint64_t</span> <span class="n">t</span> <span class="o">=</span> <span class="n">x1</span> <span class="o">*</span> <span class="n">y0</span> <span class="o">+</span> <span class="p">(</span><span class="n">w0</span> <span class="o">&gt;&gt;</span> <span class="mi">32</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>With <code>bits</code> and <code>utf8</code> done, I moved on to <code>bytes</code>.</p>
<h2 id="bytes">Bytes</h2>
<p>The <code>bytes</code> package provides functions for working with byte slices:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Count counts the number of non-overlapping instances of sep in s.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Count</span><span class="p">(</span><span class="nx">s</span><span class="p">,</span> <span class="nx">sep</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Equal reports whether a and b are the
</span></span></span><span class="line"><span class="cl"><span class="c1">// same length and contain the same bytes.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Equal</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="kt">bool</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Index returns the index of the first instance
</span></span></span><span class="line"><span class="cl"><span class="c1">// of sep in s, or -1 if sep is not present in s.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Index</span><span class="p">(</span><span class="nx">s</span><span class="p">,</span> <span class="nx">sep</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Repeat returns a new byte slice consisting of count copies of b.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Repeat</span><span class="p">(</span><span class="nx">b</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="nx">count</span> <span class="kt">int</span><span class="p">)</span> <span class="p">[]</span><span class="kt">byte</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// and others
</span></span></span></code></pre></div><p>Some of them were easy to port, like <code>Equal</code>. Here's how it looks in Go:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Equal reports whether a and b are the
</span></span></span><span class="line"><span class="cl"><span class="c1">// same length and contain the same bytes.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Equal</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Neither cmd/compile nor gccgo allocates for these string conversions.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">return</span> <span class="nb">string</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="o">==</span> <span class="nb">string</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>And here's the C version:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// bytes_string reinterprets a byte slice as a string (zero-copy).
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define so_bytes_string(bs) ({                  \
</span></span></span><span class="line"><span class="cl"><span class="cp">    so_Slice _bs = (bs);                        \
</span></span></span><span class="line"><span class="cl"><span class="cp">    (so_String){(const char*)_bs.ptr, _bs.len}; \
</span></span></span><span class="line"><span class="cl"><span class="cp">})
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// string_eq returns true if two strings are equal.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kr">inline</span> <span class="kt">bool</span> <span class="nf">so_string_eq</span><span class="p">(</span><span class="n">so_String</span> <span class="n">s1</span><span class="p">,</span> <span class="n">so_String</span> <span class="n">s2</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">s1</span><span class="p">.</span><span class="n">len</span> <span class="o">==</span> <span class="n">s2</span><span class="p">.</span><span class="n">len</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="n">s1</span><span class="p">.</span><span class="n">len</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">||</span> <span class="nf">memcmp</span><span class="p">(</span><span class="n">s1</span><span class="p">.</span><span class="n">ptr</span><span class="p">,</span> <span class="n">s2</span><span class="p">.</span><span class="n">ptr</span><span class="p">,</span> <span class="n">s1</span><span class="p">.</span><span class="n">len</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Equal reports whether a and b are the
</span></span></span><span class="line"><span class="cl"><span class="c1">// same length and contain the same bytes.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">bool</span> <span class="nf">bytes_Equal</span><span class="p">(</span><span class="n">so_Slice</span> <span class="n">a</span><span class="p">,</span> <span class="n">so_Slice</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nf">so_string_eq</span><span class="p">(</span><span class="nf">so_bytes_string</span><span class="p">(</span><span class="n">a</span><span class="p">),</span> <span class="nf">so_bytes_string</span><span class="p">(</span><span class="n">b</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Just like in Go, the <code>so_bytes_string</code> (<code>[]byte</code> → <code>string</code>) macro doesn't allocate memory; it just reinterprets the byte slice's underlying storage as a string. The <code>so_string_eq</code> function (which works like <code>==</code> in Go) is easy to implement using <code>memcmp</code> from the libc API.</p>
<p>Another example is the <code>IndexByte</code> function, which looks for a specific byte in a slice. Here's the pure-Go implementation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// IndexByte returns the index of the first instance
</span></span></span><span class="line"><span class="cl"><span class="c1">// of c in b, or -1 if c is not present in b.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">IndexByte</span><span class="p">(</span><span class="nx">b</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="nx">c</span> <span class="kt">byte</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">x</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">b</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nx">x</span> <span class="o">==</span> <span class="nx">c</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="nx">i</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="o">-</span><span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>And here's the C version:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// IndexByte returns the index of the first instance
</span></span></span><span class="line"><span class="cl"><span class="c1">// of c in b, or -1 if c is not present in b.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">so_int</span> <span class="nf">bytes_IndexByte</span><span class="p">(</span><span class="n">so_Slice</span> <span class="n">b</span><span class="p">,</span> <span class="n">so_byte</span> <span class="n">c</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">so_int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="nf">so_len</span><span class="p">(</span><span class="n">b</span><span class="p">);</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">so_byte</span> <span class="n">x</span> <span class="o">=</span> <span class="nf">so_at</span><span class="p">(</span><span class="n">so_byte</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">i</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">==</span> <span class="n">c</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>I used a regular C <code>for</code> loop to mimic Go's <code>for-range</code>:</p>
<ul>
<li>Loop over the slice indexes with <code>for</code> (<code>so_len</code> is a macro that returns <code>b.len</code>, similar to Go's <code>len</code> built-in).</li>
<li>Access the i-th byte with <code>so_at</code> (a bounds-checking macro that returns <code>*((so_byte*)b.ptr + i)</code>).</li>
</ul>
<p>But <code>Equal</code> and <code>IndexByte</code> don't allocate memory. What should I do with <code>Repeat</code>, since it clearly does? I had a decision to make.</p>
<h2 id="allocators">Allocators</h2>
<p>The Go runtime handles memory allocation and deallocation automatically. In C, I had a few options:</p>
<ul>
<li>Use a reliable garbage collector like Boehm GC to closely match Go's behavior.</li>
<li>Allocate memory with libc's <code>malloc</code> and have the caller free it later with <code>free</code>.</li>
<li>Introduce allocators.</li>
</ul>
<blockquote>
<p>An <em>allocator</em> is a tool that reserves memory (typically on the heap) so a program can store its data structures there. See <a href="/allocators/">Allocators from C to Zig</a> if you want to learn more about them.</p>
</blockquote>
<p>For me, the winner was clear. Modern systems programming languages like Zig and Odin clearly showed the value of allocators:</p>
<ul>
<li>It's obvious whether a function allocates memory or not: if it has an allocator as a parameter, it allocates.</li>
<li>It's easy to use different allocation methods: you can use <code>malloc</code> for one function, an arena for another, and a stack allocator for a third.</li>
<li>It helps with testing and debugging: you can use a tracking allocator to find memory leaks, or a failing allocator to test error handling.</li>
</ul>
<p>An <code>Allocator</code> is an interface with three methods: <code>Alloc</code>, <code>Realloc</code>, and <code>Free</code>. In C, it translates to a struct with function pointers:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Allocator defines the interface for memory allocators.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_Result</span> <span class="p">(</span><span class="o">*</span><span class="n">Alloc</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">size</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">align</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_Result</span> <span class="p">(</span><span class="o">*</span><span class="n">Realloc</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">ptr</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">so_int</span> <span class="n">oldSize</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">newSize</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">align</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">Free</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">ptr</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">size</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">align</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">mem_Allocator</span><span class="p">;</span>
</span></span></code></pre></div><blockquote>
<p>As I mentioned in the post about <a href="/porting-go-io/">porting the io package</a>, this interface representation isn't as efficient as using a static method table, but it's simpler. If you're interested in other options, check out the post on <a href="/interfaces-in-c/">interfaces</a>.</p>
</blockquote>
<p>By convention, if a function allocates memory, it takes an allocator as its first parameter. So Go's <code>Repeat</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Repeat returns a new byte slice consisting of count copies of b.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Repeat</span><span class="p">(</span><span class="nx">b</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="nx">count</span> <span class="kt">int</span><span class="p">)</span> <span class="p">[]</span><span class="kt">byte</span>
</span></span></code></pre></div><p>Translates to this C code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Repeat returns a new byte slice consisting of count copies of b.
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// If the allocator is nil, uses the system allocator.
</span></span></span><span class="line"><span class="cl"><span class="c1">// The returned slice is allocated; the caller owns it.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">so_Slice</span> <span class="nf">bytes_Repeat</span><span class="p">(</span><span class="n">mem_Allocator</span> <span class="n">a</span><span class="p">,</span> <span class="n">so_Slice</span> <span class="n">b</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">count</span><span class="p">)</span>
</span></span></code></pre></div><p>If the caller doesn't care about using a specific allocator, they can just pass an empty allocator, and the implementation will use the system allocator — <code>calloc</code>, <code>realloc</code>, and <code>free</code> from libc.</p>
<p>Here's a simplified version of the system allocator (I removed safety checks to make it easier to read):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// SystemAllocator uses the system&#39;s malloc, realloc, and free functions.
</span></span></span><span class="line"><span class="cl"><span class="c1">// It zeros out new memory on allocation and reallocation.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{}</span> <span class="n">mem_SystemAllocator</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">so_Result</span> <span class="nf">mem_SystemAllocator_Alloc</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">size</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">align</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">ptr</span> <span class="o">=</span> <span class="nf">calloc</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="p">(</span><span class="kt">size_t</span><span class="p">)(</span><span class="n">size</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">ptr</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">(</span><span class="n">so_Result</span><span class="p">){.</span><span class="n">val</span><span class="p">.</span><span class="n">as_ptr</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">,</span> <span class="p">.</span><span class="n">err</span> <span class="o">=</span> <span class="n">mem_ErrOutOfMemory</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">so_Result</span><span class="p">){</span> <span class="p">.</span><span class="n">val</span><span class="p">.</span><span class="n">as_ptr</span> <span class="o">=</span> <span class="n">ptr</span><span class="p">,</span> <span class="p">.</span><span class="n">err</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">so_Result</span> <span class="nf">mem_SystemAllocator_Realloc</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">ptr</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">oldSize</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">newSize</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">align</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">newPtr</span> <span class="o">=</span> <span class="nf">realloc</span><span class="p">(</span><span class="n">ptr</span><span class="p">,</span> <span class="p">(</span><span class="kt">size_t</span><span class="p">)(</span><span class="n">newSize</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">newPtr</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">(</span><span class="n">so_Result</span><span class="p">){.</span><span class="n">val</span><span class="p">.</span><span class="n">as_ptr</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">,</span> <span class="p">.</span><span class="n">err</span> <span class="o">=</span> <span class="n">mem_ErrOutOfMemory</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">newSize</span> <span class="o">&gt;</span> <span class="n">oldSize</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Zero new memory beyond the old size.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">memset</span><span class="p">((</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">newPtr</span> <span class="o">+</span> <span class="n">oldSize</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="p">(</span><span class="kt">size_t</span><span class="p">)(</span><span class="n">newSize</span> <span class="o">-</span> <span class="n">oldSize</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">so_Result</span><span class="p">){.</span><span class="n">val</span><span class="p">.</span><span class="n">as_ptr</span> <span class="o">=</span> <span class="n">newPtr</span><span class="p">,</span> <span class="p">.</span><span class="n">err</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">mem_SystemAllocator_Free</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">ptr</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">size</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">align</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">free</span><span class="p">(</span><span class="n">ptr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The system allocator is stateless, so it's safe to have a global instance:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// System is an instance of a memory allocator that uses
</span></span></span><span class="line"><span class="cl"><span class="c1">// the system&#39;s malloc, realloc, and free functions.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">mem_Allocator</span> <span class="n">mem_System</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">self</span> <span class="o">=</span> <span class="o">&amp;</span><span class="p">(</span><span class="n">mem_SystemAllocator</span><span class="p">){},</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">Alloc</span> <span class="o">=</span> <span class="n">mem_SystemAllocator_Alloc</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">Free</span> <span class="o">=</span> <span class="n">mem_SystemAllocator_Free</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">Realloc</span> <span class="o">=</span> <span class="n">mem_SystemAllocator_Realloc</span><span class="p">};</span>
</span></span></code></pre></div><p>Here's an example of how to call <code>Repeat</code> with an allocator:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">so_Slice</span> <span class="n">src</span> <span class="o">=</span> <span class="nf">so_string_bytes</span><span class="p">(</span><span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;abc&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">so_Slice</span> <span class="n">got</span> <span class="o">=</span> <span class="nf">bytes_Repeat</span><span class="p">(</span><span class="n">mem_System</span><span class="p">,</span> <span class="n">src</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">so_String</span> <span class="n">gotStr</span> <span class="o">=</span> <span class="nf">so_bytes_string</span><span class="p">(</span><span class="n">got</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nf">so_string_ne</span><span class="p">(</span><span class="n">gotStr</span><span class="p">,</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;abcabcabc&#34;</span><span class="p">)))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_panic</span><span class="p">(</span><span class="s">&#34;want Repeat(abc) == abcabcabc&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nf">mem_FreeSlice</span><span class="p">(</span><span class="n">so_byte</span><span class="p">,</span> <span class="n">mem_System</span><span class="p">,</span> <span class="n">got</span><span class="p">);</span>
</span></span></code></pre></div><p>Way better than hidden allocations!</p>
<h2 id="buffers-and-builders">Buffers and builders</h2>
<p>Besides pure functions, <code>bytes</code> and <code>strings</code> also provide types like <code>bytes.Buffer</code>, <code>bytes.Reader</code>, and <code>strings.Builder</code>. I ported them using the same approach as with functions.</p>
<p>For types that allocate memory, like <code>Buffer</code>, the allocator becomes a struct field:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// A Buffer is a variable-sized buffer of bytes
</span></span></span><span class="line"><span class="cl"><span class="c1">// with Read and Write methods.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">mem_Allocator</span> <span class="n">a</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_Slice</span> <span class="n">buf</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">off</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">bytes_Buffer</span><span class="p">;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Usage example.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">bytes_Buffer</span> <span class="n">buf</span> <span class="o">=</span> <span class="nf">bytes_NewBuffer</span><span class="p">(</span><span class="n">mem_System</span><span class="p">,</span> <span class="p">(</span><span class="n">so_Slice</span><span class="p">){</span><span class="mi">0</span><span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="nf">bytes_Buffer_WriteString</span><span class="p">(</span><span class="o">&amp;</span><span class="n">buf</span><span class="p">,</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;hello&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="nf">bytes_Buffer_WriteString</span><span class="p">(</span><span class="o">&amp;</span><span class="n">buf</span><span class="p">,</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34; world&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">so_String</span> <span class="n">str</span> <span class="o">=</span> <span class="nf">bytes_Buffer_String</span><span class="p">(</span><span class="o">&amp;</span><span class="n">buf</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nf">so_string_ne</span><span class="p">(</span><span class="n">str</span><span class="p">,</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;hello world&#34;</span><span class="p">)))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_panic</span><span class="p">(</span><span class="s">&#34;Buffer.WriteString failed&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nf">bytes_Buffer_Free</span><span class="p">(</span><span class="o">&amp;</span><span class="n">buf</span><span class="p">);</span>
</span></span></code></pre></div><blockquote>
<p>The code is pretty wordy — most C developers would dislike using <code>bytes_Buffer_WriteString</code> instead of something shorter like <code>buf_writestr</code>. My solution to this problem is to automatically translate Go code to C (which is actually what I do when porting Go's stdlib). If you're interested, check out the post about this approach — <a href="/solod/">Solod: Go can be a better C</a>.</p>
</blockquote>
<p>Types that don't allocate, like <code>bytes.Reader</code>, need no special treatment — they translate directly to C structs without an allocator field.</p>
<p>The <code>strings</code> package is the twin of <code>bytes</code>, so porting it was uneventful. Here's <code>strings.Builder</code> usage example in Go and C side by side:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// go
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">sb</span> <span class="nx">strings</span><span class="p">.</span><span class="nx">Builder</span>
</span></span><span class="line"><span class="cl"><span class="nx">sb</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="s">&#34;Hello&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">sb</span><span class="p">.</span><span class="nf">WriteByte</span><span class="p">(</span><span class="sc">&#39;,&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">sb</span><span class="p">.</span><span class="nf">WriteRune</span><span class="p">(</span><span class="sc">&#39; &#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">sb</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="s">&#34;world&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">s</span> <span class="o">:=</span> <span class="nx">sb</span><span class="p">.</span><span class="nf">String</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">s</span> <span class="o">!=</span> <span class="s">&#34;Hello, world&#34;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">panic</span><span class="p">(</span><span class="s">&#34;want sb.String() == &#39;Hello, world&#39;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">strings_Builder</span> <span class="n">sb</span> <span class="o">=</span> <span class="p">{.</span><span class="n">a</span> <span class="o">=</span> <span class="n">mem_System</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="nf">strings_Builder_WriteString</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sb</span><span class="p">,</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;Hello&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="nf">strings_Builder_WriteByte</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sb</span><span class="p">,</span> <span class="sc">&#39;,&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">strings_Builder_WriteRune</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sb</span><span class="p">,</span> <span class="n">U</span><span class="sc">&#39; &#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">strings_Builder_WriteString</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sb</span><span class="p">,</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;world&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">so_String</span> <span class="n">s</span> <span class="o">=</span> <span class="nf">strings_Builder_String</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sb</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nf">so_string_ne</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;Hello, world&#34;</span><span class="p">)))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_panic</span><span class="p">(</span><span class="s">&#34;want sb.String() == &#39;Hello, world&#39;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nf">strings_Builder_Free</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sb</span><span class="p">);</span>
</span></span></code></pre></div><p>Again, the C code is just a more verbose version of Go's implementation, plus explicit memory allocation.</p>
<h2 id="benchmarks">Benchmarks</h2>
<p>What's the point of writing C code if it's slow, right? I decided it was time to benchmark the ported C types and functions against their Go versions.</p>
<p>To do that, I ported the benchmarking part of Go's <code>testing</code> package. Surprisingly, the simplified version was only 300 lines long and included everything I needed:</p>
<ul>
<li>Figuring out how many iterations to run.</li>
<li>Running the benchmark function in a loop.</li>
<li>Recording metrics (ns/op, MB/s, B/op, allocs/op).</li>
<li>Reporting the results.</li>
</ul>
<p>Here's a sample benchmark for the <code>strings.Builder</code> type:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="n">so_String</span> <span class="n">someStr</span> <span class="o">=</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;some string sdljlk jsklj3lkjlk djlkjw&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="n">so_int</span> <span class="n">numWrite</span> <span class="o">=</span> <span class="mi">16</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">volatile</span> <span class="n">so_String</span> <span class="n">sink</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">main_WriteString_AutoGrow</span><span class="p">(</span><span class="n">testing_B</span><span class="o">*</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">mem_Allocator</span> <span class="n">a</span> <span class="o">=</span> <span class="nf">testing_B_Allocator</span><span class="p">(</span><span class="n">b</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;</span> <span class="nf">testing_B_Loop</span><span class="p">(</span><span class="n">b</span><span class="p">);)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">strings_Builder</span> <span class="n">sb</span> <span class="o">=</span> <span class="nf">strings_NewBuilder</span><span class="p">(</span><span class="n">a</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="p">(</span><span class="n">so_int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">numWrite</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">strings_Builder_WriteString</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sb</span><span class="p">,</span> <span class="n">someStr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="n">sink</span> <span class="o">=</span> <span class="nf">strings_Builder_String</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sb</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">strings_Builder_Free</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sb</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// more benchmarks...
</span></span></span></code></pre></div><p>Reads almost like Go's benchmarks.</p>
<p>To monitor memory usage, I created <code>Tracker</code> — a memory allocator that wraps another allocator and keeps track of allocations:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// A Stats records statistics about the memory allocator.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint64_t</span> <span class="n">Alloc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint64_t</span> <span class="n">TotalAlloc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint64_t</span> <span class="n">Mallocs</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint64_t</span> <span class="n">Frees</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">mem_Stats</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// A Tracker wraps an Allocator and tracks all
</span></span></span><span class="line"><span class="cl"><span class="c1">// allocations and deallocations made through it.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">mem_Allocator</span> <span class="n">Allocator</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mem_Stats</span> <span class="n">Stats</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">mem_Tracker</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">so_Result</span> <span class="nf">mem_Tracker_Alloc</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">size</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">align</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">mem_Tracker</span><span class="o">*</span> <span class="n">t</span> <span class="o">=</span> <span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_Result</span> <span class="n">res</span> <span class="o">=</span> <span class="n">t</span><span class="o">-&gt;</span><span class="n">Allocator</span><span class="p">.</span><span class="nf">Alloc</span><span class="p">(</span><span class="n">t</span><span class="o">-&gt;</span><span class="n">Allocator</span><span class="p">.</span><span class="n">self</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">align</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">t</span><span class="o">-&gt;</span><span class="n">Stats</span><span class="p">.</span><span class="n">Alloc</span> <span class="o">+=</span> <span class="p">(</span><span class="kt">uint64_t</span><span class="p">)(</span><span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span><span class="o">-&gt;</span><span class="n">Stats</span><span class="p">.</span><span class="n">TotalAlloc</span> <span class="o">+=</span> <span class="p">(</span><span class="kt">uint64_t</span><span class="p">)(</span><span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span><span class="o">-&gt;</span><span class="n">Stats</span><span class="p">.</span><span class="n">Mallocs</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">so_Result</span><span class="p">){.</span><span class="n">val</span><span class="p">.</span><span class="n">as_ptr</span> <span class="o">=</span> <span class="n">res</span><span class="p">.</span><span class="n">val</span><span class="p">.</span><span class="n">as_ptr</span><span class="p">,</span> <span class="p">.</span><span class="n">err</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">mem_Tracker_Free</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">ptr</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">size</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">align</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">mem_Tracker</span><span class="o">*</span> <span class="n">t</span> <span class="o">=</span> <span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span><span class="o">-&gt;</span><span class="n">Allocator</span><span class="p">.</span><span class="nf">Free</span><span class="p">(</span><span class="n">t</span><span class="o">-&gt;</span><span class="n">Allocator</span><span class="p">.</span><span class="n">self</span><span class="p">,</span> <span class="n">ptr</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">align</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span><span class="o">-&gt;</span><span class="n">Stats</span><span class="p">.</span><span class="n">Alloc</span> <span class="o">-=</span> <span class="p">(</span><span class="kt">uint64_t</span><span class="p">)(</span><span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span><span class="o">-&gt;</span><span class="n">Stats</span><span class="p">.</span><span class="n">Frees</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The benchmark gets an allocator through the <code>testing_RunBenchmarks</code> function and wraps it in a <code>Tracker</code> to keep track of allocations:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_Slice</span> <span class="n">benchs</span> <span class="o">=</span> <span class="p">{(</span><span class="n">testing_Benchmark</span><span class="p">[</span><span class="mi">4</span><span class="p">]){</span>
</span></span><span class="line"><span class="cl">        <span class="p">{.</span><span class="n">Name</span> <span class="o">=</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;WriteS_AutoGrow&#34;</span><span class="p">),</span> <span class="p">.</span><span class="n">F</span> <span class="o">=</span> <span class="n">main_WriteString_AutoGrow</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{.</span><span class="n">Name</span> <span class="o">=</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;WriteS_PreGrow&#34;</span><span class="p">),</span> <span class="p">.</span><span class="n">F</span> <span class="o">=</span> <span class="n">main_WriteString_PreGrow</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{.</span><span class="n">Name</span> <span class="o">=</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;WriteB_AutoGrow&#34;</span><span class="p">),</span> <span class="p">.</span><span class="n">F</span> <span class="o">=</span> <span class="n">main_Write_AutoGrow</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{.</span><span class="n">Name</span> <span class="o">=</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;WriteB_PreGrow&#34;</span><span class="p">),</span> <span class="p">.</span><span class="n">F</span> <span class="o">=</span> <span class="n">main_Write_PreGrow</span><span class="p">}},</span>
</span></span><span class="line"><span class="cl">        <span class="mi">4</span><span class="p">,</span> <span class="mi">4</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="nf">testing_RunBenchmarks</span><span class="p">(</span><span class="n">mem_System</span><span class="p">,</span> <span class="n">benchs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>There's no auto-discovery, but the manual setup is quite straightforward.</p>
<h2 id="optimizing-search">Optimizing search</h2>
<p>With the benchmarking setup ready, I ran benchmarks on the <code>strings</code> package. Some functions did well — about 1.5-2x faster than their Go equivalents:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">go
</span></span><span class="line"><span class="cl">Benchmark_Clone-8      12143073      98.50 ns/op    1024 B/op    1 allocs/op
</span></span><span class="line"><span class="cl">Benchmark_Fields-8       791077    1524 ns/op        288 B/op    1 allocs/op
</span></span><span class="line"><span class="cl">Benchmark_Repeat-8      9197040     127.3 ns/op     1024 B/op    1 allocs/op
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">c
</span></span><span class="line"><span class="cl">Benchmark_Clone        27935466      41.84 ns/op    1024 B/op    1 allocs/op
</span></span><span class="line"><span class="cl">Benchmark_Fields        1319384     907.7 ns/op      272 B/op    1 allocs/op
</span></span><span class="line"><span class="cl">Benchmark_Repeat       18445929      64.11 ns/op    1024 B/op    1 allocs/op
</span></span></code></pre></div><p>But <code>Index</code> (searching for a substring in a string) was a total disaster — it was nearly 20 times slower than in Go:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">go
</span></span><span class="line"><span class="cl">Benchmark_Index-8      47874408      25.14 ns/op       0 B/op    0 allocs/op
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">c
</span></span><span class="line"><span class="cl">Benchmark_Index          483787     483.1 ns/op        0 B/op    0 allocs/op
</span></span></code></pre></div><p>The problem was caused by the <code>IndexByte</code> function we looked at earlier:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// IndexByte returns the index of the first instance
</span></span></span><span class="line"><span class="cl"><span class="c1">// of c in b, or -1 if c is not present in b.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">IndexByte</span><span class="p">(</span><span class="nx">b</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="nx">c</span> <span class="kt">byte</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">x</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">b</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nx">x</span> <span class="o">==</span> <span class="nx">c</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="nx">i</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="o">-</span><span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This &quot;pure&quot; Go implementation is just a fallback. On most platforms, Go uses a specialized version of <code>IndexByte</code> written in assembly.</p>
<p>For the C version, the easiest solution was to use <code>memchr</code>, which is also optimized for most platforms:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kr">inline</span> <span class="n">so_int</span> <span class="nf">bytealg_IndexByte</span><span class="p">(</span><span class="n">so_Slice</span> <span class="n">b</span><span class="p">,</span> <span class="n">so_byte</span> <span class="n">c</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">at</span> <span class="o">=</span> <span class="nf">memchr</span><span class="p">(</span><span class="n">b</span><span class="p">.</span><span class="n">ptr</span><span class="p">,</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">c</span><span class="p">,</span> <span class="n">b</span><span class="p">.</span><span class="n">len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">at</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">so_int</span><span class="p">)((</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">at</span> <span class="o">-</span> <span class="p">(</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">b</span><span class="p">.</span><span class="n">ptr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>With this fix, the benchmark results changed drastically:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">go
</span></span><span class="line"><span class="cl">Benchmark_Index-8        47874408    25.14 ns/op    0 B/op    0 allocs/op
</span></span><span class="line"><span class="cl">Benchmark_IndexByte-8    54982188    21.98 ns/op    0 B/op    0 allocs/op
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">c
</span></span><span class="line"><span class="cl">Benchmark_Index          33552540    35.21 ns/op    0 B/op    0 allocs/op
</span></span><span class="line"><span class="cl">Benchmark_IndexByte      36868624    32.81 ns/op    0 B/op    0 allocs/op
</span></span></code></pre></div><p>Still not quite as fast as Go, but it's close. Honestly, I don't know why the <code>memchr</code>-based implementation is still slower than Go's assembly here, but I decided not to pursue it any further.</p>
<p>After running the rest of the <code>strings</code> function benchmarks, the ported versions won all of them except for two:</p>
<table>
<thead>
<tr>
<th>Benchmark</th>
<th style="text-align:right">Go</th>
<th style="text-align:right">C (mimalloc)</th>
<th style="text-align:right">C (arena)</th>
<th>Winner</th>
</tr>
</thead>
<tbody>
<tr>
<td>Clone</td>
<td style="text-align:right">99ns</td>
<td style="text-align:right">42ns</td>
<td style="text-align:right">34ns</td>
<td><strong>C</strong> - 2.4x</td>
</tr>
<tr>
<td>Compare</td>
<td style="text-align:right">47ns</td>
<td style="text-align:right">36ns</td>
<td style="text-align:right">36ns</td>
<td><strong>C</strong> - 1.3x</td>
</tr>
<tr>
<td>Fields</td>
<td style="text-align:right">1524ns</td>
<td style="text-align:right">908ns</td>
<td style="text-align:right">912ns</td>
<td><strong>C</strong> - 1.7x</td>
</tr>
<tr>
<td>Index</td>
<td style="text-align:right">25ns</td>
<td style="text-align:right">35ns</td>
<td style="text-align:right">34ns</td>
<td>Go - 0.7x</td>
</tr>
<tr>
<td>IndexByte</td>
<td style="text-align:right">22ns</td>
<td style="text-align:right">33ns</td>
<td style="text-align:right">33ns</td>
<td>Go - 0.7x</td>
</tr>
<tr>
<td>Repeat</td>
<td style="text-align:right">127ns</td>
<td style="text-align:right">64ns</td>
<td style="text-align:right">67ns</td>
<td><strong>C</strong> - 1.9x</td>
</tr>
<tr>
<td>ReplaceAll</td>
<td style="text-align:right">243ns</td>
<td style="text-align:right">200ns</td>
<td style="text-align:right">203ns</td>
<td><strong>C</strong> - 1.2x</td>
</tr>
<tr>
<td>Split</td>
<td style="text-align:right">1899ns</td>
<td style="text-align:right">1399ns</td>
<td style="text-align:right">1423ns</td>
<td><strong>C</strong> - 1.3x</td>
</tr>
<tr>
<td>ToUpper</td>
<td style="text-align:right">2066ns</td>
<td style="text-align:right">1602ns</td>
<td style="text-align:right">1622ns</td>
<td><strong>C</strong> - 1.3x</td>
</tr>
<tr>
<td>Trim</td>
<td style="text-align:right">501ns</td>
<td style="text-align:right">373ns</td>
<td style="text-align:right">375ns</td>
<td><strong>C</strong> - 1.3x</td>
</tr>
</tbody>
</table>
<p><a href="https://github.com/solod-dev/solod/blob/main/bench/README.md">Benchmarking details</a></p>
<h2 id="optimizing-builder">Optimizing builder</h2>
<p><code>strings.Builder</code> is a common way to compose strings from parts in Go, so I tested its performance too. The results were worse than I expected:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">go
</span></span><span class="line"><span class="cl">Benchmark_WriteS_AutoGrow-8   5385492   224.0 ns/op   1424 B/op   5 allocs/op
</span></span><span class="line"><span class="cl">Benchmark_WriteS_PreGrow-8   10692721   112.9 ns/op    640 B/op   1 allocs/op
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">c
</span></span><span class="line"><span class="cl">Benchmark_WriteS_AutoGrow     5659255   212.9 ns/op   1147 B/op   5 allocs/op
</span></span><span class="line"><span class="cl">Benchmark_WriteS_PreGrow      9811054   122.1 ns/op    592 B/op   1 allocs/op
</span></span></code></pre></div><p>Here, the C version performed about the same as Go, but I expected it to be faster. Unlike <code>Index</code>, <code>Builder</code> is written entirely in Go, so there's no reason the ported version should lose in this benchmark.</p>
<p>The <code>WriteString</code> method looked almost identical in Go and C:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// WriteString appends the contents of s to b&#39;s buffer.
</span></span></span><span class="line"><span class="cl"><span class="c1">// It returns the length of s and a nil error.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">Builder</span><span class="p">)</span> <span class="nf">WriteString</span><span class="p">(</span><span class="nx">s</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">b</span><span class="p">.</span><span class="nx">buf</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">b</span><span class="p">.</span><span class="nx">buf</span><span class="p">,</span> <span class="nx">s</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="nx">s</span><span class="p">),</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="n">so_Result</span> <span class="nf">strings_Builder_WriteString</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="n">so_String</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">strings_Builder</span><span class="o">*</span> <span class="n">b</span> <span class="o">=</span> <span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">strings_Builder_grow</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="nf">so_len</span><span class="p">(</span><span class="n">s</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">b</span><span class="o">-&gt;</span><span class="n">buf</span> <span class="o">=</span> <span class="nf">so_extend</span><span class="p">(</span><span class="n">so_byte</span><span class="p">,</span> <span class="n">b</span><span class="o">-&gt;</span><span class="n">buf</span><span class="p">,</span> <span class="nf">so_string_bytes</span><span class="p">(</span><span class="n">s</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">so_Result</span><span class="p">){.</span><span class="n">val</span><span class="p">.</span><span class="n">as_int</span> <span class="o">=</span> <span class="nf">so_len</span><span class="p">(</span><span class="n">s</span><span class="p">),</span> <span class="p">.</span><span class="n">err</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Go's <code>append</code> automatically grows the backing slice, while <code>strings_Builder_grow</code> does it manually (<code>so_extend</code>, on the contrary, doesn't grow the slice — it's merely a <code>memcpy</code> wrapper). So, there shouldn't be any difference. I had to investigate.</p>
<p>Looking at the compiled binary, I noticed a difference in how the functions returned results. Go returns multiple values in separate registers, so <code>(int, error)</code> uses three registers: one for 8-byte <code>int</code>, two for the <code>error</code> interface (implemented as two 8-byte pointers). But in C, <code>so_Result</code> was a single struct made up of two <code>so_Value</code> unions and a <code>so_Error</code> pointer:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">union</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">as_bool</span><span class="p">;</span>        <span class="c1">// 1 byte
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">so_int</span> <span class="n">as_int</span><span class="p">;</span>       <span class="c1">// 8 bytes
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">int64_t</span> <span class="n">as_i64</span><span class="p">;</span>      <span class="c1">// 8 bytes
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">so_String</span> <span class="n">as_string</span><span class="p">;</span> <span class="c1">// 16 bytes (ptr + len)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">so_Slice</span> <span class="n">as_slice</span><span class="p">;</span>   <span class="c1">// 24 bytes (ptr + len + cap)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">void</span><span class="o">*</span> <span class="n">as_ptr</span><span class="p">;</span>        <span class="c1">// 8 bytes
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// ... other types
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="n">so_Value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_Value</span> <span class="n">val</span><span class="p">;</span>        <span class="c1">// 24 bytes
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">so_Value</span> <span class="n">val2</span><span class="p">;</span>       <span class="c1">// 24 bytes
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">so_Error</span> <span class="n">err</span><span class="p">;</span>        <span class="c1">// 8 bytes
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="n">so_Result</span><span class="p">;</span>
</span></span></code></pre></div><p>Of course, this 56-byte monster can't be returned in registers — the C calling convention passes it through memory instead. Since <code>WriteString</code> is on the hot path in the benchmark, I figured this had to be the issue. So I switched from a single monolithic <code>so_Result</code> type to signature-specific types for multi-return pairs:</p>
<ul>
<li><code>so_R_bool_err</code> for <code>(bool, error)</code>;</li>
<li><code>so_R_int_err</code> for <code>(so_int, error)</code>;</li>
<li><code>so_R_str_err</code> for <code>(so_String, error)</code>;</li>
<li>etc.</li>
</ul>
<p>Now, the <code>Builder.WriteString</code> implementation in C looked like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">val</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_Error</span> <span class="n">err</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">so_R_int_err</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="n">so_R_int_err</span> <span class="nf">strings_Builder_WriteString</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="n">so_String</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p><code>so_R_int_err</code> is only 16 bytes — small enough to be returned in two registers. Problem solved! But it wasn't — the benchmark only showed a slight improvement.</p>
<p>After looking into it more, I finally found the real issue: unlike Go, the C compiler wasn't inlining <code>WriteString</code> calls. Adding <code>inline</code> and moving <code>strings_Builder_WriteString</code> to the header file made all the difference:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">go
</span></span><span class="line"><span class="cl">Benchmark_WriteS_AutoGrow-8   5385492   224.0 ns/op   1424 B/op   5 allocs/op
</span></span><span class="line"><span class="cl">Benchmark_WriteS_PreGrow-8   10692721   112.9 ns/op    640 B/op   1 allocs/op
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">c
</span></span><span class="line"><span class="cl">Benchmark_WriteS_AutoGrow    10344024   115.9 ns/op   1147 B/op   5 allocs/op
</span></span><span class="line"><span class="cl">Benchmark_WriteS_PreGrow     41045286    28.74 ns/op   592 B/op   1 allocs/op
</span></span></code></pre></div><p>2-4x faster. That's what I was hoping for!</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Porting <code>bytes</code> and <code>strings</code> was a mix of easy parts and interesting challenges. The pure functions were straightforward — just translate the syntax and pay attention to operator precedence. The real design challenge was memory management. Using allocators turned out to be a good solution, making memory allocation clear and explicit without being too difficult to use.</p>
<p>The benchmarks showed that the C versions outperformed Go in most cases, sometimes by 2-4x. The only exceptions were <code>Index</code> and <code>IndexByte</code>, where Go relies on hand-written assembly. The <code>strings.Builder</code> optimization was an interesting challenge: what seemed like a return-type issue was actually an inlining problem, and fixing it gave a nice speed boost.</p>
<p>There's a lot more of Go's stdlib to port. In the next post, we'll cover <code>time</code> — a very unique Go package. In the meantime, if you'd like to write Go that translates to C — with no runtime and manual memory management — I invite you to try <a href="https://github.com/solod-dev/solod">Solod</a>. The <code>bytes</code> and <code>strings</code> packages are included, of course.</p>
]]></content:encoded></item><item><title>Porting Go's io package to C</title><link>https://antonz.org/porting-go-io/</link><pubDate>Wed, 25 Mar 2026 14:00:00 +0000</pubDate><guid>https://antonz.org/porting-go-io/</guid><description>Interfaces, slices, multi-returns and alloca.</description><content:encoded><![CDATA[<p>Creating a subset of Go that <a href="/solod/">translates to C</a> was never my end goal. I liked writing C code with Go, but without the standard library it felt pretty limited. So, the next logical step was to port Go's stdlib to C.</p>
<p>Of course, this isn't something I could do all at once. So I started with the standard library packages that had the fewest dependencies, and one of them was the <code>io</code> package. This post is about how that went.</p>
<p><a href="#the-io-package">io package</a> •
<a href="#slices">Slices</a> •
<a href="#multiple-returns">Multiple returns</a> •
<a href="#errors">Errors</a> •
<a href="#interfaces">Interfaces</a> •
<a href="#type-assertion">Type assertion</a> •
<a href="#specialized-readers">Specialized readers</a> •
<a href="#copy">Copy</a> •
<a href="#wrapping-up">Wrapping up</a></p>
<h2 id="the-io-package">The io package</h2>
<p><code>io</code> is one of the core Go packages. It introduces the concepts of <em>readers</em> and <em>writers</em>, which are also common in other programming languages.</p>
<p>In Go, a reader is anything that can read some raw data (bytes) from a source into a slice:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Reader</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">Read</span><span class="p">(</span><span class="nx">p</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>A writer is anything that can take some raw data from a slice and write it to a destination:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Writer</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">Write</span><span class="p">(</span><span class="nx">p</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The <code>io</code> package defines many other interfaces, like <code>Seeker</code> and <code>Closer</code>, as well as combinations like <code>ReadWriter</code> and <code>WriteCloser</code>. It also provides several functions, the most well-known being <code>Copy</code>, which copies all data from a source (represented by a reader) to a destination (represented by a writer):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Copy</span><span class="p">(</span><span class="nx">dst</span> <span class="nx">Writer</span><span class="p">,</span> <span class="nx">src</span> <span class="nx">Reader</span><span class="p">)</span> <span class="p">(</span><span class="nx">written</span> <span class="kt">int64</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span>
</span></span></code></pre></div><p>C, of course, doesn't have interfaces. But before I get into that, I had to make several other design decisions.</p>
<h2 id="slices">Slices</h2>
<p>In general, a <em>slice</em> is a linear container that holds N elements of type T. Typically, a slice is a view of some underlying data. In Go, a slice consists of a pointer to a block of allocated memory, a length (the number of elements in the slice), and a capacity (the total number of elements that can fit in the backing memory before the runtime needs to re-allocate):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">slice</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">array</span> <span class="nx">unsafe</span><span class="p">.</span><span class="nx">Pointer</span>
</span></span><span class="line"><span class="cl">    <span class="nx">len</span>   <span class="kt">int</span>
</span></span><span class="line"><span class="cl">    <span class="nx">cap</span>   <span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Interfaces in the <code>io</code> package work with fixed-length slices (readers and writers should never append to a slice), and they only use byte slices. So, the simplest way to represent this in C could be:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">ptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">Bytes</span><span class="p">;</span>
</span></span></code></pre></div><p>But since I needed a general-purpose slice type, I decided to do it the Go way instead:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">ptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="n">cap</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">so_Slice</span><span class="p">;</span>
</span></span></code></pre></div><p>Plus a bound-checking helper to access slice elements:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define so_at(T, s, i) (*so_at_ptr(T, s, i))
</span></span></span><span class="line"><span class="cl"><span class="cp">#define so_at_ptr(T, s, i) ({            \
</span></span></span><span class="line"><span class="cl"><span class="cp">    so_Slice _s_at = (s);                \
</span></span></span><span class="line"><span class="cl"><span class="cp">    size_t _i = (size_t)(i);             \
</span></span></span><span class="line"><span class="cl"><span class="cp">    if (_i &gt;= _s_at.len)                 \
</span></span></span><span class="line"><span class="cl"><span class="cp">        so_panic(&#34;index out of bounds&#34;); \
</span></span></span><span class="line"><span class="cl"><span class="cp">    (T*)_s_at.ptr + _i;                  \
</span></span></span><span class="line"><span class="cl"><span class="cp">})
</span></span></span></code></pre></div><p>Usage example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// go
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">nums</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">int</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">nums</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">=</span> <span class="mi">11</span>
</span></span><span class="line"><span class="cl"><span class="nx">nums</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="p">=</span> <span class="mi">22</span>
</span></span><span class="line"><span class="cl"><span class="nx">nums</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="p">=</span> <span class="mi">33</span>
</span></span><span class="line"><span class="cl"><span class="nx">n1</span> <span class="o">:=</span> <span class="nx">nums</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">so_Slice</span> <span class="n">nums</span> <span class="o">=</span> <span class="nf">so_make_slice</span><span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">so_at</span><span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="n">nums</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="o">=</span> <span class="mi">11</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">so_at</span><span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="n">nums</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="o">=</span> <span class="mi">22</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">so_at</span><span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="n">nums</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">=</span> <span class="mi">33</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">so_int</span> <span class="n">n1</span> <span class="o">=</span> <span class="nf">so_at</span><span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="n">nums</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span></code></pre></div><p>So far, so good.</p>
<h2 id="multiple-returns">Multiple returns</h2>
<p>Let's look at the <code>Read</code> method again:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nf">Read</span><span class="p">(</span><span class="nx">p</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span>
</span></span></code></pre></div><p>It returns two values: an <code>int</code> and an <code>error</code>. C functions can only return one value, so I needed to figure out how to handle this.</p>
<p>The classic approach would be to pass output parameters by pointer, like <code>read(p, &amp;n, &amp;err)</code> or <code>n = read(p, &amp;err)</code>. But that doesn't compose well and looks nothing like Go. Instead, I went with a result struct:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">union</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">as_bool</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">as_int</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int64_t</span> <span class="n">as_i64</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_String</span> <span class="n">as_string</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_Slice</span> <span class="n">as_slice</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">as_ptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ... other types
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="n">so_Value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_Value</span> <span class="n">val</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_Error</span> <span class="n">err</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">so_Result</span><span class="p">;</span>
</span></span></code></pre></div><p>The <code>so_Value</code> union can store any primitive type, as well as strings, slices, and pointers. The <code>so_Result</code> type combines a value with an error. So, our <code>Read</code> method (let's assume it's just a regular function for now):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Read</span><span class="p">(</span><span class="nx">p</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span>
</span></span></code></pre></div><p>Translates to:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">so_Result</span> <span class="nf">Read</span><span class="p">(</span><span class="n">so_Slice</span> <span class="n">p</span><span class="p">);</span>
</span></span></code></pre></div><p>And the caller can access the result like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">so_Result</span> <span class="n">res</span> <span class="o">=</span> <span class="nf">Read</span><span class="p">(</span><span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="n">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_panic</span><span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="n">err</span><span class="o">-&gt;</span><span class="n">msg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nf">so_println</span><span class="p">(</span><span class="s">&#34;read&#34;</span><span class="p">,</span> <span class="n">res</span><span class="p">.</span><span class="n">val</span><span class="p">.</span><span class="n">as_int</span><span class="p">,</span> <span class="s">&#34;bytes&#34;</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="errors">Errors</h2>
<p>For the error type itself, I went with a simple pointer to an immutable string:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">so_Error_</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">msg</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">so_Error_</span><span class="o">*</span> <span class="n">so_Error</span><span class="p">;</span>
</span></span></code></pre></div><p>Plus a constructor macro:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define errors_New(s) (&amp;(struct so_Error_){s})
</span></span></span></code></pre></div><p>I wanted to avoid heap allocations as much as possible, so decided not to support dynamic errors. Only sentinel errors are used, and they're defined at the file level like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">so_Error</span> <span class="n">io_EOF</span> <span class="o">=</span> <span class="nf">errors_New</span><span class="p">(</span><span class="s">&#34;EOF&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">so_Error</span> <span class="n">io_ErrOffset</span> <span class="o">=</span> <span class="nf">errors_New</span><span class="p">(</span><span class="s">&#34;io: invalid offset&#34;</span><span class="p">);</span>
</span></span></code></pre></div><p>Errors are compared by pointer identity (<code>==</code>), not by string content — just like sentinel errors in Go. A <code>nil</code> error is a <code>NULL</code> pointer. This keeps error handling cheap and straightforward.</p>
<h2 id="interfaces">Interfaces</h2>
<p>This was the big one. In Go, an interface is a type that specifies a set of methods. Any concrete type that implements those methods satisfies the interface — no explicit declaration needed. In C, there's no such mechanism.</p>
<p>For interfaces, I decided to use &quot;fat&quot; structs with function pointers. That way, Go's <code>io.Reader</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Reader</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">Read</span><span class="p">(</span><span class="nx">p</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Becomes an <code>io_Reader</code> struct in C:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_Result</span> <span class="p">(</span><span class="o">*</span><span class="n">Read</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="n">so_Slice</span> <span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">io_Reader</span><span class="p">;</span>
</span></span></code></pre></div><p>The <code>self</code> pointer holds the concrete value, and each method becomes a function pointer that takes <code>self</code> as its first argument. This is less efficient than using a static method table, especially if the interface has a lot of methods, but it's simpler. So I decided it was good enough for the first version.</p>
<p>Now functions can work with interfaces without knowing the specific implementation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// ReadFull reads exactly len(buf) bytes from r into buf.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">so_Result</span> <span class="nf">io_ReadFull</span><span class="p">(</span><span class="n">io_Reader</span> <span class="n">r</span><span class="p">,</span> <span class="n">so_Slice</span> <span class="n">buf</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">n</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_Error</span> <span class="n">err</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;</span> <span class="n">n</span> <span class="o">&lt;</span> <span class="nf">so_len</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">err</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">so_Slice</span> <span class="n">curBuf</span> <span class="o">=</span> <span class="nf">so_slice</span><span class="p">(</span><span class="n">so_byte</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">buf</span><span class="p">.</span><span class="n">len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">so_Result</span> <span class="n">res</span> <span class="o">=</span> <span class="n">r</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">self</span><span class="p">,</span> <span class="n">curBuf</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">err</span> <span class="o">=</span> <span class="n">res</span><span class="p">.</span><span class="n">err</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">n</span> <span class="o">+=</span> <span class="n">res</span><span class="p">.</span><span class="n">val</span><span class="p">.</span><span class="n">as_int</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// A custom reader.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_Slice</span> <span class="n">b</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">reader</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="n">so_Result</span> <span class="nf">reader_Read</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="n">so_Slice</span> <span class="n">p</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// We&#39;ll read from a string literal.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">so_String</span> <span class="n">str</span> <span class="o">=</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;hello world&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">reader</span> <span class="n">rdr</span> <span class="o">=</span> <span class="p">(</span><span class="n">reader</span><span class="p">){.</span><span class="n">b</span> <span class="o">=</span> <span class="nf">so_string_bytes</span><span class="p">(</span><span class="n">str</span><span class="p">)};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Wrap the specific reader into an interface.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">io_Reader</span> <span class="n">r</span> <span class="o">=</span> <span class="p">(</span><span class="n">io_Reader</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">self</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">rdr</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">Read</span> <span class="o">=</span> <span class="n">reader_Read</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Read the first 4 bytes from the string into a buffer.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">so_Slice</span> <span class="n">buf</span> <span class="o">=</span> <span class="nf">so_make_slice</span><span class="p">(</span><span class="n">so_byte</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ReadFull doesn&#39;t care about the specific reader implementation -
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// it could read from a file, the network, or anything else.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">so_Result</span> <span class="n">res</span> <span class="o">=</span> <span class="nf">io_ReadFull</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">buf</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Calling a method on the interface just goes through the function pointer:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// r.Read(buf) becomes:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">r</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">self</span><span class="p">,</span> <span class="n">buf</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="type-assertion">Type assertion</h2>
<p>Go's interface is more than just a value wrapper with a method table. It also stores type information about the value it holds:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">iface</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">tab</span>  <span class="o">*</span><span class="nx">itab</span>
</span></span><span class="line"><span class="cl">    <span class="nx">data</span> <span class="nx">unsafe</span><span class="p">.</span><span class="nx">Pointer</span>  <span class="c1">// specific value
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">itab</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Inter</span> <span class="o">*</span><span class="nx">InterfaceType</span> <span class="c1">// method table
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">Type</span>  <span class="o">*</span><span class="nx">Type</span>          <span class="c1">// type information
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>Since the runtime knows the exact type inside the interface, it can try to &quot;upgrade&quot; the interface (for example, a regular <code>Reader</code>) to another interface (like <code>WriterTo</code>) using a <em>type assertion</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// copyBuffer copies from src to dst using the provided buffer
</span></span></span><span class="line"><span class="cl"><span class="c1">// until either EOF is reached on src or an error occurs.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">copyBuffer</span><span class="p">(</span><span class="nx">dst</span> <span class="nx">Writer</span><span class="p">,</span> <span class="nx">src</span> <span class="nx">Reader</span><span class="p">,</span> <span class="nx">buf</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="nx">written</span> <span class="kt">int64</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// If the reader has a WriteTo method, use it to do the copy.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="nx">wt</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">src</span><span class="p">.(</span><span class="nx">WriterTo</span><span class="p">);</span> <span class="nx">ok</span> <span class="p">{</span>  <span class="c1">// try &#34;upgrading&#34; to WriterTo
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">return</span> <span class="nx">wt</span><span class="p">.</span><span class="nf">WriteTo</span><span class="p">(</span><span class="nx">dst</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// src is not a WriterTo, proceed with the default copy implementation.
</span></span></span></code></pre></div><p>The last thing I wanted to do was reinvent Go's dynamic type system in C, so dropping this feature was an easy decision.</p>
<p>There's another kind of type assertion, though — when we unwrap the interface to get the value of a specific type:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Does r (a Reader) hold a pointer to a value of concrete type LimitedReader?
</span></span></span><span class="line"><span class="cl"><span class="c1">// If true, lr will get the unwrapped pointer.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">lr</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">r</span><span class="p">.(</span><span class="o">*</span><span class="nx">LimitedReader</span><span class="p">)</span>
</span></span></code></pre></div><p>And this kind of assertion is quite possible in C. All we have to do is compare function pointers:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Are r.Read and LimitedReader_Read the same function?
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">bool</span> <span class="n">ok</span> <span class="o">=</span> <span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">Read</span> <span class="o">==</span> <span class="n">LimitedReader_Read</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">io_LimitedReader</span><span class="o">*</span> <span class="n">lr</span> <span class="o">=</span> <span class="n">r</span><span class="p">.</span><span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>If two different types happened to share the same method implementation, this would break. In practice, each concrete type has its own methods, so the function pointer serves as a reliable type tag.</p>
<h2 id="specialized-readers">Specialized readers</h2>
<p>After I decided on the interface approach, porting the actual <code>io</code> types was pretty easy. For example, <code>LimitedReader</code> wraps a reader and stops with EOF after reading N bytes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">LimitedReader</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">R</span> <span class="nx">Reader</span>
</span></span><span class="line"><span class="cl">    <span class="nx">N</span> <span class="kt">int64</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">l</span> <span class="o">*</span><span class="nx">LimitedReader</span><span class="p">)</span> <span class="nf">Read</span><span class="p">(</span><span class="nx">p</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">l</span><span class="p">.</span><span class="nx">N</span> <span class="o">&lt;=</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">EOF</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">int64</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="nx">p</span><span class="p">))</span> <span class="p">&gt;</span> <span class="nx">l</span><span class="p">.</span><span class="nx">N</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">p</span> <span class="p">=</span> <span class="nx">p</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="nx">l</span><span class="p">.</span><span class="nx">N</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nx">n</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">l</span><span class="p">.</span><span class="nx">R</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="nx">p</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">l</span><span class="p">.</span><span class="nx">N</span> <span class="o">-=</span> <span class="nb">int64</span><span class="p">(</span><span class="nx">n</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">n</span><span class="p">,</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The logic is straightforward: if there are no bytes left, return EOF. Otherwise, if the buffer is bigger than the remaining size, shorten it. Then, call the underlying reader, and decrease the remaining size.</p>
<p>Here's what the ported C code looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">io_Reader</span> <span class="n">R</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int64_t</span> <span class="n">N</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">io_LimitedReader</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">so_Result</span> <span class="nf">io_LimitedReader_Read</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="n">so_Slice</span> <span class="n">p</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">io_LimitedReader</span><span class="o">*</span> <span class="n">l</span> <span class="o">=</span> <span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">l</span><span class="o">-&gt;</span><span class="n">N</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">(</span><span class="n">so_Result</span><span class="p">){.</span><span class="n">val</span><span class="p">.</span><span class="n">as_int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="p">.</span><span class="n">err</span> <span class="o">=</span> <span class="n">io_EOF</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">((</span><span class="kt">int64_t</span><span class="p">)(</span><span class="nf">so_len</span><span class="p">(</span><span class="n">p</span><span class="p">))</span> <span class="o">&gt;</span> <span class="n">l</span><span class="o">-&gt;</span><span class="n">N</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">p</span> <span class="o">=</span> <span class="nf">so_slice</span><span class="p">(</span><span class="n">so_byte</span><span class="p">,</span> <span class="n">p</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">l</span><span class="o">-&gt;</span><span class="n">N</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_Result</span> <span class="n">res</span> <span class="o">=</span> <span class="n">l</span><span class="o">-&gt;</span><span class="n">R</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="n">l</span><span class="o">-&gt;</span><span class="n">R</span><span class="p">.</span><span class="n">self</span><span class="p">,</span> <span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">n</span> <span class="o">=</span> <span class="n">res</span><span class="p">.</span><span class="n">val</span><span class="p">.</span><span class="n">as_int</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">l</span><span class="o">-&gt;</span><span class="n">N</span> <span class="o">-=</span> <span class="p">(</span><span class="kt">int64_t</span><span class="p">)(</span><span class="n">n</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">so_Result</span><span class="p">){.</span><span class="n">val</span><span class="p">.</span><span class="n">as_int</span> <span class="o">=</span> <span class="n">n</span><span class="p">,</span> <span class="p">.</span><span class="n">err</span> <span class="o">=</span> <span class="n">res</span><span class="p">.</span><span class="n">err</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>A bit more verbose, but nothing special. The multiple return values, the interface call with <code>l.R.Read</code>, and the slice handling are all implemented as described in previous sections.</p>
<h2 id="copy">Copy</h2>
<p><code>Copy</code> is where everything comes together. Here's the simplified Go version:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Copy copies from src to dst until either
</span></span></span><span class="line"><span class="cl"><span class="c1">// EOF is reached on src or an error occurs.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Copy</span><span class="p">(</span><span class="nx">dst</span> <span class="nx">Writer</span><span class="p">,</span> <span class="nx">src</span> <span class="nx">Reader</span><span class="p">)</span> <span class="p">(</span><span class="nx">written</span> <span class="kt">int64</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Allocate a temporary buffer for copying.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">size</span> <span class="o">:=</span> <span class="mi">32</span> <span class="o">*</span> <span class="mi">1024</span>
</span></span><span class="line"><span class="cl">    <span class="nx">buf</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="nx">size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Copy from src to dst using the buffer.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">nr</span><span class="p">,</span> <span class="nx">er</span> <span class="o">:=</span> <span class="nx">src</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="nx">buf</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nx">nr</span> <span class="p">&gt;</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">nw</span><span class="p">,</span> <span class="nx">ew</span> <span class="o">:=</span> <span class="nx">dst</span><span class="p">.</span><span class="nf">Write</span><span class="p">(</span><span class="nx">buf</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="nx">nr</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">            <span class="nx">written</span> <span class="o">+=</span> <span class="nb">int64</span><span class="p">(</span><span class="nx">nw</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="nx">ew</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nx">err</span> <span class="p">=</span> <span class="nx">ew</span>
</span></span><span class="line"><span class="cl">                <span class="k">break</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nx">er</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="nx">er</span> <span class="o">!=</span> <span class="nx">EOF</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nx">err</span> <span class="p">=</span> <span class="nx">er</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">written</span><span class="p">,</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>In Go, <code>Copy</code> allocates its buffer on the heap with <code>make([]byte, size)</code>. I could take a similar approach in C — make <code>Copy</code> take an allocator and use it to create the buffer like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">so_Result</span> <span class="nf">io_Copy</span><span class="p">(</span><span class="n">mem_Allocator</span> <span class="n">a</span><span class="p">,</span> <span class="n">io_Writer</span> <span class="n">dst</span><span class="p">,</span> <span class="n">io_Reader</span> <span class="n">src</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">size</span> <span class="o">=</span> <span class="mi">32</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_Slice</span> <span class="n">buf</span> <span class="o">=</span> <span class="nf">mem_AllocSlice</span><span class="p">(</span><span class="n">so_byte</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>But since this is just a temporary buffer that only exists during the function call, I decided stack allocation was a better choice:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">so_Result</span> <span class="nf">io_Copy</span><span class="p">(</span><span class="n">io_Writer</span> <span class="n">dst</span><span class="p">,</span> <span class="n">io_Reader</span> <span class="n">src</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">size</span> <span class="o">=</span> <span class="mi">8</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_Slice</span> <span class="n">buf</span> <span class="o">=</span> <span class="nf">so_make_slice</span><span class="p">(</span><span class="n">so_byte</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p><code>so_make_slice</code> allocates memory on a stack with a bounds-checking macro that wraps C's <code>alloca</code>. It moves the stack pointer and gives you a chunk of memory that's automatically freed when the function returns.</p>
<p>People often avoid using <code>alloca</code> because it can cause a stack overflow, but using a bounds-checking wrapper fixes this issue. Another common concern with <code>alloca</code> is that it's not block-scoped — the memory stays allocated until the function exits. However, since we only allocate once, this isn't a problem.</p>
<p>Here's the simplified C version of <code>Copy</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">so_Result</span> <span class="nf">io_Copy</span><span class="p">(</span><span class="n">io_Writer</span> <span class="n">dst</span><span class="p">,</span> <span class="n">io_Reader</span> <span class="n">src</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">size</span> <span class="o">=</span> <span class="mi">8</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">;</span> <span class="c1">// smaller buffer, 8 KiB
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">so_Slice</span> <span class="n">buf</span> <span class="o">=</span> <span class="nf">so_make_slice</span><span class="p">(</span><span class="n">so_byte</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int64_t</span> <span class="n">written</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_Error</span> <span class="n">err</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">so_Result</span> <span class="n">resr</span> <span class="o">=</span> <span class="n">src</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="n">src</span><span class="p">.</span><span class="n">self</span><span class="p">,</span> <span class="n">buf</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">so_int</span> <span class="n">nr</span> <span class="o">=</span> <span class="n">resr</span><span class="p">.</span><span class="n">val</span><span class="p">.</span><span class="n">as_int</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">nr</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">so_Result</span> <span class="n">resw</span> <span class="o">=</span> <span class="n">dst</span><span class="p">.</span><span class="nf">Write</span><span class="p">(</span><span class="n">dst</span><span class="p">.</span><span class="n">self</span><span class="p">,</span> <span class="nf">so_slice</span><span class="p">(</span><span class="n">so_byte</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">nr</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">            <span class="n">so_int</span> <span class="n">nw</span> <span class="o">=</span> <span class="n">resw</span><span class="p">.</span><span class="n">val</span><span class="p">.</span><span class="n">as_int</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">written</span> <span class="o">+=</span> <span class="p">(</span><span class="kt">int64_t</span><span class="p">)(</span><span class="n">nw</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">resw</span><span class="p">.</span><span class="n">err</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">err</span> <span class="o">=</span> <span class="n">resw</span><span class="p">.</span><span class="n">err</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">resr</span><span class="p">.</span><span class="n">err</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">resr</span><span class="p">.</span><span class="n">err</span> <span class="o">!=</span> <span class="n">io_EOF</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">err</span> <span class="o">=</span> <span class="n">resr</span><span class="p">.</span><span class="n">err</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">so_Result</span><span class="p">){.</span><span class="n">val</span><span class="p">.</span><span class="n">as_i64</span> <span class="o">=</span> <span class="n">written</span><span class="p">,</span> <span class="p">.</span><span class="n">err</span> <span class="o">=</span> <span class="n">err</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Here, you can see all the parts from this post working together: a function accepting interfaces, slices passed to interface methods, a result type wrapping multiple return values, error sentinels compared by identity, and a stack-allocated buffer used for the copy.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Porting Go's <code>io</code> package to C meant solving a few problems: representing slices, handling multiple return values, modeling errors, and implementing interfaces using function pointers. None of this needed anything fancy — just structs, unions, functions, and some macros. The resulting C code is more verbose than Go, but it's structurally similar, easy enough to read, and this approach should work well for other Go packages too.</p>
<p>The <code>io</code> package isn't very useful on its own — it mainly defines interfaces and doesn't provide concrete implementations. So, the next two packages to port were naturally <code>bytes</code> and <code>strings</code> — I'll talk about those in the next post.</p>
<p>In the meantime, if you'd like to write Go that translates to C — with no runtime and manual memory management — I invite you to try <a href="https://github.com/solod-dev/solod">Solod</a>. The <code>io</code> package is included, of course.</p>
]]></content:encoded></item><item><title>Solod: Go can be a better C</title><link>https://antonz.org/solod/</link><pubDate>Sat, 21 Mar 2026 14:00:00 +0000</pubDate><guid>https://antonz.org/solod/</guid><description>A subset of Go that transpiles to regular C, with zero runtime.</description><content:encoded><![CDATA[<p>I'm working on a new programming language named Solod (<strong>So</strong>). It's a strict subset of Go that translates to regular C.</p>
<p>Highlights:</p>
<ul>
<li>Go in, C out. You write regular Go code and get readable C11 as output.</li>
<li>Zero runtime. No garbage collection, no reference counting, no hidden allocations.</li>
<li>Rich standard library. Use familiar types and functions ported from Go's stdlib.</li>
<li>Native C interop. Call C from So and So from C — no CGO, no overhead.</li>
<li>Go tooling works out of the box. Syntax highlighting, LSP, linting and &quot;go test&quot;.</li>
</ul>
<p>So supports structs, methods, interfaces, slices, maps, multiple returns, and defer. Everything is stack-allocated by default; heap is opt-in through the standard library. There is limited support for generics, and concurrency is provided by the standard library instead of being built into the language.</p>
<p>So is for Go developers who want systems-level control without learning a new language. And for C programmers who like Go's safety, structure, and tooling.</p>
<p><a href="#hello-world-example">Hello world</a> •
<a href="#language-tour">Language tour</a> •
<a href="#compatibility">Compatibility</a> •
<a href="#design-decisions">Design decisions</a> •
<a href="#frequently-asked-questions">FAQ</a> •
<a href="#final-thoughts">Final thoughts</a></p>
<h2 id="hello-world-example">'Hello world' example</h2>
<p>This Go code in a file <code>main.go</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">&#34;solod.dev/so/time&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Person</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Name</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Age</span>  <span class="kt">int</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Nums</span> <span class="p">[</span><span class="mi">3</span><span class="p">]</span><span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="o">*</span><span class="nx">Person</span><span class="p">)</span> <span class="nf">Sleep</span><span class="p">()</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">p</span><span class="p">.</span><span class="nx">Age</span> <span class="o">+=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">p</span><span class="p">.</span><span class="nx">Age</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">p</span> <span class="o">:=</span> <span class="nx">Person</span><span class="p">{</span><span class="nx">Name</span><span class="p">:</span> <span class="s">&#34;Alice&#34;</span><span class="p">,</span> <span class="nx">Age</span><span class="p">:</span> <span class="mi">30</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nx">p</span><span class="p">.</span><span class="nf">Sleep</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="nb">println</span><span class="p">(</span><span class="nx">p</span><span class="p">.</span><span class="nx">Name</span><span class="p">,</span> <span class="s">&#34;is now&#34;</span><span class="p">,</span> <span class="nx">p</span><span class="p">.</span><span class="nx">Age</span><span class="p">,</span> <span class="s">&#34;years old.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">p</span><span class="p">.</span><span class="nx">Nums</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">=</span> <span class="mi">42</span>
</span></span><span class="line"><span class="cl">    <span class="nb">println</span><span class="p">(</span><span class="s">&#34;1st lucky number is&#34;</span><span class="p">,</span> <span class="nx">p</span><span class="p">.</span><span class="nx">Nums</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">year</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">Year</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="nb">println</span><span class="p">(</span><span class="s">&#34;The year is&#34;</span><span class="p">,</span> <span class="nx">year</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Translates to a header file <code>main.h</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#pragma once
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;so/builtin/builtin.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;so/time/time.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">main_Person</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_String</span> <span class="n">Name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">Age</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">Nums</span><span class="p">[</span><span class="mi">3</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">main_Person</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">so_int</span> <span class="nf">main_Person_Sleep</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">);</span>
</span></span></code></pre></div><p>Plus an implementation file <code>main.c</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;main.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="n">so_int</span> <span class="nf">main_Person_Sleep</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">main_Person</span><span class="o">*</span> <span class="n">p</span> <span class="o">=</span> <span class="p">(</span><span class="n">main_Person</span><span class="o">*</span><span class="p">)</span><span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">p</span><span class="o">-&gt;</span><span class="n">Age</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">p</span><span class="o">-&gt;</span><span class="n">Age</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">main_Person</span> <span class="n">p</span> <span class="o">=</span> <span class="p">(</span><span class="n">main_Person</span><span class="p">){.</span><span class="n">Name</span> <span class="o">=</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;Alice&#34;</span><span class="p">),</span> <span class="p">.</span><span class="n">Age</span> <span class="o">=</span> <span class="mi">30</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="nf">main_Person_Sleep</span><span class="p">(</span><span class="o">&amp;</span><span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_println</span><span class="p">(</span><span class="s">&#34;%.*s %s %&#34;</span> <span class="n">PRId64</span> <span class="s">&#34; %s&#34;</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">Name</span><span class="p">.</span><span class="n">len</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">Name</span><span class="p">.</span><span class="n">ptr</span><span class="p">,</span> <span class="s">&#34;is now&#34;</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">Age</span><span class="p">,</span> <span class="s">&#34;years old.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">p</span><span class="p">.</span><span class="n">Nums</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_println</span><span class="p">(</span><span class="s">&#34;%s %&#34;</span> <span class="n">PRId64</span><span class="p">,</span> <span class="s">&#34;1st lucky number is&#34;</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">Nums</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">year</span> <span class="o">=</span> <span class="nf">time_Time_Year</span><span class="p">(</span><span class="nf">time_Now</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_println</span><span class="p">(</span><span class="s">&#34;%s %&#34;</span> <span class="n">PRId64</span><span class="p">,</span> <span class="s">&#34;The year is&#34;</span><span class="p">,</span> <span class="n">year</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="language-tour">Language tour</h2>
<p>In terms of features, So is an intersection between Go and C, making it one of the simplest C-like languages out there — on par with Hare.</p>
<p>And since So is a strict subset of Go, you already know it if you know Go. It's pretty handy if you don't want to learn another syntax.</p>
<p>Let's briefly go over the language features and see how they translate to C.</p>
<p><a href="#values-and-variables">Variables</a> •
<a href="#strings">Strings</a> •
<a href="#arrays">Arrays</a> •
<a href="#slices">Slices</a> •
<a href="#maps">Maps</a> •
<a href="#ifelse-and-for">If/else and for</a> •
<a href="#functions">Functions</a> •
<a href="#multiple-returns">Multiple returns</a> •
<a href="#structs">Structs</a> •
<a href="#methods">Methods</a> •
<a href="#interfaces">Interfaces</a> •
<a href="#enums">Enums</a> •
<a href="#errors">Errors</a> •
<a href="#defer">Defer</a> •
<a href="#c-interop">C interop</a> •
<a href="#packages">Packages</a></p>
<h3 id="values-and-variables">Values and variables</h3>
<p>So supports basic Go types and variable declarations:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">const</span> <span class="nx">n</span> <span class="p">=</span> <span class="mi">100_000</span>
</span></span><span class="line"><span class="cl"><span class="nx">f</span> <span class="o">:=</span> <span class="mf">3.14</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">r</span> <span class="p">=</span> <span class="sc">&#39;本&#39;</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">v</span> <span class="nx">any</span> <span class="p">=</span> <span class="mi">42</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="n">so_int</span> <span class="n">n</span> <span class="o">=</span> <span class="mi">100000</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">double</span> <span class="n">f</span> <span class="o">=</span> <span class="mf">3.14</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">so_rune</span> <span class="n">r</span> <span class="o">=</span> <span class="n">U</span><span class="sc">&#39;本&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span><span class="o">*</span> <span class="n">v</span> <span class="o">=</span> <span class="o">&amp;</span><span class="p">(</span><span class="n">so_int</span><span class="p">){</span><span class="mi">42</span><span class="p">};</span>
</span></span></code></pre></div><p><code>byte</code> is translated to <code>so_byte</code> (<code>uint8_t</code>), <code>rune</code> to <code>so_rune</code> (<code>int32_t</code>), and <code>int</code> to <code>so_int</code> (<code>int64_t</code>).</p>
<p><code>any</code> is not treated as an interface. Instead, it's translated to <code>void*</code>. This makes handling pointers much easier and removes the need for <code>unsafe.Pointer</code>.</p>
<p><code>nil</code> is translated to <code>NULL</code> (for pointer types).</p>
<h3 id="strings">Strings</h3>
<p>Strings are represented as <code>so_String</code> type in C:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">ptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">so_String</span><span class="p">;</span>
</span></span></code></pre></div><p>All standard string operations are supported, including indexing, slicing, and iterating with a for-range loop.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">str</span> <span class="o">:=</span> <span class="s">&#34;Hi 世界!&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">println</span><span class="p">(</span><span class="s">&#34;str[1] =&#34;</span><span class="p">,</span> <span class="nx">str</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">r</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">str</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">println</span><span class="p">(</span><span class="s">&#34;i =&#34;</span><span class="p">,</span> <span class="nx">i</span><span class="p">,</span> <span class="s">&#34;r =&#34;</span><span class="p">,</span> <span class="nx">r</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">so_String</span> <span class="n">str</span> <span class="o">=</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;Hi 世界!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">so_println</span><span class="p">(</span><span class="s">&#34;%s %u&#34;</span><span class="p">,</span> <span class="s">&#34;str[1] =&#34;</span><span class="p">,</span> <span class="nf">so_at</span><span class="p">(</span><span class="n">so_byte</span><span class="p">,</span> <span class="n">str</span><span class="p">,</span> <span class="mi">1</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="n">so_int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">_iw</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="nf">so_len</span><span class="p">(</span><span class="n">str</span><span class="p">);</span> <span class="n">i</span> <span class="o">+=</span> <span class="n">_iw</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">_iw</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_rune</span> <span class="n">r</span> <span class="o">=</span> <span class="nf">so_utf8_decode</span><span class="p">(</span><span class="n">str</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">_iw</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_println</span><span class="p">(</span><span class="s">&#34;%s %&#34;</span> <span class="n">PRId64</span> <span class="s">&#34; %s %d&#34;</span><span class="p">,</span> <span class="s">&#34;i =&#34;</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="s">&#34;r =&#34;</span><span class="p">,</span> <span class="n">r</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Converting a string to a byte slice and back is a zero-copy operation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">s</span> <span class="o">:=</span> <span class="s">&#34;1世3&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">bs</span> <span class="o">:=</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">s1</span> <span class="o">:=</span> <span class="nb">string</span><span class="p">(</span><span class="nx">bs</span><span class="p">)</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">so_String</span> <span class="n">s</span> <span class="o">=</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;1世3&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">so_Slice</span> <span class="n">bs</span> <span class="o">=</span> <span class="nf">so_string_bytes</span><span class="p">(</span><span class="n">s</span><span class="p">);</span>   <span class="c1">// wraps s.ptr
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">so_String</span> <span class="n">s1</span> <span class="o">=</span> <span class="nf">so_bytes_string</span><span class="p">(</span><span class="n">bs</span><span class="p">);</span> <span class="c1">// wraps bs.ptr
</span></span></span></code></pre></div><p>Converting a string to a rune slice and back allocates on the stack with <code>alloca</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">s</span> <span class="o">:=</span> <span class="s">&#34;1世3&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">rs</span> <span class="o">:=</span> <span class="p">[]</span><span class="nb">rune</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">s1</span> <span class="o">:=</span> <span class="nb">string</span><span class="p">(</span><span class="nx">rs</span><span class="p">)</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">so_String</span> <span class="n">s</span> <span class="o">=</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;1世3&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">so_Slice</span> <span class="n">rs</span> <span class="o">=</span> <span class="nf">so_string_runes</span><span class="p">(</span><span class="n">s</span><span class="p">);</span>   <span class="c1">// allocates
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">so_String</span> <span class="n">s1</span> <span class="o">=</span> <span class="nf">so_runes_string</span><span class="p">(</span><span class="n">rs</span><span class="p">);</span> <span class="c1">// allocates
</span></span></span></code></pre></div><p>There's a <code>so/strings</code> stdlib package for heap-allocated strings and various string operations.</p>
<h3 id="arrays">Arrays</h3>
<p>Arrays are represented as plain C arrays (<code>T name[N]</code>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">a</span> <span class="p">[</span><span class="mi">5</span><span class="p">]</span><span class="kt">int</span>                       <span class="c1">// zero-initialized
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">b</span> <span class="o">:=</span> <span class="p">[</span><span class="mi">5</span><span class="p">]</span><span class="kt">int</span><span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">}</span>         <span class="c1">// explicit values
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">c</span> <span class="o">:=</span> <span class="p">[</span><span class="o">...</span><span class="p">]</span><span class="kt">int</span><span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">}</span>       <span class="c1">// inferred size
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">d</span> <span class="o">:=</span> <span class="p">[</span><span class="o">...</span><span class="p">]</span><span class="kt">int</span><span class="p">{</span><span class="mi">100</span><span class="p">,</span> <span class="mi">3</span><span class="p">:</span> <span class="mi">400</span><span class="p">,</span> <span class="mi">500</span><span class="p">}</span>    <span class="c1">// designated initializers
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">so_int</span> <span class="n">a</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="n">so_int</span> <span class="n">b</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="n">so_int</span> <span class="n">c</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="n">so_int</span> <span class="n">d</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">100</span><span class="p">,</span> <span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="mi">400</span><span class="p">,</span> <span class="mi">500</span><span class="p">};</span>
</span></span></code></pre></div><p><code>len()</code> on arrays is emitted as compile-time constant.</p>
<p>Slicing an array produces a <code>so_Slice</code>.</p>
<h3 id="slices">Slices</h3>
<p>Slices are represented as <code>so_Slice</code> type in C:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">ptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">cap</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">so_Slice</span><span class="p">;</span>
</span></span></code></pre></div><p>All standard slice operations are supported, including indexing, slicing, and iterating with a for-range loop.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">s1</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">&#34;a&#34;</span><span class="p">,</span> <span class="s">&#34;b&#34;</span><span class="p">,</span> <span class="s">&#34;c&#34;</span><span class="p">,</span> <span class="s">&#34;d&#34;</span><span class="p">,</span> <span class="s">&#34;e&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">s2</span> <span class="o">:=</span> <span class="nx">s1</span><span class="p">[</span><span class="mi">1</span> <span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="nx">s1</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">v</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">s2</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">println</span><span class="p">(</span><span class="nx">i</span><span class="p">,</span> <span class="nx">v</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">so_Slice</span> <span class="n">s1</span> <span class="o">=</span> <span class="p">(</span><span class="n">so_Slice</span><span class="p">){(</span><span class="n">so_String</span><span class="p">[</span><span class="mi">5</span><span class="p">]){</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;a&#34;</span><span class="p">),</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;b&#34;</span><span class="p">),</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;c&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;d&#34;</span><span class="p">),</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;e&#34;</span><span class="p">)},</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">5</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="n">so_Slice</span> <span class="n">s2</span> <span class="o">=</span> <span class="nf">so_slice</span><span class="p">(</span><span class="n">so_String</span><span class="p">,</span> <span class="n">s1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="nf">so_len</span><span class="p">(</span><span class="n">s1</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="n">so_int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="nf">so_len</span><span class="p">(</span><span class="n">s2</span><span class="p">);</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_String</span> <span class="n">v</span> <span class="o">=</span> <span class="nf">so_at</span><span class="p">(</span><span class="n">so_String</span><span class="p">,</span> <span class="n">s2</span><span class="p">,</span> <span class="n">i</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_println</span><span class="p">(</span><span class="s">&#34;%&#34;</span> <span class="n">PRId64</span> <span class="s">&#34; %.*s&#34;</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">v</span><span class="p">.</span><span class="n">len</span><span class="p">,</span> <span class="n">v</span><span class="p">.</span><span class="n">ptr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>As in Go, a slice is a value type. Unlike in Go, a nil slice and an empty slice are the same thing:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">nils</span> <span class="p">[]</span><span class="kt">int</span> <span class="p">=</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">empty</span> <span class="p">[]</span><span class="kt">int</span> <span class="p">=</span> <span class="p">[]</span><span class="kt">int</span><span class="p">{}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">so_Slice</span> <span class="n">nils</span> <span class="o">=</span> <span class="p">(</span><span class="n">so_Slice</span><span class="p">){</span><span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="n">so_Slice</span> <span class="n">empty</span> <span class="o">=</span> <span class="p">(</span><span class="n">so_Slice</span><span class="p">){</span><span class="mi">0</span><span class="p">};</span>
</span></span></code></pre></div><p><code>make()</code> allocates a fixed amount of memory on the stack (<code>sizeof(T)*cap</code>). <code>append()</code> only works up to the initial capacity and panics if it's exceeded. There's no automatic reallocation; use the <code>so/slices</code> stdlib package for heap allocation and dynamic arrays.</p>
<h3 id="maps">Maps</h3>
<p>Maps are fixed-size and stack-allocated, backed by &quot;mask-step-index&quot; hashtables. They are pointer-based reference types, represented as <code>so_Map*</code> in C. No delete, no resize.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">keys</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">vals</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">cap</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">so_Map</span><span class="p">;</span>
</span></span></code></pre></div><p>Only use maps when you have a small, fixed number of items (&lt;1024). For anything else, use heap-allocated maps from the <code>so/maps</code> package.</p>
<p>Most of the standard map operations are supported, including getting/setting values and iterating with a for-range loop:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">m</span> <span class="o">:=</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">int</span><span class="p">{</span><span class="s">&#34;a&#34;</span><span class="p">:</span> <span class="mi">11</span><span class="p">,</span> <span class="s">&#34;b&#34;</span><span class="p">:</span> <span class="mi">22</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="nx">k</span><span class="p">,</span> <span class="nx">v</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">m</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">println</span><span class="p">(</span><span class="nx">k</span><span class="p">,</span> <span class="nx">v</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">so_Map</span><span class="o">*</span> <span class="n">m</span> <span class="o">=</span> <span class="o">&amp;</span><span class="p">(</span><span class="n">so_Map</span><span class="p">){(</span><span class="n">so_String</span><span class="p">[</span><span class="mi">2</span><span class="p">]){</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;a&#34;</span><span class="p">),</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;b&#34;</span><span class="p">)},</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="n">so_int</span><span class="p">[</span><span class="mi">2</span><span class="p">]){</span><span class="mi">11</span><span class="p">,</span> <span class="mi">22</span><span class="p">},</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="n">so_int</span> <span class="n">_i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">_i</span> <span class="o">&lt;</span> <span class="p">(</span><span class="n">so_int</span><span class="p">)</span><span class="n">m</span><span class="o">-&gt;</span><span class="n">len</span><span class="p">;</span> <span class="n">_i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_String</span> <span class="n">k</span> <span class="o">=</span> <span class="p">((</span><span class="n">so_String</span><span class="o">*</span><span class="p">)</span><span class="n">m</span><span class="o">-&gt;</span><span class="n">keys</span><span class="p">)[</span><span class="n">_i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">v</span> <span class="o">=</span> <span class="p">((</span><span class="n">so_int</span><span class="o">*</span><span class="p">)</span><span class="n">m</span><span class="o">-&gt;</span><span class="n">vals</span><span class="p">)[</span><span class="n">_i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_println</span><span class="p">(</span><span class="s">&#34;%.*s %&#34;</span> <span class="n">PRId64</span><span class="p">,</span> <span class="n">k</span><span class="p">.</span><span class="n">len</span><span class="p">,</span> <span class="n">k</span><span class="p">.</span><span class="n">ptr</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>As in Go, a map is a pointer type. A <code>nil</code> map emits as <code>NULL</code> in C.</p>
<h3 id="ifelse-and-for">If/else and for</h3>
<p>If-else and for come in all shapes and sizes, just like in Go.</p>
<p>Standard if-else with chaining:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="nx">x</span> <span class="p">&gt;</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">println</span><span class="p">(</span><span class="s">&#34;positive&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="nx">x</span> <span class="p">&lt;</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">println</span><span class="p">(</span><span class="s">&#34;negative&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">println</span><span class="p">(</span><span class="s">&#34;zero&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_println</span><span class="p">(</span><span class="s">&#34;%s&#34;</span><span class="p">,</span> <span class="s">&#34;positive&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_println</span><span class="p">(</span><span class="s">&#34;%s&#34;</span><span class="p">,</span> <span class="s">&#34;negative&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_println</span><span class="p">(</span><span class="s">&#34;%s&#34;</span><span class="p">,</span> <span class="s">&#34;zero&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Init statement (scoped to the if block):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="nx">num</span> <span class="o">:=</span> <span class="mi">9</span><span class="p">;</span> <span class="nx">num</span> <span class="p">&lt;</span> <span class="mi">10</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">println</span><span class="p">(</span><span class="nx">num</span><span class="p">,</span> <span class="s">&#34;has 1 digit&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">num</span> <span class="o">=</span> <span class="mi">9</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">num</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">so_println</span><span class="p">(</span><span class="s">&#34;%&#34;</span> <span class="n">PRId64</span> <span class="s">&#34; %s&#34;</span><span class="p">,</span> <span class="n">num</span><span class="p">,</span> <span class="s">&#34;has 1 digit&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Traditional for loop:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">for</span> <span class="nx">j</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">j</span> <span class="p">&lt;</span> <span class="mi">3</span><span class="p">;</span> <span class="nx">j</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">println</span><span class="p">(</span><span class="nx">j</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">for</span> <span class="p">(</span><span class="n">so_int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_println</span><span class="p">(</span><span class="s">&#34;%&#34;</span> <span class="n">PRId64</span><span class="p">,</span> <span class="n">j</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>While-style loop:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">i</span> <span class="o">:=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="nx">i</span> <span class="o">&lt;=</span> <span class="mi">3</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">println</span><span class="p">(</span><span class="nx">i</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">i</span> <span class="p">=</span> <span class="nx">i</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">so_int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="mi">3</span><span class="p">;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_println</span><span class="p">(</span><span class="s">&#34;%&#34;</span> <span class="n">PRId64</span><span class="p">,</span> <span class="n">i</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">i</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Range over an integer:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">for</span> <span class="nx">k</span> <span class="o">:=</span> <span class="k">range</span> <span class="mi">3</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">println</span><span class="p">(</span><span class="nx">k</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">for</span> <span class="p">(</span><span class="n">so_int</span> <span class="n">k</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">k</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">;</span> <span class="n">k</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_println</span><span class="p">(</span><span class="s">&#34;%&#34;</span> <span class="n">PRId64</span><span class="p">,</span> <span class="n">k</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="functions">Functions</h3>
<p>Regular functions translate to C naturally:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">sumABC</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">,</span> <span class="nx">c</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">a</span> <span class="o">+</span> <span class="nx">b</span> <span class="o">+</span> <span class="nx">c</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">so_int</span> <span class="nf">sumABC</span><span class="p">(</span><span class="n">so_int</span> <span class="n">a</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">b</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">c</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span> <span class="o">+</span> <span class="n">c</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Named function types become typedefs:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">SumFn</span> <span class="kd">func</span><span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">fn1</span> <span class="o">:=</span> <span class="nx">sumABC</span>           <span class="c1">// infer type
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">fn2</span> <span class="nx">SumFn</span> <span class="p">=</span> <span class="nx">sumABC</span>  <span class="c1">// explicit type
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">s</span> <span class="o">:=</span> <span class="nf">fn2</span><span class="p">(</span><span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">)</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// main.h
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="nf">so_int</span> <span class="p">(</span><span class="o">*</span><span class="n">main_SumFn</span><span class="p">)(</span><span class="n">so_int</span><span class="p">,</span> <span class="n">so_int</span><span class="p">,</span> <span class="n">so_int</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// main.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">main_SumFn</span> <span class="n">fn1</span> <span class="o">=</span> <span class="n">sumABC</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">main_SumFn</span> <span class="n">fn2</span> <span class="o">=</span> <span class="n">sumABC</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">so_int</span> <span class="n">s</span> <span class="o">=</span> <span class="nf">fn2</span><span class="p">(</span><span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">);</span>
</span></span></code></pre></div><p>Exported functions (capitalized) become public C symbols prefixed with the package name (<code>package_Func</code>). Unexported functions are <code>static</code>.</p>
<p>Variadic functions use the standard <code>...</code> syntax and translate to passing a slice:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">sum</span><span class="p">(</span><span class="nx">nums</span> <span class="o">...</span><span class="kt">int</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">total</span> <span class="o">:=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">num</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">nums</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">total</span> <span class="o">+=</span> <span class="nx">num</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">total</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">sum</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">so_int</span> <span class="nf">sum</span><span class="p">(</span><span class="n">so_Slice</span> <span class="n">nums</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">total</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">so_int</span> <span class="n">_</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">_</span> <span class="o">&lt;</span> <span class="nf">so_len</span><span class="p">(</span><span class="n">nums</span><span class="p">);</span> <span class="n">_</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">so_int</span> <span class="n">num</span> <span class="o">=</span> <span class="nf">so_at</span><span class="p">(</span><span class="n">so_int</span><span class="p">,</span> <span class="n">nums</span><span class="p">,</span> <span class="n">_</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">total</span> <span class="o">+=</span> <span class="n">num</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">total</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">sum</span><span class="p">((</span><span class="n">so_Slice</span><span class="p">){(</span><span class="n">so_int</span><span class="p">[</span><span class="mi">5</span><span class="p">]){</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">},</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">5</span><span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Function literals (anonymous functions and closures) are not supported.</p>
<h3 id="multiple-returns">Multiple returns</h3>
<p>So supports two-value multiple returns in two patterns: <code>(T, error)</code> and <code>(T1, T2)</code>. Both cases translate to signature-specific C types:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">divide</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span> <span class="kt">int</span><span class="p">)</span> <span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">a</span> <span class="o">/</span> <span class="nx">b</span><span class="p">,</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">divmod</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span> <span class="kt">int</span><span class="p">)</span> <span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">a</span> <span class="o">/</span> <span class="nx">b</span><span class="p">,</span> <span class="nx">a</span> <span class="o">%</span> <span class="nx">b</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> <span class="n">so_int</span> <span class="n">val</span><span class="p">;</span> <span class="n">so_Error</span> <span class="n">err</span><span class="p">;</span> <span class="p">}</span> <span class="n">so_R_int_err</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> <span class="n">so_int</span> <span class="n">val</span><span class="p">;</span> <span class="n">so_int</span> <span class="n">val2</span><span class="p">;</span> <span class="p">}</span> <span class="n">so_R_int_int</span><span class="p">;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">so_R_int_err</span> <span class="nf">divide</span><span class="p">(</span><span class="n">so_int</span> <span class="n">a</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">so_R_int_err</span><span class="p">){.</span><span class="n">val</span> <span class="o">=</span> <span class="n">a</span> <span class="o">/</span> <span class="n">b</span><span class="p">,</span> <span class="p">.</span><span class="n">err</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="n">so_R_int_int</span> <span class="nf">divmod</span><span class="p">(</span><span class="n">so_int</span> <span class="n">a</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">so_R_int_int</span><span class="p">){.</span><span class="n">val</span> <span class="o">=</span> <span class="n">a</span> <span class="o">/</span> <span class="n">b</span><span class="p">,</span> <span class="p">.</span><span class="n">val2</span> <span class="o">=</span> <span class="n">a</span> <span class="o">%</span> <span class="n">b</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Named return values are not supported.</p>
<h3 id="structs">Structs</h3>
<p>Structs translate to C naturally:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">person</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl">    <span class="nx">age</span>  <span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">bob</span> <span class="o">:=</span> <span class="nx">person</span><span class="p">{</span><span class="s">&#34;Bob&#34;</span><span class="p">,</span> <span class="mi">20</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">alice</span> <span class="o">:=</span> <span class="nx">person</span><span class="p">{</span><span class="nx">name</span><span class="p">:</span> <span class="s">&#34;Alice&#34;</span><span class="p">,</span> <span class="nx">age</span><span class="p">:</span> <span class="mi">30</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">fred</span> <span class="o">:=</span> <span class="nx">person</span><span class="p">{</span><span class="nx">name</span><span class="p">:</span> <span class="s">&#34;Fred&#34;</span><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="n">person</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_String</span> <span class="n">name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">age</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">person</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">person</span> <span class="n">bob</span> <span class="o">=</span> <span class="p">(</span><span class="n">person</span><span class="p">){</span><span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;Bob&#34;</span><span class="p">),</span> <span class="mi">20</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="n">person</span> <span class="n">alice</span> <span class="o">=</span> <span class="p">(</span><span class="n">person</span><span class="p">){.</span><span class="n">name</span> <span class="o">=</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;Alice&#34;</span><span class="p">),</span> <span class="p">.</span><span class="n">age</span> <span class="o">=</span> <span class="mi">30</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="n">person</span> <span class="n">fred</span> <span class="o">=</span> <span class="p">(</span><span class="n">person</span><span class="p">){.</span><span class="n">name</span> <span class="o">=</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;Fred&#34;</span><span class="p">)};</span>
</span></span></code></pre></div><p><code>new()</code> works with types and values:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">n</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span>                    <span class="c1">// *int, zero-initialized
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">p</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="nx">person</span><span class="p">)</span>                 <span class="c1">// *person, zero-initialized
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">n2</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span>                    <span class="c1">// *int with value 42
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">p2</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="nx">person</span><span class="p">{</span><span class="nx">name</span><span class="p">:</span> <span class="s">&#34;Alice&#34;</span><span class="p">})</span> <span class="c1">// *person with values
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">so_int</span><span class="o">*</span> <span class="n">n</span> <span class="o">=</span> <span class="o">&amp;</span><span class="p">(</span><span class="n">so_int</span><span class="p">){</span><span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="n">person</span><span class="o">*</span> <span class="n">p</span> <span class="o">=</span> <span class="o">&amp;</span><span class="p">(</span><span class="n">person</span><span class="p">){</span><span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="n">so_int</span><span class="o">*</span> <span class="n">n2</span> <span class="o">=</span> <span class="o">&amp;</span><span class="p">(</span><span class="n">so_int</span><span class="p">){</span><span class="mi">42</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="n">person</span><span class="o">*</span> <span class="n">p2</span> <span class="o">=</span> <span class="o">&amp;</span><span class="p">(</span><span class="n">person</span><span class="p">){.</span><span class="n">name</span> <span class="o">=</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;Alice&#34;</span><span class="p">)};</span>
</span></span></code></pre></div><h3 id="methods">Methods</h3>
<p>Methods are defined on struct types with pointer or value receivers:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Rect</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">r</span> <span class="o">*</span><span class="nx">Rect</span><span class="p">)</span> <span class="nf">Area</span><span class="p">()</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">r</span><span class="p">.</span><span class="nx">width</span> <span class="o">*</span> <span class="nx">r</span><span class="p">.</span><span class="nx">height</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">r</span> <span class="nx">Rect</span><span class="p">)</span> <span class="nf">resize</span><span class="p">(</span><span class="nx">x</span> <span class="kt">int</span><span class="p">)</span> <span class="nx">Rect</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">r</span><span class="p">.</span><span class="nx">height</span> <span class="o">*=</span> <span class="nx">x</span>
</span></span><span class="line"><span class="cl">    <span class="nx">r</span><span class="p">.</span><span class="nx">width</span> <span class="o">*=</span> <span class="nx">x</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">r</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Pointer receivers pass <code>void* self</code> in C and cast to the struct pointer. Value receivers pass the struct by value, so modifications operate on a copy:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="n">main_Rect</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">width</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">so_int</span> <span class="n">height</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">main_Rect</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">so_int</span> <span class="nf">main_Rect_Area</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">main_Rect</span><span class="o">*</span> <span class="n">r</span> <span class="o">=</span> <span class="p">(</span><span class="n">main_Rect</span><span class="o">*</span><span class="p">)</span><span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">r</span><span class="o">-&gt;</span><span class="n">width</span> <span class="o">*</span> <span class="n">r</span><span class="o">-&gt;</span><span class="n">height</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="n">main_Rect</span> <span class="nf">main_Rect_resize</span><span class="p">(</span><span class="n">main_Rect</span> <span class="n">r</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">r</span><span class="p">.</span><span class="n">height</span> <span class="o">*=</span> <span class="n">x</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">r</span><span class="p">.</span><span class="n">width</span> <span class="o">*=</span> <span class="n">x</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">r</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Calling methods on values and pointers emits pointers or values as necessary:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">r</span> <span class="o">:=</span> <span class="nx">Rect</span><span class="p">{</span><span class="nx">width</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nx">height</span><span class="p">:</span> <span class="mi">5</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">r</span><span class="p">.</span><span class="nf">Area</span><span class="p">()</span>      <span class="c1">// called on value (address taken automatically)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">r</span><span class="p">.</span><span class="nf">resize</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>   <span class="c1">// called on value (passed by value)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nx">rp</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">r</span>
</span></span><span class="line"><span class="cl"><span class="nx">rp</span><span class="p">.</span><span class="nf">Area</span><span class="p">()</span>     <span class="c1">// called on pointer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">rp</span><span class="p">.</span><span class="nf">resize</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>  <span class="c1">// called on pointer (dereferenced automatically)
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">main_Rect</span> <span class="n">r</span> <span class="o">=</span> <span class="p">(</span><span class="n">main_Rect</span><span class="p">){.</span><span class="n">width</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span> <span class="p">.</span><span class="n">height</span> <span class="o">=</span> <span class="mi">5</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="nf">main_Rect_Area</span><span class="p">(</span><span class="o">&amp;</span><span class="n">r</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">main_Rect_resize</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">main_Rect</span><span class="o">*</span> <span class="n">rp</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">r</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">main_Rect_Area</span><span class="p">(</span><span class="n">rp</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">main_Rect_resize</span><span class="p">(</span><span class="o">*</span><span class="n">rp</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="interfaces">Interfaces</h3>
<p>Interfaces in So are like Go interfaces, but they don't include runtime type information.</p>
<p>Interface declarations list the required methods:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Shape</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">Area</span><span class="p">()</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl">    <span class="nf">Perim</span><span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>In C, an interface is a struct with a <code>void* self</code> pointer and function pointers for each method (less efficient than using a static method table, but simpler; this might change in the future):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="n">main_Shape</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_int</span> <span class="p">(</span><span class="o">*</span><span class="n">Area</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_int</span> <span class="p">(</span><span class="o">*</span><span class="n">Perim</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">n</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">main_Shape</span><span class="p">;</span>
</span></span></code></pre></div><p>Just as in Go, a concrete type implements an interface by providing the necessary methods:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">r</span> <span class="o">*</span><span class="nx">Rect</span><span class="p">)</span> <span class="nf">Area</span><span class="p">()</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">r</span> <span class="o">*</span><span class="nx">Rect</span><span class="p">)</span> <span class="nf">Perim</span><span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">so_int</span> <span class="nf">main_Rect_Area</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">so_int</span> <span class="nf">main_Rect_Perim</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="n">so_int</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>Passing a concrete type to functions that accept interfaces:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">calcShape</span><span class="p">(</span><span class="nx">s</span> <span class="nx">Shape</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">s</span><span class="p">.</span><span class="nf">Perim</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="nx">s</span><span class="p">.</span><span class="nf">Area</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">r</span> <span class="o">:=</span> <span class="nx">Rect</span><span class="p">{</span><span class="nx">width</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nx">height</span><span class="p">:</span> <span class="mi">5</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nf">calcShape</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">r</span><span class="p">)</span>         <span class="c1">// implicit conversion
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">calcShape</span><span class="p">(</span><span class="nf">Shape</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">r</span><span class="p">))</span>  <span class="c1">// explicit conversion
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">so_int</span> <span class="nf">calcShape</span><span class="p">(</span><span class="n">main_Shape</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">s</span><span class="p">.</span><span class="nf">Perim</span><span class="p">(</span><span class="n">s</span><span class="p">.</span><span class="n">self</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="n">s</span><span class="p">.</span><span class="nf">Area</span><span class="p">(</span><span class="n">s</span><span class="p">.</span><span class="n">self</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">main_Rect</span> <span class="n">r</span> <span class="o">=</span> <span class="p">(</span><span class="n">main_Rect</span><span class="p">){.</span><span class="n">width</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span> <span class="p">.</span><span class="n">height</span> <span class="o">=</span> <span class="mi">5</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="nf">calcShape</span><span class="p">((</span><span class="n">main_Shape</span><span class="p">){.</span><span class="n">self</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">r</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">Area</span> <span class="o">=</span> <span class="n">main_Rect_Area</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">Perim</span> <span class="o">=</span> <span class="n">main_Rect_Perim</span><span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="nf">calcShape</span><span class="p">((</span><span class="n">main_Shape</span><span class="p">){.</span><span class="n">self</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">r</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">Area</span> <span class="o">=</span> <span class="n">main_Rect_Area</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">Perim</span> <span class="o">=</span> <span class="n">main_Rect_Perim</span><span class="p">});</span>
</span></span></code></pre></div><p>Type assertion works for concrete types (<code>v := iface.(*Type)</code>), but not for interfaces (<code>iface.(Interface)</code>). Type switch is not supported.</p>
<p>Empty interfaces (<code>interface{}</code> and <code>any</code>) are translated to <code>void*</code>.</p>
<h3 id="enums">Enums</h3>
<p>So supports typed constant groups as enums:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">ServerState</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">const</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nx">StateIdle</span>      <span class="nx">ServerState</span> <span class="p">=</span> <span class="s">&#34;idle&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">StateConnected</span> <span class="nx">ServerState</span> <span class="p">=</span> <span class="s">&#34;connected&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">StateError</span>     <span class="nx">ServerState</span> <span class="p">=</span> <span class="s">&#34;error&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></div><p>Each constant is emitted as a C <code>const</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// main.h
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="n">so_String</span> <span class="n">main_ServerState</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="n">main_ServerState</span> <span class="n">main_StateIdle</span> <span class="o">=</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;idle&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="n">main_ServerState</span> <span class="n">main_StateConnected</span> <span class="o">=</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;connected&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="n">main_ServerState</span> <span class="n">main_StateError</span> <span class="o">=</span> <span class="nf">so_str</span><span class="p">(</span><span class="s">&#34;error&#34;</span><span class="p">);</span>
</span></span></code></pre></div><p><code>iota</code> is supported for integer-typed constants:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Day</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">const</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Sunday</span> <span class="nx">Day</span> <span class="p">=</span> <span class="kc">iota</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Monday</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Tuesday</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></div><p>Iota values are evaluated at compile time and translated to integer literals:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="n">so_int</span> <span class="n">main_Day</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="n">main_Day</span> <span class="n">main_Sunday</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="n">main_Day</span> <span class="n">main_Monday</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="n">main_Day</span> <span class="n">main_Tuesday</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
</span></span></code></pre></div><h3 id="errors">Errors</h3>
<p>The <code>error</code> type is a regular interface with an <code>Error() string</code> method. In C, it is represented as <code>so_Error</code> an interface struct:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_String</span> <span class="p">(</span><span class="o">*</span><span class="n">Error</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">so_Error</span><span class="p">;</span>
</span></span></code></pre></div><p>Use <code>errors.New</code> to create sentinel errors at the package level:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kn">import</span> <span class="s">&#34;solod.dev/so/errors&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">ErrOutOfTea</span> <span class="p">=</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;no more tea available&#34;</span><span class="p">)</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&#34;so/errors/errors.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="n">so_Error</span> <span class="n">main_ErrOutOfTea</span> <span class="o">=</span> <span class="nf">errors_New</span><span class="p">(</span><span class="s">&#34;no more tea available&#34;</span><span class="p">);</span>
</span></span></code></pre></div><p>Errors are compared using <code>==</code>. This is an O(1) operation (compares pointers, not strings):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">makeTea</span><span class="p">(</span><span class="nx">arg</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">arg</span> <span class="o">==</span> <span class="mi">42</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">ErrOutOfTea</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">err</span> <span class="o">:=</span> <span class="nf">makeTea</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">==</span> <span class="nx">ErrOutOfTea</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">println</span><span class="p">(</span><span class="s">&#34;out of tea&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">so_Error</span> <span class="nf">makeTea</span><span class="p">(</span><span class="n">so_int</span> <span class="n">arg</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">arg</span> <span class="o">==</span> <span class="mi">42</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">main_ErrOutOfTea</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">so_Error</span> <span class="n">err</span> <span class="o">=</span> <span class="nf">makeTea</span><span class="p">(</span><span class="mi">42</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">err</span> <span class="o">==</span> <span class="n">main_ErrOutOfTea</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">so_println</span><span class="p">(</span><span class="s">&#34;%s&#34;</span><span class="p">,</span> <span class="s">&#34;out of tea&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Dynamic errors (<code>fmt.Errorf</code>) and error wrapping are not supported.</p>
<h3 id="defer">Defer</h3>
<p><code>defer</code> schedules a function or method call to run at the end of the enclosing function (as in Go):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">funcScope</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">xopen</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">state</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">defer</span> <span class="nf">xclose</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">state</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">state</span> <span class="o">!=</span> <span class="mi">1</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nb">panic</span><span class="p">(</span><span class="s">&#34;unexpected state&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Deferred calls are emitted inline (before returns, panics, and function end) in LIFO order:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">void</span> <span class="nf">funcScope</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">xopen</span><span class="p">(</span><span class="o">&amp;</span><span class="n">state</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">state</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">xclose</span><span class="p">(</span><span class="o">&amp;</span><span class="n">state</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">so_panic</span><span class="p">(</span><span class="s">&#34;unexpected state&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nf">xclose</span><span class="p">(</span><span class="o">&amp;</span><span class="n">state</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="c-interop">C interop</h3>
<p>Include a C header file with <code>so:include</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//so:include &lt;stdio.h&gt;
</span></span></span></code></pre></div><p>Declare an external C type (excluded from emission) with <code>so:extern</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//so:extern FILE
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">os_file</span> <span class="kd">struct</span><span class="p">{}</span>
</span></span></code></pre></div><p>Declare an external C function:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//so:extern
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">fopen</span><span class="p">(</span><span class="nx">path</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">mode</span> <span class="kt">string</span><span class="p">)</span> <span class="o">*</span><span class="nx">os_file</span> <span class="p">{</span> <span class="k">return</span> <span class="kc">nil</span> <span class="p">}</span>
</span></span></code></pre></div><p>When calling extern functions, <code>string</code> and <code>[]T</code> arguments are automatically decayed to their C equivalents: string literals become raw C strings (<code>&quot;hello&quot;</code>), string values become <code>char*</code>, and slices become raw pointers. This makes interop cleaner:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">f</span> <span class="o">:=</span> <span class="nf">fopen</span><span class="p">(</span><span class="s">&#34;/tmp/test.txt&#34;</span><span class="p">,</span> <span class="s">&#34;w&#34;</span><span class="p">)</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">os_file</span><span class="o">*</span> <span class="n">f</span> <span class="o">=</span> <span class="nf">fopen</span><span class="p">(</span><span class="s">&#34;/tmp/test.txt&#34;</span><span class="p">,</span> <span class="s">&#34;w&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// not like this:
</span></span></span><span class="line"><span class="cl"><span class="c1">// fopen(so_str(&#34;/tmp/test.txt&#34;), so_str(&#34;w&#34;))
</span></span></span></code></pre></div><p>The decay behavior can be turned off with the <code>nodecay</code> flag:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//so:extern nodecay
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">set_name</span><span class="p">(</span><span class="nx">acc</span> <span class="o">*</span><span class="nx">Account</span><span class="p">,</span> <span class="nx">name</span> <span class="kt">string</span><span class="p">)</span>
</span></span></code></pre></div><p>The <code>so/c</code> package includes helpers for converting C pointers back to So string and slice types. The <code>unsafe</code> package is also available and is implemented as compiler built-ins.</p>
<h3 id="packages">Packages</h3>
<p>Each Go package is translated into a single <code>.h</code> + <code>.c</code> pair, regardless of how many <code>.go</code> files it contains. Multiple <code>.go</code> files in the same package are merged into one <code>.c</code> file, separated by <code>// -- filename.go --</code> comments.</p>
<p>Exported symbols (capitalized names) are prefixed with the package name:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// geom/geom.go
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kn">package</span> <span class="nx">geom</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">const</span> <span class="nx">Pi</span> <span class="p">=</span> <span class="mf">3.14159</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">RectArea</span><span class="p">(</span><span class="nx">width</span><span class="p">,</span> <span class="nx">height</span> <span class="kt">float64</span><span class="p">)</span> <span class="kt">float64</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">width</span> <span class="o">*</span> <span class="nx">height</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Becomes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// geom.h
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">extern</span> <span class="k">const</span> <span class="kt">double</span> <span class="n">geom_Pi</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">double</span> <span class="nf">geom_RectArea</span><span class="p">(</span><span class="kt">double</span> <span class="n">width</span><span class="p">,</span> <span class="kt">double</span> <span class="n">height</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// geom.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="kt">double</span> <span class="n">geom_Pi</span> <span class="o">=</span> <span class="mf">3.14159</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">double</span> <span class="nf">geom_RectArea</span><span class="p">(</span><span class="kt">double</span> <span class="n">width</span><span class="p">,</span> <span class="kt">double</span> <span class="n">height</span><span class="p">)</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span>
</span></span></code></pre></div><p>Unexported symbols (lowercase names) keep their original names and are marked <code>static</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">double</span> <span class="nf">rectArea</span><span class="p">(</span><span class="kt">double</span> <span class="n">width</span><span class="p">,</span> <span class="kt">double</span> <span class="n">height</span><span class="p">);</span>
</span></span></code></pre></div><p>Exported symbols are declared in the <code>.h</code> file (with <code>extern</code> for variables). Unexported symbols only appear in the <code>.c</code> file.</p>
<p>Importing a So package translates to a C <code>#include</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kn">import</span> <span class="s">&#34;example/geom&#34;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&#34;geom/geom.h&#34;</span><span class="cp">
</span></span></span></code></pre></div><p>Calling imported symbols uses the package prefix:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">a</span> <span class="o">:=</span> <span class="nx">geom</span><span class="p">.</span><span class="nf">RectArea</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">_</span> <span class="p">=</span> <span class="nx">geom</span><span class="p">.</span><span class="nx">Pi</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">double</span> <span class="n">a</span> <span class="o">=</span> <span class="nf">geom_RectArea</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">geom_Pi</span><span class="p">;</span>
</span></span></code></pre></div><p>That's it for the language tour!</p>
<h2 id="compatibility">Compatibility</h2>
<p>So generates C11 code that relies on several GCC/Clang extensions:</p>
<ul>
<li>Binary literals (<code>0b1010</code>) in generated code.</li>
<li>Statement expressions (<code>({...})</code>) in macros.</li>
<li><code>__attribute__((constructor))</code> for package-level initialization.</li>
<li><code>__auto_type</code> for local type inference in generated code.</li>
<li><code>__typeof__</code> for type inference in generic macros.</li>
<li><code>alloca</code> and VLAs for <code>make()</code> and other dynamic stack allocations.</li>
</ul>
<p>You can use GCC, Clang, or <code>zig cc</code> to compile the transpiled C code. MSVC is not supported.</p>
<p>Supported operating systems: Linux, macOS, and Windows (core language only).</p>
<p>Supported platforms: amd64, arm64, riscv64, i386, wasm32, and freestanding environments.</p>
<h2 id="design-decisions">Design decisions</h2>
<p>So is highly opinionated.</p>
<p><strong>Simplicity is key</strong>. Fewer features are always better. Every new feature is strongly discouraged by default and should be added only if there are very convincing real-world use cases to support it. This applies to the standard library too — So tries to export as little of Go's stdlib API as possible while still remaining highly useful for real-world use cases.</p>
<p><strong>No heap allocations</strong> are allowed in language built-ins (like maps, slices, new, or append). Heap allocations are allowed in the standard library, but they have to be explicit. If a function or type allocates memory, it must take an allocator and clearly state ownership in the documentation.</p>
<p><strong>Fast and easy C interop</strong>. Even though So uses Go syntax, it's basically C with its own standard library. Calling C from So, and So from C, should always be simple to write and run efficiently. The So standard library (translated to C) should be easy to add to any C project.</p>
<p><strong>Performance</strong>. You can definitely write C code by hand that runs faster than code produced by So. Also, some features in So, like interfaces, are currently implemented in a way that's not the most efficient, mainly to keep things simple. Still, performance matters: the code is benchmarked and optimized to match or beat Go in speed and resource usage.</p>
<p><strong>Readability</strong>. There are several languages that claim they can transpile to readable C code. Unfortunately, the C code they generate is usually unreadable or barely readable at best. So isn't perfect in this area either (though it's arguably better than others), but it aims to produce C code that's as readable as possible.</p>
<p><strong>Go compatibility</strong>. So code is syntactically valid Go code, with no exceptions. Semantics may differ.</p>
<p>Non-goals:</p>
<p><strong>Hiding C entirely</strong>. So is a cleaner way to write C, not a replacement for it. You should be familiar with C to use So effectively.</p>
<p><strong>Go feature parity</strong>. Less is more. Iterators aren't coming, and neither are &quot;true&quot; generics.</p>
<h2 id="frequently-asked-questions">Frequently asked questions</h2>
<p>I have heard these several times, so it's worth answering.</p>
<blockquote>
<p>Why not Rust/Zig/Odin/other language?</p>
</blockquote>
<p>Because I like C and Go.</p>
<blockquote>
<p>Why not TinyGo?</p>
</blockquote>
<p>TinyGo is lightweight, but it still has a garbage collector, a runtime, and aims to support all Go features. What I'm after is something even simpler, with no runtime at all, source-level C interop, and eventually, Go's standard library ported to plain C so it can be used in regular C projects.</p>
<blockquote>
<p>How does So handle memory?</p>
</blockquote>
<p>Everything is stack-allocated by default. There's no garbage collector or reference counting. The standard library provides explicit heap allocation in the <code>so/mem</code> package when you need it.</p>
<blockquote>
<p>Is it safe?</p>
</blockquote>
<p>So itself has few safeguards other than the default Go type checking. It will panic on out-of-bounds array access, but it won't stop you from returning a dangling pointer or forgetting to free allocated memory.</p>
<p>Most memory-related problems can be caught with AddressSanitizer in modern compilers, so I recommend enabling it during development by adding <code>-fsanitize=address</code> to your <code>CFLAGS</code>.</p>
<blockquote>
<p>Is it fast?</p>
</blockquote>
<p>Usually on par with Go or faster — see the benchmark link at the end of the post for details.</p>
<blockquote>
<p>Can I use So code from C (and vice versa)?</p>
</blockquote>
<p>Yes. So compiles to plain C, therefore calling So from C is just calling C from C. Calling C from So is equally straightforward.</p>
<blockquote>
<p>Can I compile existing Go packages with So?</p>
</blockquote>
<p>Not really. Go uses automatic memory management, while So uses manual memory management. So also supports far fewer features than Go. Neither Go's standard library nor third-party packages will work with So without changes.</p>
<blockquote>
<p>How stable is this?</p>
</blockquote>
<p>Not for production at the moment.</p>
<blockquote>
<p>Where's the standard library?</p>
</blockquote>
<p>There is a growing set of high-level packages (<code>so/bytes</code>, <code>so/mem</code>, <code>so/time</code>, ...), and a low-level <code>so/c</code> package to help with C interop. Check out the standard library overview for more details.</p>
<h2 id="final-thoughts">Final thoughts</h2>
<p>Even though So isn't ready for production yet, I encourage you to try it out on a hobby project or just keep an eye on it if you like the concept.</p>
<p>Further reading:
<a href="https://github.com/solod-dev/solod#installation">Installation</a> •
<a href="https://github.com/solod-dev/solod#usage">Usage</a> •
<a href="https://github.com/solod-dev/solod/blob/main/doc/spec.md">Language tour</a> •
<a href="https://github.com/solod-dev/solod/blob/main/doc/stdlib.md">Standard library</a> •
<a href="https://github.com/solod-dev/example">So by example</a> •
<a href="https://codapi.org/so">Playground</a> •
<a href="https://github.com/solod-dev/solod/blob/main/doc/benchmarks.md">Benchmarks</a> •
<a href="https://github.com/solod-dev/solod">Source code</a></p>
]]></content:encoded></item><item><title>Allocators from C to Zig</title><link>https://antonz.org/allocators/</link><pubDate>Thu, 12 Feb 2026 12:00:00 +0000</pubDate><guid>https://antonz.org/allocators/</guid><description>Exploring allocator design in C, C3, Hare, Odin, Rust, and Zig.</description><content:encoded><![CDATA[<p>An allocator is a tool that reserves memory (typically on the heap) so a program can store its data structures there. Many C programs use the standard libc allocator, or at best, let you switch it out for another one like jemalloc or mimalloc.</p>
<p>Unlike C, modern systems languages usually treat allocators as first-class citizens. Let's look at how they handle allocation and then create a C allocator following their approach.</p>
<p><a href="#rust">Rust</a> • <a href="#zig">Zig</a> • <a href="#odin">Odin</a> • <a href="#c3">C3</a> •
<a href="#hare">Hare</a> • <a href="#c">C</a> • <a href="#final-thoughts">Final thoughts</a></p>
<h2 id="rust">Rust</h2>
<p>Rust is one of the older languages we'll be looking at, and it handles memory allocation in a more traditional way. Right now, it uses a global allocator, but there's an experimental <a href="https://doc.rust-lang.org/src/core/alloc/mod.rs.html">Allocator API</a> implemented behind a feature flag (issue <a href="https://github.com/rust-lang/rust/issues/32838">#32838</a>). We'll set the experimental API aside and focus on the stable one.</p>
<h3 id="global-allocator">Global allocator</h3>
<p>The documentation begins with a clear statement:</p>
<blockquote>
<p>In a given program, the standard library has one &quot;global&quot; memory allocator that is used for example by <code>Box&lt;T&gt;</code> and <code>Vec&lt;T&gt;</code>.</p>
</blockquote>
<p>Followed by a vague one:</p>
<blockquote>
<p>Currently the default global allocator is unspecified.</p>
</blockquote>
<p>It doesn't mean that a Rust program will abort an allocation, of course. In practice, Rust uses the system allocator as the global default (but the Rust developers don't want to commit to this, hence the &quot;unspecified&quot; note):</p>
<ul>
<li><code>malloc</code> on Unix platforms;</li>
<li><code>HeapAlloc</code> on Windows;</li>
<li><code>dlmalloc</code> in WASM.</li>
</ul>
<p>The global allocator interface is defined by the <code>GlobalAlloc</code> trait in the <code>std::alloc</code> module. It requires the implementor to provide two essential methods — <code>alloc</code> and <code>dealloc</code>, and provides two more based on them — <code>alloc_zeroed</code> and <code>realloc</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">pub</span><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="n">GlobalAlloc</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Allocates memory as described by the given `layout`.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="c1">// Returns a pointer to newly-allocated memory,
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="c1">// or null to indicate allocation failure.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">alloc</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">layout</span>: <span class="nc">Layout</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="kt">u8</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Deallocates the block of memory at the given `ptr`
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="c1">// pointer with the given `layout`.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">dealloc</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">ptr</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="n">layout</span>: <span class="nc">Layout</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Behaves like `alloc`, but also ensures that the contents
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="c1">// are set to zero before being returned.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">alloc_zeroed</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">layout</span>: <span class="nc">Layout</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="kt">u8</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Shrinks or grows a block of memory to the given `new_size` in bytes.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="c1">// The block is described by the given `ptr` pointer and `layout`.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">realloc</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">ptr</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="n">layout</span>: <span class="nc">Layout</span><span class="p">,</span><span class="w"> </span><span class="n">new_size</span>: <span class="kt">usize</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="kt">u8</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h3 id="layout">Layout</h3>
<p>The <code>Layout</code> struct describes a piece of memory we want to allocate — its size in bytes and alignment:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Layout</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// private fields
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="n">size</span>: <span class="kt">usize</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">align</span>: <span class="nc">Alignment</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><div class="boxed">
<p><strong>Memory alignment</strong></p>
<p>Alignment restricts where a piece of data can start in memory. The memory address for the data has to be a multiple of a certain number, which is always a power of 2.</p>
<p>Alignment depends on the type of data:</p>
<ul>
<li><code>u8</code>: alignment = 1. Can start at any address (0, 1, 2, 3...).</li>
<li><code>i32</code>: alignment = 4. Must start at addresses divisible by 4 (0, 4, 8, 12...).</li>
<li><code>f64</code>: alignment = 8. Must start at addresses divisible by 8 (0, 8, 16...).</li>
</ul>
<p>CPUs are designed to read &quot;aligned&quot; memory efficiently. For example, if you read a 4-byte integer starting at address 0x03 (which is unaligned), the CPU has to do two memory reads — one for the first byte and another for the other three bytes — and then combine them. But if the integer starts at address 0x04 (which is aligned), the CPU can read all four bytes at once.</p>
<p>Aligned memory is also needed for vectorized CPU operations (SIMD), where one processor instruction handles a group of values at once instead of just one.</p>
</div>
<p>The compiler knows the size and alignment for each type, so we can use the <code>Layout</code> constructor or helper functions to create a valid layout:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">alloc</span>::<span class="n">Layout</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// 64-bit integer.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">let</span><span class="w"> </span><span class="n">i64_layout</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Layout</span>::<span class="n">new</span>::<span class="o">&lt;</span><span class="kt">i64</span><span class="o">&gt;</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{:?}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">i64_layout</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// Ten 32-bit integers.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">let</span><span class="w"> </span><span class="n">array_layout</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Layout</span>::<span class="n">array</span>::<span class="o">&lt;</span><span class="kt">i32</span><span class="o">&gt;</span><span class="p">(</span><span class="mi">10</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{:?}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">array_layout</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// Custom structure.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">struct</span> <span class="nc">Cat</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">name</span>: <span class="nb">String</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">is_grumpy</span>: <span class="kt">bool</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">let</span><span class="w"> </span><span class="n">cat_layout</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Layout</span>::<span class="n">new</span>::<span class="o">&lt;</span><span class="n">Cat</span><span class="o">&gt;</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{:?}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">cat_layout</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// Layout from a value.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">let</span><span class="w"> </span><span class="n">fluffy</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Cat</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">name</span>: <span class="nb">String</span>::<span class="n">from</span><span class="p">(</span><span class="s">&#34;Fluffy&#34;</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">is_grumpy</span>: <span class="nc">true</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">let</span><span class="w"> </span><span class="n">fluffy_layout</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Layout</span>::<span class="n">for_value</span><span class="p">(</span><span class="o">&amp;</span><span class="n">fluffy</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{:?}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">fluffy_layout</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><codapi-snippet sandbox="rust" editor="basic" template="main.rs" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Layout { size: 8, align: 8 (1 &lt;&lt; 3) }
</span></span><span class="line"><span class="cl">Layout { size: 40, align: 4 (1 &lt;&lt; 2) }
</span></span><span class="line"><span class="cl">Layout { size: 32, align: 8 (1 &lt;&lt; 3) }
</span></span><span class="line"><span class="cl">Layout { size: 32, align: 8 (1 &lt;&lt; 3) }
</span></span></code></pre></div><blockquote>
<p>Don't be surprised that a <code>Cat</code> takes up 32 bytes. In Rust, the <code>String</code> type can grow, so it stores a data pointer, a length, and a capacity (3 × 8 = 24 bytes). There's also 1 byte for the boolean and 7 bytes of padding (because of 8-byte alignment), making a total of 32 bytes.</p>
</blockquote>
<h3 id="system-allocator">System allocator</h3>
<p><code>System</code> is the default memory allocator provided by the operating system. The exact implementation depends on the <a href="https://github.com/rust-lang/rust/tree/main/library/std/src/sys/alloc">platform</a>. It implements the <code>GlobalAlloc</code> trait and is used as the global allocator by default, but the documentation does not guarantee this (remember the &quot;unspecified&quot; note?). If you want to explicitly set <code>System</code> as the global allocator, you can use the <code>#[global_allocator]</code> attribute:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">alloc</span>::<span class="n">System</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[global_allocator]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">static</span><span class="w"> </span><span class="no">GLOBAL</span>: <span class="nc">System</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">System</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>You can also set a custom allocator as global, like <code>jemalloc</code> in this example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">jemallocator</span>::<span class="n">Jemalloc</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[global_allocator]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">static</span><span class="w"> </span><span class="no">GLOBAL</span>: <span class="nc">Jemalloc</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Jemalloc</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{}</span><span class="w">
</span></span></span></code></pre></div><h3 id="allocation-helpers">Allocation helpers</h3>
<p>To use the global allocator directly, call the <code>alloc</code> and <code>dealloc</code> functions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">alloc</span>::<span class="p">{</span><span class="n">alloc</span><span class="p">,</span><span class="w"> </span><span class="n">dealloc</span><span class="p">,</span><span class="w"> </span><span class="n">Layout</span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">layout</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Layout</span>::<span class="n">new</span>::<span class="o">&lt;</span><span class="kt">u16</span><span class="o">&gt;</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">ptr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">alloc</span><span class="p">(</span><span class="n">layout</span><span class="p">);</span><span class="w"> </span><span class="c1">// no OOM check for now
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="n">dealloc</span><span class="p">(</span><span class="n">ptr</span><span class="p">,</span><span class="w"> </span><span class="n">layout</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><codapi-snippet sandbox="rust" editor="basic" template="main.rs" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><p>In practice, people rarely use <code>alloc</code> or <code>dealloc</code> directly. Instead, they work with types like <code>Box</code>, <code>String</code> or <code>Vec</code> that handle allocation for them:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Box</span>::<span class="n">new</span><span class="p">(</span><span class="mi">42</span><span class="p">);</span><span class="w"> </span><span class="c1">// allocates
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{:?}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">num</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">new</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">vec</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span><span class="w"> </span><span class="c1">// allocates
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">vec</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{:?}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">vec</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// num and vec automatically deallocate
</span></span></span><span class="line"><span class="cl"><span class="c1">// when they go out of scope.
</span></span></span></code></pre></div><codapi-snippet sandbox="rust" editor="basic" template="main.rs" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">42
</span></span><span class="line"><span class="cl">[1, 2]
</span></span></code></pre></div><h3 id="error-handling">Error handling</h3>
<p>The <code>System</code> allocator doesn't abort if it can't allocate memory; instead, it returns <code>null</code> (which is exactly what <code>GlobalAlloc</code> recommends):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">alloc</span>::<span class="p">{</span><span class="n">alloc</span><span class="p">,</span><span class="w"> </span><span class="n">dealloc</span><span class="p">,</span><span class="w"> </span><span class="n">handle_alloc_error</span><span class="p">,</span><span class="w"> </span><span class="n">Layout</span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Attempt to allocate a ton of memory.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">layout</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Layout</span>::<span class="n">array</span>::<span class="o">&lt;</span><span class="kt">u8</span><span class="o">&gt;</span><span class="p">(</span><span class="kt">usize</span>::<span class="no">MAX</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">2</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">ptr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">alloc</span><span class="p">(</span><span class="n">layout</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="n">ptr</span><span class="p">.</span><span class="n">is_null</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;Out of memory!&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// Uncomment to abort.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">        </span><span class="c1">// handle_alloc_error(layout);
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;Allocation succeeded.&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">dealloc</span><span class="p">(</span><span class="n">ptr</span><span class="p">,</span><span class="w"> </span><span class="n">layout</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><codapi-snippet sandbox="rust" editor="basic" template="main.rs" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Out of memory!
</span></span></code></pre></div><p>The documentation recommends using the <code>handle_alloc_error</code> function to signal out-of-memory errors. It immediately aborts the process, or panics if the binary isn't linked to the standard library.</p>
<p>Unlike the low-level <code>alloc</code> function, types like <code>Box</code> or <code>Vec</code> call <code>handle_alloc_error</code> if allocation fails, so the program usually aborts if it runs out of memory:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">v</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">u8</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">with_capacity</span><span class="p">(</span><span class="kt">usize</span>::<span class="no">MAX</span><span class="o">/</span><span class="mi">2</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">v</span><span class="p">.</span><span class="n">len</span><span class="p">());</span><span class="w">
</span></span></span></code></pre></div><codapi-snippet sandbox="rust" editor="basic" template="main.rs" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">memory allocation of 9223372036854775807 bytes failed (exit status 139)
</span></span></code></pre></div><h3 id="further-reading">Further reading</h3>
<p><a href="https://doc.rust-lang.org/src/core/alloc/mod.rs.html">Allocator API</a> •
<a href="https://doc.rust-lang.org/src/std/alloc.rs.html">Memory allocation APIs</a></p>
<h2 id="zig">Zig</h2>
<p>Memory management in Zig is explicit. There is no default global allocator, and any function that needs to allocate memory accepts an allocator as a separate parameter. This makes the code a bit more verbose, but it matches Zig's goal of giving programmers as much control and transparency as possible.</p>
<h3 id="allocator-interface">Allocator interface</h3>
<p>An allocator in Zig is a <code>std.mem.Allocator</code> struct with an opaque self-pointer and a method table with four methods:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zig" data-lang="zig"><span class="line"><span class="cl"><span class="kr">const</span><span class="w"> </span><span class="n">Allocator</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">@This</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">ptr</span><span class="o">:</span><span class="w"> </span><span class="o">*</span><span class="n">anyopaque</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">vtable</span><span class="o">:</span><span class="w"> </span><span class="o">*</span><span class="kr">const</span><span class="w"> </span><span class="n">VTable</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">pub</span><span class="w"> </span><span class="kr">const</span><span class="w"> </span><span class="n">VTable</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">/// Return a pointer to `len` bytes with specified `alignment`,
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="c1">/// or return `null` indicating the allocation failed.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="n">alloc</span><span class="o">:</span><span class="w"> </span><span class="o">*</span><span class="kr">const</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="n">anyopaque</span><span class="p">,</span><span class="w"> </span><span class="n">len</span><span class="o">:</span><span class="w"> </span><span class="kt">usize</span><span class="p">,</span><span class="w"> </span><span class="n">alignment</span><span class="o">:</span><span class="w"> </span><span class="n">Alignment</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                      </span><span class="n">ret_addr</span><span class="o">:</span><span class="w"> </span><span class="kt">usize</span><span class="p">)</span><span class="w"> </span><span class="o">?</span><span class="p">[</span><span class="o">*</span><span class="p">]</span><span class="kt">u8</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">/// Attempt to expand or shrink memory in place.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="n">resize</span><span class="o">:</span><span class="w"> </span><span class="o">*</span><span class="kr">const</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="n">anyopaque</span><span class="p">,</span><span class="w"> </span><span class="n">memory</span><span class="o">:</span><span class="w"> </span><span class="p">[]</span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="n">alignment</span><span class="o">:</span><span class="w"> </span><span class="n">Alignment</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                       </span><span class="n">new_len</span><span class="o">:</span><span class="w"> </span><span class="kt">usize</span><span class="p">,</span><span class="w"> </span><span class="n">ret_addr</span><span class="o">:</span><span class="w"> </span><span class="kt">usize</span><span class="p">)</span><span class="w"> </span><span class="kt">bool</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">/// Attempt to expand or shrink memory, allowing relocation.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="n">remap</span><span class="o">:</span><span class="w"> </span><span class="o">*</span><span class="kr">const</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="n">anyopaque</span><span class="p">,</span><span class="w"> </span><span class="n">memory</span><span class="o">:</span><span class="w"> </span><span class="p">[]</span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="n">alignment</span><span class="o">:</span><span class="w"> </span><span class="n">Alignment</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                      </span><span class="n">new_len</span><span class="o">:</span><span class="w"> </span><span class="kt">usize</span><span class="p">,</span><span class="w"> </span><span class="n">ret_addr</span><span class="o">:</span><span class="w"> </span><span class="kt">usize</span><span class="p">)</span><span class="w"> </span><span class="o">?</span><span class="p">[</span><span class="o">*</span><span class="p">]</span><span class="kt">u8</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">/// Free and invalidate a region of memory.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="n">free</span><span class="o">:</span><span class="w"> </span><span class="o">*</span><span class="kr">const</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="n">anyopaque</span><span class="p">,</span><span class="w"> </span><span class="n">memory</span><span class="o">:</span><span class="w"> </span><span class="p">[]</span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="n">alignment</span><span class="o">:</span><span class="w"> </span><span class="n">Alignment</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                     </span><span class="n">ret_addr</span><span class="o">:</span><span class="w"> </span><span class="kt">usize</span><span class="p">)</span><span class="w"> </span><span class="kt">void</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">};</span><span class="w">
</span></span></span></code></pre></div><p>Unlike Rust's allocator methods, which take a raw pointer and a size as arguments, Zig's allocator methods take a slice of bytes (<code>[]u8</code>) — a type that combines both a pointer and a length.</p>
<p>Another interesting difference is the optional <code>ret_addr</code> parameter, which is the first return address in the allocation call stack. Some allocators, like the <code>DebugAllocator</code>, use it to keep track of which function requested memory. This helps with debugging issues related to memory allocation.</p>
<p>Just like in Rust, allocator methods don't return errors. Instead, <code>alloc</code> and <code>remap</code> return <code>null</code> if they fail.</p>
<h3 id="allocation-helpers">Allocation helpers</h3>
<p>Zig also provides type-safe wrappers that you can use instead of calling the allocator methods directly:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zig" data-lang="zig"><span class="line"><span class="cl"><span class="c1">// Allocate / deallocate a single object.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">pub</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="n">create</span><span class="p">(</span><span class="n">a</span><span class="o">:</span><span class="w"> </span><span class="n">Allocator</span><span class="p">,</span><span class="w"> </span><span class="kr">comptime</span><span class="w"> </span><span class="n">T</span><span class="o">:</span><span class="w"> </span><span class="kt">type</span><span class="p">)</span><span class="w"> </span><span class="n">Error</span><span class="o">!*</span><span class="n">T</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">pub</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="n">destroy</span><span class="p">(</span><span class="n">self</span><span class="o">:</span><span class="w"> </span><span class="n">Allocator</span><span class="p">,</span><span class="w"> </span><span class="n">ptr</span><span class="o">:</span><span class="w"> </span><span class="n">anytype</span><span class="p">)</span><span class="w"> </span><span class="kt">void</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// Allocate / deallocate multiple objects.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">pub</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="n">alloc</span><span class="p">(</span><span class="n">self</span><span class="o">:</span><span class="w"> </span><span class="n">Allocator</span><span class="p">,</span><span class="w"> </span><span class="kr">comptime</span><span class="w"> </span><span class="n">T</span><span class="o">:</span><span class="w"> </span><span class="kt">type</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="o">:</span><span class="w"> </span><span class="kt">usize</span><span class="p">)</span><span class="w"> </span><span class="n">Error</span><span class="o">!</span><span class="p">[]</span><span class="n">T</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">pub</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="n">free</span><span class="p">(</span><span class="n">self</span><span class="o">:</span><span class="w"> </span><span class="n">Allocator</span><span class="p">,</span><span class="w"> </span><span class="n">memory</span><span class="o">:</span><span class="w"> </span><span class="n">anytype</span><span class="p">)</span><span class="w"> </span><span class="kt">void</span><span class="w">
</span></span></span></code></pre></div><p>Example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zig" data-lang="zig"><span class="line"><span class="cl"><span class="kr">const</span><span class="w"> </span><span class="n">allocator</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">heap</span><span class="p">.</span><span class="n">page_allocator</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// Create and destroy a single integer.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="n">allocator</span><span class="p">.</span><span class="n">create</span><span class="p">(</span><span class="kt">i32</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">num</span><span class="p">.</span><span class="o">*</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">42</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">allocator</span><span class="p">.</span><span class="n">destroy</span><span class="p">(</span><span class="n">num</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// Allocate and free a slice of 10 bytes.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span><span class="w"> </span><span class="n">slice</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="n">allocator</span><span class="p">.</span><span class="n">alloc</span><span class="p">(</span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="mi">100</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nb">@memset</span><span class="p">(</span><span class="n">slice</span><span class="p">,</span><span class="w"> </span><span class="s">&#39;A&#39;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">allocator</span><span class="p">.</span><span class="n">free</span><span class="p">(</span><span class="n">slice</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><codapi-snippet sandbox="zig" editor="basic" template="main.zig" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><p>Unlike the allocator methods, these allocation functions return an error if they fail.</p>
<p>If a function or method allocates memory, it expects the developer to provide an allocator instance:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zig" data-lang="zig"><span class="line"><span class="cl"><span class="kr">const</span><span class="w"> </span><span class="n">allocator</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">heap</span><span class="p">.</span><span class="n">page_allocator</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">var</span><span class="w"> </span><span class="n">list</span><span class="o">:</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">ArrayList</span><span class="p">(</span><span class="kt">u8</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">.</span><span class="n">empty</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">defer</span><span class="w"> </span><span class="n">list</span><span class="p">.</span><span class="n">deinit</span><span class="p">(</span><span class="n">allocator</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">try</span><span class="w"> </span><span class="n">list</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">allocator</span><span class="p">,</span><span class="w"> </span><span class="s">&#39;z&#39;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">try</span><span class="w"> </span><span class="n">list</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">allocator</span><span class="p">,</span><span class="w"> </span><span class="s">&#39;i&#39;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">try</span><span class="w"> </span><span class="n">list</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">allocator</span><span class="p">,</span><span class="w"> </span><span class="s">&#39;g&#39;</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><codapi-snippet sandbox="zig" editor="basic" template="main.zig" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><h3 id="standard-allocators">Standard allocators</h3>
<p>Zig's standard library includes several built-in allocators in the <code>std.heap</code> namespace.</p>
<p><code>page_allocator</code> asks the operating system for entire pages of memory, each allocation is a syscall:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zig" data-lang="zig"><span class="line"><span class="cl"><span class="kr">const</span><span class="w"> </span><span class="n">allocator</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">heap</span><span class="p">.</span><span class="n">page_allocator</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">const</span><span class="w"> </span><span class="n">memory</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="n">allocator</span><span class="p">.</span><span class="n">alloc</span><span class="p">(</span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="mi">100</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">allocator</span><span class="p">.</span><span class="n">free</span><span class="p">(</span><span class="n">memory</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><codapi-snippet sandbox="zig" editor="basic" template="main.zig" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><p><code>FixedBufferAllocator</code> allocates memory into a fixed buffer and doesn't make any heap allocations:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zig" data-lang="zig"><span class="line"><span class="cl"><span class="kr">var</span><span class="w"> </span><span class="n">buffer</span><span class="o">:</span><span class="w"> </span><span class="p">[</span><span class="mi">1000</span><span class="p">]</span><span class="kt">u8</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">undefined</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">var</span><span class="w"> </span><span class="n">fba</span><span class="o">:</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">heap</span><span class="p">.</span><span class="n">FixedBufferAllocator</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">.</span><span class="n">init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">buffer</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">const</span><span class="w"> </span><span class="n">allocator</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">fba</span><span class="p">.</span><span class="n">allocator</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">const</span><span class="w"> </span><span class="n">memory</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="n">allocator</span><span class="p">.</span><span class="n">alloc</span><span class="p">(</span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="mi">100</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">allocator</span><span class="p">.</span><span class="n">free</span><span class="p">(</span><span class="n">memory</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><codapi-snippet sandbox="zig" editor="basic" template="main.zig" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><p><code>ArenaAllocator</code> wraps a child allocator and allows you to allocate many times and only free once:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zig" data-lang="zig"><span class="line"><span class="cl"><span class="kr">var</span><span class="w"> </span><span class="n">arena</span><span class="o">:</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">heap</span><span class="p">.</span><span class="n">ArenaAllocator</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">.</span><span class="n">init</span><span class="p">(</span><span class="n">std</span><span class="p">.</span><span class="n">heap</span><span class="p">.</span><span class="n">page_allocator</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">defer</span><span class="w"> </span><span class="n">arena</span><span class="p">.</span><span class="n">deinit</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">const</span><span class="w"> </span><span class="n">allocator</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">arena</span><span class="p">.</span><span class="n">allocator</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">const</span><span class="w"> </span><span class="n">mem1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="n">allocator</span><span class="p">.</span><span class="n">alloc</span><span class="p">(</span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="mi">100</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">const</span><span class="w"> </span><span class="n">mem2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="n">allocator</span><span class="p">.</span><span class="n">alloc</span><span class="p">(</span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="mi">100</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">allocator</span><span class="p">.</span><span class="n">free</span><span class="p">(</span><span class="n">mem1</span><span class="p">);</span><span class="w"> </span><span class="c1">// not needed
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">allocator</span><span class="p">.</span><span class="n">free</span><span class="p">(</span><span class="n">mem2</span><span class="p">);</span><span class="w"> </span><span class="c1">// not needed
</span></span></span></code></pre></div><codapi-snippet sandbox="zig" editor="basic" template="main.zig" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><p>The <code>arena.deinit()</code> call frees all memory. Individual <code>allocator.free()</code> calls are no-ops.</p>
<p><code>DebugAllocator</code> (aka <code>GeneralPurposeAllocator</code>) is a safe allocator that can prevent double-free, use-after-free and can detect leaks:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zig" data-lang="zig"><span class="line"><span class="cl"><span class="kr">var</span><span class="w"> </span><span class="n">gpa</span><span class="o">:</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">heap</span><span class="p">.</span><span class="n">DebugAllocator</span><span class="p">(.{})</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">.</span><span class="n">init</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">const</span><span class="w"> </span><span class="n">allocator</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">gpa</span><span class="p">.</span><span class="n">allocator</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">const</span><span class="w"> </span><span class="n">memory</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="n">allocator</span><span class="p">.</span><span class="n">alloc</span><span class="p">(</span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="mi">100</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">allocator</span><span class="p">.</span><span class="n">free</span><span class="p">(</span><span class="n">memory</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">allocator</span><span class="p">.</span><span class="n">free</span><span class="p">(</span><span class="n">memory</span><span class="p">);</span><span class="w"> </span><span class="c1">// aborts
</span></span></span></code></pre></div><p><code>SmpAllocator</code> is a general-purpose thread-safe allocator designed for maximum performance on multithreaded machines:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zig" data-lang="zig"><span class="line"><span class="cl"><span class="kr">const</span><span class="w"> </span><span class="n">allocator</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">heap</span><span class="p">.</span><span class="n">smp_allocator</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">const</span><span class="w"> </span><span class="n">memory</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="n">allocator</span><span class="p">.</span><span class="n">alloc</span><span class="p">(</span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="mi">100</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">allocator</span><span class="p">.</span><span class="n">free</span><span class="p">(</span><span class="n">memory</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><codapi-snippet sandbox="zig" editor="basic" template="main.zig" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><p><code>c_allocator</code> is a wrapper around the libc allocator:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zig" data-lang="zig"><span class="line"><span class="cl"><span class="kr">const</span><span class="w"> </span><span class="n">allocator</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">heap</span><span class="p">.</span><span class="n">c_allocator</span><span class="p">;</span><span class="w"> </span><span class="c1">// requires linking libc
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span><span class="w"> </span><span class="n">memory</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="n">allocator</span><span class="p">.</span><span class="n">alloc</span><span class="p">(</span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="mi">100</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">allocator</span><span class="p">.</span><span class="n">free</span><span class="p">(</span><span class="n">memory</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><h3 id="error-handling">Error handling</h3>
<p>Zig doesn't panic or abort when it can't allocate memory. An allocation failure is just a regular error that you're expected to handle:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zig" data-lang="zig"><span class="line"><span class="cl"><span class="kr">const</span><span class="w"> </span><span class="n">allocator</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">heap</span><span class="p">.</span><span class="n">page_allocator</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">const</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">math</span><span class="p">.</span><span class="n">maxInt</span><span class="p">(</span><span class="kt">i64</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">const</span><span class="w"> </span><span class="n">memory</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">allocator</span><span class="p">.</span><span class="n">alloc</span><span class="p">(</span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="o">|</span><span class="n">err</span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">err</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="k">error</span><span class="p">.</span><span class="n">OutOfMemory</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">print</span><span class="p">(</span><span class="s">&#34;Out of memory!</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="p">.{});</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">err</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">defer</span><span class="w"> </span><span class="n">allocator</span><span class="p">.</span><span class="n">free</span><span class="p">(</span><span class="n">memory</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><codapi-snippet sandbox="zig" editor="basic" template="main.zig" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Out of memory!
</span></span></code></pre></div><h3 id="further-reading">Further reading</h3>
<p><a href="https://zig.guide/standard-library/allocators">Allocators</a> •
<a href="https://codeberg.org/ziglang/zig/src/branch/master/lib/std/mem/Allocator.zig">std.mem.Allocator</a> •
<a href="https://codeberg.org/ziglang/zig/src/branch/master/lib/std/heap.zig">std.heap</a></p>
<h2 id="odin">Odin</h2>
<p>Odin supports explicit allocators, but, unlike Zig, it's not the only option. In Odin, every scope has an implicit <code>context</code> variable that provides a default allocator:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="cl"><span class="n">Context</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="n">allocator</span><span class="o">:</span><span class="w">          </span><span class="n">Allocator</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="n">temp_allocator</span><span class="o">:</span><span class="w">     </span><span class="n">Allocator</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// Returns the default `context` for each scope
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="na">@(require_results)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">default_context</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">proc</span><span class="w"> </span><span class="s">&#34;contextless&#34;</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">Context</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="n">c</span><span class="o">:</span><span class="w"> </span><span class="n">Context</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="n">__init_context</span><span class="p">(</span><span class="o">&amp;</span><span class="n">c</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="n">c</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>If you don't pass an allocator to a function, it uses the one currently set in the context.</p>
<h3 id="allocator-interface">Allocator interface</h3>
<p>An allocator in Odin is a <code>runtime.Allocator</code> struct with an opaque self-pointer and a single function pointer:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="cl"><span class="n">Allocator_Mode</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">enum</span><span class="w"> </span><span class="kt">byte</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="n">Alloc</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="n">Free</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="n">Resize</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">Allocator_Error</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">enum</span><span class="w"> </span><span class="kt">byte</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="n">None</span><span class="w">                 </span><span class="o">=</span><span class="w"> </span><span class="nx">0</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="n">Out_Of_Memory</span><span class="w">        </span><span class="o">=</span><span class="w"> </span><span class="nx">1</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">Allocator_Proc</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="nd">#type</span><span class="w"> </span><span class="kd">proc</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">allocator_data</span><span class="o">:</span><span class="w"> </span><span class="kt">rawptr</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">mode</span><span class="o">:</span><span class="w"> </span><span class="n">Allocator_Mode</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">size</span><span class="p">,</span><span class="w"> </span><span class="n">alignment</span><span class="o">:</span><span class="w"> </span><span class="kt">int</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">old_memory</span><span class="o">:</span><span class="w"> </span><span class="kt">rawptr</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">old_size</span><span class="o">:</span><span class="w"> </span><span class="kt">int</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">location</span><span class="o">:</span><span class="w"> </span><span class="n">Source_Code_Location</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nd">#caller_location</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">)</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span><span class="w"> </span><span class="n">Allocator_Error</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">Allocator</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="n">procedure</span><span class="o">:</span><span class="w"> </span><span class="n">Allocator_Proc</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="n">data</span><span class="o">:</span><span class="w">      </span><span class="kt">rawptr</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>Unlike other languages, Odin's allocator uses a single procedure for all allocation tasks. The specific action — like allocating, resizing, or freeing memory — is decided by the <code>mode</code> parameter.</p>
<p>The allocation procedure returns the allocated memory (for <code>.Alloc</code> and <code>.Resize</code> operations) and an error (<code>.None</code> on success).</p>
<h3 id="allocation-helpers">Allocation helpers</h3>
<p>Odin provides low-level wrapper functions in the <code>core:mem</code> package that call the allocator procedure using a specific mode:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="cl"><span class="n">alloc</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">proc</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">size</span><span class="o">:</span><span class="w"> </span><span class="kt">int</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">alignment</span><span class="o">:</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">DEFAULT_ALIGNMENT</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">allocator</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">context</span><span class="p">.</span><span class="n">allocator</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">loc</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nd">#caller_location</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">)</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="kt">rawptr</span><span class="p">,</span><span class="w"> </span><span class="n">runtime</span><span class="p">.</span><span class="n">Allocator_Error</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">free</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">proc</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">ptr</span><span class="o">:</span><span class="w"> </span><span class="kt">rawptr</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">allocator</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">context</span><span class="p">.</span><span class="n">allocator</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">loc</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nd">#caller_location</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">)</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">runtime</span><span class="p">.</span><span class="n">Allocator_Error</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// and others
</span></span></span></code></pre></div><p>There are also type-safe builtins like <code>new</code>/<code>free</code> (for a single object) and <code>make</code>/<code>delete</code> (for multiple objects) that you can use instead of the low-level interface:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="cl"><span class="n">num</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">new</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">defer</span><span class="w"> </span><span class="n">free</span><span class="p">(</span><span class="n">num</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">slice</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">make</span><span class="p">([]</span><span class="kt">int</span><span class="p">,</span><span class="w"> </span><span class="nx">100</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">defer</span><span class="w"> </span><span class="k">delete</span><span class="p">(</span><span class="n">slice</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><codapi-snippet sandbox="odin" editor="basic" template="main.odin" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><p>By default, all builtins use the context allocator, but you can pass a custom allocator as an optional parameter:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="cl"><span class="n">ptr</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">new</span><span class="p">(</span><span class="kt">int</span><span class="p">,</span><span class="w"> </span><span class="n">allocator</span><span class="o">=</span><span class="k">context</span><span class="p">.</span><span class="n">allocator</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">defer</span><span class="w"> </span><span class="n">free</span><span class="p">(</span><span class="n">ptr</span><span class="p">,</span><span class="w"> </span><span class="n">allocator</span><span class="o">=</span><span class="k">context</span><span class="p">.</span><span class="n">allocator</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">slice</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">make</span><span class="p">([]</span><span class="kt">int</span><span class="p">,</span><span class="w"> </span><span class="nx">10</span><span class="p">,</span><span class="w"> </span><span class="n">allocator</span><span class="o">=</span><span class="k">context</span><span class="p">.</span><span class="n">allocator</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">defer</span><span class="w"> </span><span class="k">delete</span><span class="p">(</span><span class="n">slice</span><span class="p">,</span><span class="w"> </span><span class="n">allocator</span><span class="o">=</span><span class="k">context</span><span class="p">.</span><span class="n">allocator</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><codapi-snippet sandbox="odin" editor="basic" template="main.odin" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><p>To use a different allocator for a specific block of code, you can reassign it in the context:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="cl"><span class="n">alloc</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="n">custom_allocator</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">context</span><span class="p">.</span><span class="n">allocator</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">alloc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// Uses the custom allocator.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">ptr</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">new</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">defer</span><span class="w"> </span><span class="n">free</span><span class="p">(</span><span class="n">ptr</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><h3 id="temp-allocator">Temp allocator</h3>
<p>Odin's <code>context</code> provides two different allocators:</p>
<ul>
<li><code>context.allocator</code> is for general-purpose allocations. It uses the operating system's heap allocator.</li>
<li><code>context.temp_allocator</code> is for short-lived allocations. It uses a scratch allocator (a kind of growing arena).</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="cl"><span class="c1">// Temporary allocation (no manual free required).
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">temp_mem</span><span class="p">,</span><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="n">mem</span><span class="p">.</span><span class="n">alloc</span><span class="p">(</span><span class="nx">100</span><span class="p">,</span><span class="w"> </span><span class="n">allocator</span><span class="o">=</span><span class="k">context</span><span class="p">.</span><span class="n">temp_allocator</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// Persistent allocation (requires manual free).
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">perm_mem</span><span class="p">,</span><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="n">mem</span><span class="p">.</span><span class="n">alloc</span><span class="p">(</span><span class="nx">100</span><span class="p">,</span><span class="w"> </span><span class="n">allocator</span><span class="o">=</span><span class="k">context</span><span class="p">.</span><span class="n">allocator</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">defer</span><span class="w"> </span><span class="n">mem</span><span class="p">.</span><span class="n">free</span><span class="p">(</span><span class="n">perm_mem</span><span class="p">,</span><span class="w"> </span><span class="k">context</span><span class="p">.</span><span class="n">allocator</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// Clear the entire scratchpad at the end of the work cycle.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">free_all</span><span class="p">(</span><span class="k">context</span><span class="p">.</span><span class="n">temp_allocator</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><codapi-snippet sandbox="odin" editor="basic" template="main.odin" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><p>When using the temp allocator, you only need a single <code>free_all</code> call to clear all the allocated memory.</p>
<h3 id="standard-allocators">Standard allocators</h3>
<p>Odin's standard library includes several allocators, found in the <code>base:runtime</code> and <code>core:mem</code> packages.</p>
<p>The <code>heap_allocator</code> procedure returns a general-purpose allocator:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="cl"><span class="n">allocator</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="n">runtime</span><span class="p">.</span><span class="n">heap_allocator</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">memory</span><span class="p">,</span><span class="w"> </span><span class="n">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="n">mem</span><span class="p">.</span><span class="n">alloc</span><span class="p">(</span><span class="nx">100</span><span class="p">,</span><span class="w"> </span><span class="n">allocator</span><span class="o">=</span><span class="n">allocator</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">mem</span><span class="p">.</span><span class="n">free</span><span class="p">(</span><span class="n">memory</span><span class="p">,</span><span class="w"> </span><span class="n">allocator</span><span class="o">=</span><span class="n">allocator</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><codapi-snippet sandbox="odin" editor="basic" template="main.odin" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><p><code>Arena</code> uses a single backing buffer for allocations, allowing you to allocate many times and only free once:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="cl"><span class="n">arena</span><span class="o">:</span><span class="w"> </span><span class="n">mem</span><span class="p">.</span><span class="n">Arena</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">buffer</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span><span class="w"> </span><span class="nx">1024</span><span class="p">,</span><span class="w"> </span><span class="n">runtime</span><span class="p">.</span><span class="n">heap_allocator</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">mem</span><span class="p">.</span><span class="n">arena_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">arena</span><span class="p">,</span><span class="w"> </span><span class="n">buffer</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">defer</span><span class="w"> </span><span class="n">mem</span><span class="p">.</span><span class="n">arena_free_all</span><span class="p">(</span><span class="o">&amp;</span><span class="n">arena</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">allocator</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="n">mem</span><span class="p">.</span><span class="n">arena_allocator</span><span class="p">(</span><span class="o">&amp;</span><span class="n">arena</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">err1</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="n">mem</span><span class="p">.</span><span class="n">alloc</span><span class="p">(</span><span class="nx">100</span><span class="p">,</span><span class="w"> </span><span class="n">allocator</span><span class="o">=</span><span class="n">allocator</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">m2</span><span class="p">,</span><span class="w"> </span><span class="n">err2</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="n">mem</span><span class="p">.</span><span class="n">alloc</span><span class="p">(</span><span class="nx">100</span><span class="p">,</span><span class="w"> </span><span class="n">allocator</span><span class="o">=</span><span class="n">allocator</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><codapi-snippet sandbox="odin" editor="basic" template="main.odin" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><p><code>Tracking_Allocator</code> detects leaks and invalid memory access, similar to <code>DebugAllocator</code> in Zig:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="cl"><span class="n">track</span><span class="o">:</span><span class="w"> </span><span class="n">mem</span><span class="p">.</span><span class="n">Tracking_Allocator</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">mem</span><span class="p">.</span><span class="n">tracking_allocator_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">track</span><span class="p">,</span><span class="w"> </span><span class="n">runtime</span><span class="p">.</span><span class="n">default_allocator</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">defer</span><span class="w"> </span><span class="n">mem</span><span class="p">.</span><span class="n">tracking_allocator_destroy</span><span class="p">(</span><span class="o">&amp;</span><span class="n">track</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">allocator</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="n">mem</span><span class="p">.</span><span class="n">tracking_allocator</span><span class="p">(</span><span class="o">&amp;</span><span class="n">track</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">memory</span><span class="p">,</span><span class="w"> </span><span class="n">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="n">mem</span><span class="p">.</span><span class="n">alloc</span><span class="p">(</span><span class="nx">100</span><span class="p">,</span><span class="w"> </span><span class="n">allocator</span><span class="o">=</span><span class="n">allocator</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">free</span><span class="p">(</span><span class="n">memory</span><span class="p">,</span><span class="w"> </span><span class="n">allocator</span><span class="o">=</span><span class="n">allocator</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">free</span><span class="p">(</span><span class="n">memory</span><span class="p">,</span><span class="w"> </span><span class="n">allocator</span><span class="o">=</span><span class="n">allocator</span><span class="p">)</span><span class="w"> </span><span class="c1">// aborts
</span></span></span></code></pre></div><codapi-snippet sandbox="odin" editor="basic" template="main.odin" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Tracking allocator error: Bad free of pointer 139851252672688 (exit status 132)
</span></span></code></pre></div><p>There are also others, such as <code>Stack</code> or <code>Buddy_Allocator</code>.</p>
<h3 id="error-handling">Error handling</h3>
<p>Like Zig, Odin doesn't panic or abort when it can't allocate memory. Instead, it returns an error code as the second return value:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="cl"><span class="n">data</span><span class="p">,</span><span class="w"> </span><span class="n">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="n">mem</span><span class="p">.</span><span class="n">alloc</span><span class="p">(</span><span class="nx">1</span><span class="w"> </span><span class="o">&lt;&lt;</span><span class="w"> </span><span class="nx">62</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">if</span><span class="w"> </span><span class="n">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="p">.</span><span class="n">None</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">fmt</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="s">&#34;Allocation failed:&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">err</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">defer</span><span class="w"> </span><span class="n">mem</span><span class="p">.</span><span class="n">free</span><span class="p">(</span><span class="n">data</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><codapi-snippet sandbox="odin" editor="basic" template="main.odin" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Allocation failed: Out_Of_Memory
</span></span></code></pre></div><h3 id="further-reading">Further reading</h3>
<p><a href="https://odin-lang.org/docs/overview/#allocators">Allocators</a> •
<a href="https://pkg.odin-lang.org/base/runtime/">base:runtime</a> •
<a href="https://pkg.odin-lang.org/core/mem/">core:mem</a></p>
<h2 id="c3">C3</h2>
<p>Like Zig and Odin, C3 supports explicit allocators. Like Odin, C3 provides two default allocators: heap and temp.</p>
<h3 id="allocator-interface">Allocator interface</h3>
<p>An allocator in C3 is a <code>core::mem::allocator::Allocator</code> interface with an additional option of zeroing or not zeroing the allocated memory:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">enum AllocInitType
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">	NO_ZERO,
</span></span><span class="line"><span class="cl">	ZERO
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">interface Allocator
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">	&lt;*
</span></span><span class="line"><span class="cl">	 Acquire memory from the allocator, with the given
</span></span><span class="line"><span class="cl">     alignment and initialization type.
</span></span><span class="line"><span class="cl">	*&gt;
</span></span><span class="line"><span class="cl">	fn void*? acquire(usz size, AllocInitType init_type, usz alignment = 0);
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	&lt;*
</span></span><span class="line"><span class="cl">	 Resize acquired memory from the allocator,
</span></span><span class="line"><span class="cl">     with the given new size and alignment.
</span></span><span class="line"><span class="cl">	*&gt;
</span></span><span class="line"><span class="cl">	fn void*? resize(void* ptr, usz new_size, usz alignment = 0);
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	&lt;*
</span></span><span class="line"><span class="cl">	 Release memory acquired using `acquire` or `resize`.
</span></span><span class="line"><span class="cl">	*&gt;
</span></span><span class="line"><span class="cl">	fn void release(void* ptr, bool aligned);
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Unlike Zig and Odin, the <code>resize</code> and <code>release</code> methods don't take the (old) size as a parameter — neither directly like Odin nor through a slice like Zig. This makes it a bit harder to create custom allocators because the allocator has to keep track of the size along with the allocated memory. On the other hand, this approach makes C interop easier (if you use the default C3 allocator): data allocated in C can be freed in C3 without needing to pass the size parameter from the C code.</p>
<p>Like in Odin, allocator methods return an error if they fail.</p>
<h3 id="allocation-helpers">Allocation helpers</h3>
<p>C3 provides low-level wrapper macros in the <code>core::mem::allocator</code> module that call allocator methods:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">macro void* malloc(Allocator allocator, usz size)
</span></span><span class="line"><span class="cl">macro void*? malloc_try(Allocator allocator, usz size)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">macro void* realloc(Allocator allocator, void* ptr, usz new_size)
</span></span><span class="line"><span class="cl">macro void*? realloc_try(Allocator allocator, void* ptr, usz new_size)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">macro void free(Allocator allocator, void* ptr)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">// and others
</span></span></code></pre></div><p>These either return an error (the <code>_try</code>-suffix macros) or abort if they fail.</p>
<p>Example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">// `mem` is the global allocator instance.
</span></span><span class="line"><span class="cl">int* ptr = allocator::malloc(mem, int.sizeof);
</span></span><span class="line"><span class="cl">defer allocator::free(mem, ptr);
</span></span></code></pre></div><codapi-snippet sandbox="c3" editor="basic" template="main.c3" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><p>There are also functions and macros with similar names in the <code>core::mem</code> module that use the global <code>allocator::mem</code> allocator instance:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">// Call the core::mem::allocator macros directly.
</span></span><span class="line"><span class="cl">fn void* malloc(usz size)
</span></span><span class="line"><span class="cl">fn void free(void* ptr)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">// Accept a type instead of a size.
</span></span><span class="line"><span class="cl">macro new($Type, #init = ...)
</span></span><span class="line"><span class="cl">macro alloc($Type)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">// Allocate multiple objects.
</span></span><span class="line"><span class="cl">macro new_array($Type, usz elements)
</span></span><span class="line"><span class="cl">macro alloc_array($Type, usz elements)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">// and others
</span></span></code></pre></div><p>Example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">// `malloc` and `free` are builtins,
</span></span><span class="line"><span class="cl">// so they don&#39;t require the namespace.
</span></span><span class="line"><span class="cl">int* num = malloc(int.sizeof);
</span></span><span class="line"><span class="cl">defer free(num);
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">// `new_array` requires the namespace.
</span></span><span class="line"><span class="cl">int[] slice = mem::new_array(int, 100);
</span></span><span class="line"><span class="cl">defer free(slice);
</span></span></code></pre></div><codapi-snippet sandbox="c3" editor="basic" template="main.c3" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><p>If a function or method allocates memory, it often expects the developer to provide an allocator instance:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">List{int} list;
</span></span><span class="line"><span class="cl">list.init(mem); // use the heap allocator
</span></span><span class="line"><span class="cl">defer list.free();
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">list.push(11);
</span></span><span class="line"><span class="cl">list.push(22);
</span></span><span class="line"><span class="cl">list.push(33);
</span></span></code></pre></div><codapi-snippet sandbox="c3" editor="basic" template="main.c3" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><h3 id="temp-allocator">Temp allocator</h3>
<p>C3 provides two thread-local allocator instances:</p>
<ul>
<li><code>allocator::mem</code> is for general-purpose allocations. It uses a operating system's heap allocator (typically a libc wrapper).</li>
<li><code>allocator::tmem</code> is for short-lived allocations. It uses an arena allocator.</li>
</ul>
<p>There are functions and macros in the <code>core::mem</code> module that use the <code>allocator::tmem</code> temporary allocator:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">// Calls the core::mem::allocator macro directly.
</span></span><span class="line"><span class="cl">fn void* tmalloc(usz size, usz alignment = 0)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">// Accept a type instead of a size.
</span></span><span class="line"><span class="cl">macro tnew($Type, #init = ...)
</span></span><span class="line"><span class="cl">macro talloc($Type)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">// Allocate multiple objects.
</span></span><span class="line"><span class="cl">macro talloc_array($Type, usz elements)
</span></span></code></pre></div><p>To <code>@pool</code> macro releases all temporary allocations when leaving the scope:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">@pool()
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">    int* p1 = tmalloc(int.sizeof);
</span></span><span class="line"><span class="cl">    int* p2 = tmalloc(int.sizeof);
</span></span><span class="line"><span class="cl">    int* p3 = tmalloc(int.sizeof);
</span></span><span class="line"><span class="cl">    // no manual free required
</span></span><span class="line"><span class="cl">};  // p1, p2, p3 are freed here
</span></span></code></pre></div><codapi-snippet sandbox="c3" editor="basic" template="main.c3" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><p>Some types, like <code>List</code> or <code>DString</code>, use the temp allocator by default if they are not initialized:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">@pool()
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">    List{int} list;
</span></span><span class="line"><span class="cl">    list.push(11);  // implicitly initialize with the temp allocator
</span></span><span class="line"><span class="cl">    list.push(22);
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    DString str;
</span></span><span class="line"><span class="cl">    str.appendf(&#34;Hello %s&#34;, &#34;World&#34;);  // same
</span></span><span class="line"><span class="cl">};
</span></span></code></pre></div><codapi-snippet sandbox="c3" editor="basic" template="main.c3" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><h3 id="standard-allocators">Standard allocators</h3>
<p>C3's standard library includes several built-in allocators, found in the <code>core::mem::allocator</code> module.</p>
<p><code>LibcAllocator</code> is a wrapper around libc's malloc/free:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">LibcAllocator libc;
</span></span><span class="line"><span class="cl">char* memory = allocator::malloc(&amp;libc, 100*char.sizeof);
</span></span><span class="line"><span class="cl">allocator::free(&amp;libc, memory);
</span></span></code></pre></div><codapi-snippet sandbox="c3" editor="basic" template="main.c3" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><p><code>ArenaAllocator</code> uses a single backing buffer for allocations, allowing you to allocate many times and only free once:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">char[1024] buf;
</span></span><span class="line"><span class="cl">ArenaAllocator* arena = allocator::wrap(&amp;buf);
</span></span><span class="line"><span class="cl">defer arena.clear();
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">char* m1 = allocator::malloc(arena, 100*char.sizeof);
</span></span><span class="line"><span class="cl">char* m2 = allocator::malloc(arena, 100*char.sizeof);
</span></span></code></pre></div><codapi-snippet sandbox="c3" editor="basic" template="main.c3" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><p><code>TrackingAllocator</code> detects leaks and invalid memory access:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">TrackingAllocator track;
</span></span><span class="line"><span class="cl">track.init(mem);
</span></span><span class="line"><span class="cl">defer track.clear();
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">char* memory = allocator::malloc(&amp;track, 100*char.sizeof);
</span></span><span class="line"><span class="cl">allocator::free(&amp;track, memory);
</span></span><span class="line"><span class="cl">allocator::free(&amp;track, memory); // aborts
</span></span></code></pre></div><codapi-snippet sandbox="c3" editor="basic" template="main.c3" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ERROR: &#39;Attempt to release untracked pointer 0x55f5b0333330, this is likely a bug.&#39;
</span></span></code></pre></div><p>There are also others, such as <code>BackedArenaAllocator</code> or <code>OnStackAllocator</code>.</p>
<h3 id="error-handling">Error handling</h3>
<p>Like Zig and Odin, C3 can return an error in case of allocation failure:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">void*? data = allocator::malloc_try(mem, 1uLL &lt;&lt; 62);
</span></span><span class="line"><span class="cl">if (catch err = data) {
</span></span><span class="line"><span class="cl">    io::printfn(&#34;Allocation failed: %s&#34;, err);
</span></span><span class="line"><span class="cl">    return;
</span></span><span class="line"><span class="cl">};
</span></span><span class="line"><span class="cl">defer mem::free(data);
</span></span></code></pre></div><codapi-snippet sandbox="c3" editor="basic" template="main.c3" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Allocation failed: mem::OUT_OF_MEMORY
</span></span></code></pre></div><p>C3 can also abort in case of allocation failure:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">void* data = allocator::malloc(mem, 1uLL &lt;&lt; 62);
</span></span><span class="line"><span class="cl">// void* data = malloc(1uLL &lt;&lt; 62); // same thing
</span></span><span class="line"><span class="cl">defer free(data);
</span></span></code></pre></div><codapi-snippet sandbox="c3" editor="basic" template="main.c3" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ERROR: &#39;Unexpected fault &#39;mem::OUT_OF_MEMORY&#39; was unwrapped!&#39;
</span></span></code></pre></div><p>Since the functions and macros in the <code>core::mem</code> module use <code>allocator::malloc</code> instead of <code>allocator::malloc_try</code>, it looks like aborting on failure is the preferred approach.</p>
<h3 id="further-reading">Further reading</h3>
<p><a href="https://c3-lang.org/language-common/memory/">Memory Handling</a> •
<a href="https://github.com/c3lang/c3c/blob/master/lib/std/core/mem_allocator.c3">core::mem::alocator</a> •
<a href="https://github.com/c3lang/c3c/blob/master/lib/std/core/mem.c3">core::mem</a></p>
<h2 id="hare">Hare</h2>
<p>Unlike other languages, Hare doesn't support explicit allocators. The standard library has multiple allocator implementations, but only one of them is used at runtime.</p>
<h3 id="global-allocator">Global allocator</h3>
<p>Hare's compiler expects the runtime to provide <code>malloc</code> and <code>free</code> implementations:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hare" data-lang="hare"><span class="line"><span class="cl"><span class="k">fn</span><span class="w"> </span><span class="n">malloc</span><span class="p">(</span><span class="n">n</span><span class="o">:</span><span class="w"> </span><span class="kt">size</span><span class="p">)</span><span class="w"> </span><span class="kt">nullable</span><span class="w"> </span><span class="o">*</span><span class="kt">opaque</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nd">@symbol</span><span class="p">(</span><span class="s">&#34;rt.free&#34;</span><span class="p">)</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="n">free_</span><span class="p">(</span><span class="n">_p</span><span class="o">:</span><span class="w"> </span><span class="kt">nullable</span><span class="w"> </span><span class="o">*</span><span class="kt">opaque</span><span class="p">)</span><span class="w"> </span><span class="kt">void</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>The programmer isn't supposed to access them directly (although it's possible by importing <code>rt</code> and calling <code>rt::malloc</code> or <code>rt::free</code>). Instead, Hare uses them to provide higher-level allocation helpers.</p>
<h3 id="allocation-helpers">Allocation helpers</h3>
<p>Hare offers two high-level allocation helpers that use the global allocator internally: <code>alloc</code> and <code>free</code>.</p>
<p><code>alloc</code> can allocate individual objects. It takes a value, not a type:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hare" data-lang="hare"><span class="line"><span class="cl"><span class="k">let</span><span class="w"> </span><span class="n">n</span><span class="o">:</span><span class="w"> </span><span class="o">*</span><span class="kt">int</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">alloc</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span><span class="o">!</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">defer</span><span class="w"> </span><span class="k">free</span><span class="p">(</span><span class="n">n</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">let</span><span class="w"> </span><span class="n">s</span><span class="o">:</span><span class="w"> </span><span class="o">*</span><span class="kt">str</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">alloc</span><span class="p">(</span><span class="s">&#34;hello world&#34;</span><span class="p">)</span><span class="o">!</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">defer</span><span class="w"> </span><span class="k">free</span><span class="p">(</span><span class="n">s</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// coords is defined as struct { x: int, y: int }
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">let</span><span class="w"> </span><span class="n">p</span><span class="o">:</span><span class="w"> </span><span class="o">*</span><span class="n">coords</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">alloc</span><span class="p">(</span><span class="n">coords</span><span class="p">{</span><span class="n">x</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="o">=</span><span class="mi">5</span><span class="p">})</span><span class="o">!</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">defer</span><span class="w"> </span><span class="k">free</span><span class="p">(</span><span class="n">p</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><codapi-snippet sandbox="hare" editor="basic" template="main.hare" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><p><code>alloc</code> can also allocate slices if you provide a second parameter (the number of items):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hare" data-lang="hare"><span class="line"><span class="cl"><span class="c1">// Allocate a slice of 100 integers.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">let</span><span class="w"> </span><span class="n">nums</span><span class="o">:</span><span class="w"> </span><span class="p">[]</span><span class="kt">int</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">alloc</span><span class="p">([</span><span class="mi">0</span><span class="o">...</span><span class="p">],</span><span class="w"> </span><span class="mi">100</span><span class="p">)</span><span class="o">!</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">defer</span><span class="w"> </span><span class="k">free</span><span class="p">(</span><span class="n">nums</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><codapi-snippet sandbox="hare" editor="basic" template="main.hare" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><p><code>free</code> works correctly with both pointers to single objects (like <code>*int</code>) and slices (like <code>[]int</code>).</p>
<h3 id="standard-allocators">Standard allocators</h3>
<p>Hare's standard library has three built-in memory allocators:</p>
<ul>
<li>The default allocator is based on the algorithm from the <a href="https://www.cs.princeton.edu/~appel/papers/memmgr.pdf">Verified sequential malloc/free</a> paper.</li>
<li>The libc allocator uses the operating system's malloc and free functions from libc.</li>
<li>The debug allocator uses a simple mmap-based method for memory allocation.</li>
</ul>
<p>The allocator that's actually used is selected at compile time.</p>
<h3 id="error-handling">Error handling</h3>
<p>Like other languages, Hare returns an error in case of allocation failure:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hare" data-lang="hare"><span class="line"><span class="cl"><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="k">alloc</span><span class="p">([</span><span class="mi">0</span><span class="o">...</span><span class="p">],</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&lt;&lt;</span><span class="w"> </span><span class="mi">62</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">case</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">nums</span><span class="o">:</span><span class="w"> </span><span class="p">[]</span><span class="kt">int</span><span class="w"> </span><span class="o">=&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">defer</span><span class="w"> </span><span class="k">free</span><span class="p">(</span><span class="n">nums</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">case</span><span class="w"> </span><span class="n">nomem</span><span class="w"> </span><span class="o">=&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">fmt</span><span class="o">::</span><span class="n">println</span><span class="p">(</span><span class="s">&#34;Out of memory&#34;</span><span class="p">)</span><span class="o">!</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">};</span><span class="w">
</span></span></span></code></pre></div><codapi-snippet sandbox="hare" editor="basic" template="main.hare" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Out of memory
</span></span></code></pre></div><p>You can abort on error with <code>!</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hare" data-lang="hare"><span class="line"><span class="cl"><span class="k">let</span><span class="w"> </span><span class="n">nums</span><span class="o">:</span><span class="w"> </span><span class="p">[]</span><span class="kt">int</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">alloc</span><span class="p">([</span><span class="mi">0</span><span class="o">...</span><span class="p">],</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&lt;&lt;</span><span class="w"> </span><span class="mi">62</span><span class="p">)</span><span class="o">!</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">defer</span><span class="w"> </span><span class="k">free</span><span class="p">(</span><span class="n">nums</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><codapi-snippet sandbox="hare" editor="basic" template="main.hare" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Aborted (core dumped) (exit status 134)
</span></span></code></pre></div><p>Or propagate the error with <code>?</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hare" data-lang="hare"><span class="line"><span class="cl"><span class="k">let</span><span class="w"> </span><span class="n">nums</span><span class="o">:</span><span class="w"> </span><span class="p">[]</span><span class="kt">int</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">alloc</span><span class="p">([</span><span class="mi">0</span><span class="o">...</span><span class="p">],</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&lt;&lt;</span><span class="w"> </span><span class="mi">62</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">defer</span><span class="w"> </span><span class="k">free</span><span class="p">(</span><span class="n">nums</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><h3 id="further-reading">Further reading</h3>
<p><a href="https://harelang.org/tutorials/introduction/#dynamic-memory-allocation--defer">Dynamic memory allocation</a> •
<a href="https://git.sr.ht/~sircmpwn/hare/tree/master/item/rt/malloc.ha">malloc.ha</a></p>
<h2 id="c">C</h2>
<p>Many C programs use the standard libc allocator, or at most, let you swap it out for another one using macros:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define LIB_MALLOC malloc
</span></span></span><span class="line"><span class="cl"><span class="cp">#define LIB_FREE free
</span></span></span></code></pre></div><p>Or using a simple setter:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="o">*</span><span class="p">(</span><span class="o">*</span><span class="n">_lib_malloc</span><span class="p">)(</span><span class="kt">size_t</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="nf">void</span> <span class="p">(</span><span class="o">*</span><span class="n">_lib_free</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">lib_set_allocator</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">(</span><span class="o">*</span><span class="n">malloc</span><span class="p">)(</span><span class="kt">size_t</span><span class="p">),</span> <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">free</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">_lib_malloc</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">_lib_free</span> <span class="o">=</span> <span class="n">free</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>While this might work for switching the libc allocator to jemalloc or mimalloc, it's not very flexible. For example, trying to implement an arena allocator with this kind of API is almost impossible.</p>
<p>Now that we've seen the modern allocator design in Zig, Odin, and C3 — let's try building something similar in C. There are a lot of small choices to make, and I'm going with what I personally prefer. I'm not saying this is the only way to design an allocator — it's just one way out of many.</p>
<h3 id="allocator-interface">Allocator interface</h3>
<p>Our allocator should return an error instead of <code>NULL</code> if it fails, so we'll need an error enum:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Allocation errors.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">enum</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Error_None</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">Error_OutOfMemory</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">Error_SizeOverflow</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">Error</span><span class="p">;</span>
</span></span></code></pre></div><p>The allocation function needs to return either a tagged union (value | error) or a tuple (value, error). Since C doesn't have these built in, let's use a custom tuple type:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Allocation result.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">ptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">Error</span> <span class="n">err</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">AllocResult</span><span class="p">;</span>
</span></span></code></pre></div><p>The next step is the allocator interface. I think Odin's approach of using a single function makes the implementation more complicated than it needs to be, so let's create separate methods like Zig does:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Allocator interface.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">struct</span> <span class="n">_Allocator</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">AllocResult</span> <span class="p">(</span><span class="o">*</span><span class="n">alloc</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">size</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">align</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">AllocResult</span> <span class="p">(</span><span class="o">*</span><span class="n">realloc</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">ptr</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">oldSize</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                           <span class="kt">size_t</span> <span class="n">newSize</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">align</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">free</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">ptr</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">size</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">align</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="k">struct</span> <span class="n">_Allocator</span><span class="o">*</span> <span class="n">m</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">Allocator</span><span class="p">;</span>
</span></span></code></pre></div><blockquote>
<p>This approach to interface design is explained in detail in a separate post: <a href="/interfaces-in-c/">Interfaces in C</a>.</p>
</blockquote>
<p>Zig uses byte slices (<code>[]u8</code>) instead of raw memory pointers. We could make our own byte slice type, but I don't see any real advantage to doing that in C — it would just mean more type casting. So let's keep it simple and stick with <code>void*</code> like our ancestors did.</p>
<h3 id="allocation-helpers">Allocation helpers</h3>
<p>Now let's create generic <code>Alloc</code> and <code>Free</code> wrappers:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Allocates an item of type T.
</span></span></span><span class="line"><span class="cl"><span class="c1">// `AllocResult Alloc[T](Allocator a, T)`
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define Alloc(a, T) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    ((a).m-&gt;alloc((a).self, sizeof(T), alignof(T)))
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// Frees an item allocated with Alloc.
</span></span></span><span class="line"><span class="cl"><span class="c1">// Only accepts typed pointers, not void*.
</span></span></span><span class="line"><span class="cl"><span class="c1">// `void Free[T](Allocator a, T* ptr)`
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define Free(a, ptr) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    ((a).m-&gt;free((a).self, (ptr), sizeof(*(ptr)), alignof(typeof(*(ptr)))))
</span></span></span></code></pre></div><p>I'm taking <code>typeof</code> for granted here to keep things simple. A more robust implementation should properly check if it is available or pass the type to <code>Free</code> directly.</p>
<p>We can even create a separate pair of helpers for collections:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Helper to prevent integer overflow during N-item allocation.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kr">inline</span> <span class="kt">size_t</span> <span class="nf">calcSize</span><span class="p">(</span><span class="kt">size_t</span> <span class="n">size</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">count</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">count</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">size</span> <span class="o">&gt;</span> <span class="n">SIZE_MAX</span> <span class="o">/</span> <span class="n">count</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">size</span> <span class="o">*</span> <span class="n">count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Allocates n items of type T.
</span></span></span><span class="line"><span class="cl"><span class="c1">// `AllocResult AllocN[T](Allocator a, T, size_t n)`
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define AllocN(a, T, n) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    ((a).m-&gt;alloc((a).self, calcSize(sizeof(T), (n)), alignof(T)))
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// Frees n items allocated with AllocN.
</span></span></span><span class="line"><span class="cl"><span class="c1">// Only accepts typed pointers, not void*.
</span></span></span><span class="line"><span class="cl"><span class="c1">// `void FreeN[T](Allocator a, T* ptr, size_t n)`
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define FreeN(a, ptr, n)               \
</span></span></span><span class="line"><span class="cl"><span class="cp">    ((a).m-&gt;free(                      \
</span></span></span><span class="line"><span class="cl"><span class="cp">        (a).self, (ptr),               \
</span></span></span><span class="line"><span class="cl"><span class="cp">        calcSize(sizeof(*(ptr)), (n)), \
</span></span></span><span class="line"><span class="cl"><span class="cp">        alignof(typeof(*(ptr)))))
</span></span></span></code></pre></div><p>We could use some <code>__VA_ARGS__</code> macro tricks to make <code>Alloc</code> and <code>Free</code> work for both a single object and a collection. But let's not do that — I prefer to avoid heavy-magic macros in this post.</p>
<h3 id="libc-allocator">Libc allocator</h3>
<p>As for the custom allocators, let's start with a libc wrapper. It's not particularly interesting, since it ignores most of the parameters, but still:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// The libc allocator wrapper.
</span></span></span><span class="line"><span class="cl"><span class="c1">// Ignores alignment and treats zero-size allocations as errors.
</span></span></span><span class="line"><span class="cl"><span class="c1">// Doesn&#39;t support reallocation to keep things simple.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">AllocResult</span> <span class="nf">Libc_Alloc</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">size</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">align</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">align</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">size</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="p">(</span><span class="n">AllocResult</span><span class="p">){</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">Error_SizeOverflow</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">ptr</span> <span class="o">=</span> <span class="nf">malloc</span><span class="p">(</span><span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">ptr</span><span class="p">)</span> <span class="k">return</span> <span class="p">(</span><span class="n">AllocResult</span><span class="p">){</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">Error_OutOfMemory</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">AllocResult</span><span class="p">){</span><span class="n">ptr</span><span class="p">,</span> <span class="n">Error_None</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">Libc_Free</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">ptr</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">size</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">align</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">align</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">free</span><span class="p">(</span><span class="n">ptr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Allocator</span> <span class="nf">LibcAllocator</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">static</span> <span class="k">const</span> <span class="k">struct</span> <span class="n">_Allocator</span> <span class="n">mtab</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">alloc</span> <span class="o">=</span> <span class="n">Libc_Alloc</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">free</span> <span class="o">=</span> <span class="n">Libc_Free</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">Allocator</span><span class="p">){.</span><span class="n">m</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">mtab</span><span class="p">,</span> <span class="p">.</span><span class="n">self</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet id="s-libc-alloc" editor="basic">
</codapi-snippet>
<p>Usage example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Allocator</span> <span class="n">allocator</span> <span class="o">=</span> <span class="nf">LibcAllocator</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Allocate a single integer.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">AllocResult</span> <span class="n">res</span> <span class="o">=</span> <span class="nf">Alloc</span><span class="p">(</span><span class="n">allocator</span><span class="p">,</span> <span class="kt">int64_t</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="n">err</span> <span class="o">!=</span> <span class="n">Error_None</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Error: %d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">res</span><span class="p">.</span><span class="n">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kt">int64_t</span><span class="o">*</span> <span class="n">x</span> <span class="o">=</span> <span class="n">res</span><span class="p">.</span><span class="n">ptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="o">*</span><span class="n">x</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nf">Free</span><span class="p">(</span><span class="n">allocator</span><span class="p">,</span> <span class="n">x</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Allocate an array of integers.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="kt">size_t</span> <span class="n">n</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">AllocResult</span> <span class="n">res</span> <span class="o">=</span> <span class="nf">AllocN</span><span class="p">(</span><span class="n">allocator</span><span class="p">,</span> <span class="kt">int64_t</span><span class="p">,</span> <span class="n">n</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="n">err</span> <span class="o">!=</span> <span class="n">Error_None</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Error: %d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">res</span><span class="p">.</span><span class="n">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kt">int64_t</span><span class="o">*</span> <span class="n">arr</span> <span class="o">=</span> <span class="n">res</span><span class="p">.</span><span class="n">ptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">arr</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nf">FreeN</span><span class="p">(</span><span class="n">allocator</span><span class="p">,</span> <span class="n">arr</span><span class="p">,</span> <span class="n">n</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet sandbox="gcc" editor="basic" depends-on="s-libc-alloc" template="header.c" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><h3 id="arena-allocator">Arena allocator</h3>
<p>Now let's use that <code>self</code> field to implement an arena allocator backed by a fixed-size buffer:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// A simple arena allocator.
</span></span></span><span class="line"><span class="cl"><span class="c1">// Doesn&#39;t support reallocation.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">buf</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="n">cap</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="n">offset</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">Arena</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Arena</span> <span class="nf">NewArena</span><span class="p">(</span><span class="kt">uint8_t</span><span class="o">*</span> <span class="n">buf</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">cap</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">Arena</span><span class="p">){.</span><span class="n">buf</span> <span class="o">=</span> <span class="n">buf</span><span class="p">,</span> <span class="p">.</span><span class="n">cap</span> <span class="o">=</span> <span class="n">cap</span><span class="p">,</span> <span class="p">.</span><span class="n">offset</span> <span class="o">=</span> <span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="n">AllocResult</span> <span class="nf">Arena_Alloc</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">size</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">align</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Arena</span><span class="o">*</span> <span class="n">arena</span> <span class="o">=</span> <span class="p">(</span><span class="n">Arena</span><span class="o">*</span><span class="p">)</span><span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. Calculate the alignment padding.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">size</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="p">(</span><span class="n">AllocResult</span><span class="p">){</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">Error_SizeOverflow</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uintptr_t</span> <span class="n">currentPtr</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uintptr_t</span><span class="p">)</span><span class="n">arena</span><span class="o">-&gt;</span><span class="n">buf</span> <span class="o">+</span> <span class="n">arena</span><span class="o">-&gt;</span><span class="n">offset</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uintptr_t</span> <span class="n">alignedPtr</span> <span class="o">=</span> <span class="p">(</span><span class="n">currentPtr</span> <span class="o">+</span> <span class="p">(</span><span class="n">align</span> <span class="o">-</span> <span class="mi">1</span><span class="p">))</span> <span class="o">&amp;</span> <span class="o">~</span><span class="p">(</span><span class="n">align</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="n">newOffset</span> <span class="o">=</span> <span class="p">(</span><span class="n">alignedPtr</span> <span class="o">-</span> <span class="p">(</span><span class="kt">uintptr_t</span><span class="p">)</span><span class="n">arena</span><span class="o">-&gt;</span><span class="n">buf</span><span class="p">)</span> <span class="o">+</span> <span class="n">size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. Check for errors.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">newOffset</span> <span class="o">&lt;</span> <span class="n">arena</span><span class="o">-&gt;</span><span class="n">offset</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">(</span><span class="n">AllocResult</span><span class="p">){</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">Error_SizeOverflow</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">newOffset</span> <span class="o">&gt;</span> <span class="n">arena</span><span class="o">-&gt;</span><span class="n">cap</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">(</span><span class="n">AllocResult</span><span class="p">){</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">Error_OutOfMemory</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 3. Commit the allocation.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">arena</span><span class="o">-&gt;</span><span class="n">offset</span> <span class="o">=</span> <span class="n">newOffset</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">AllocResult</span><span class="p">){(</span><span class="kt">void</span><span class="o">*</span><span class="p">)</span><span class="n">alignedPtr</span><span class="p">,</span> <span class="n">Error_None</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">Arena_Free</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">ptr</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">size</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">align</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Individual deallocations are no-ops.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">ptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">align</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">Arena_Reset</span><span class="p">(</span><span class="n">Arena</span><span class="o">*</span> <span class="n">arena</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">arena</span><span class="o">-&gt;</span><span class="n">offset</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Allocator</span> <span class="nf">Arena_Allocator</span><span class="p">(</span><span class="n">Arena</span><span class="o">*</span> <span class="n">arena</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">static</span> <span class="k">const</span> <span class="k">struct</span> <span class="n">_Allocator</span> <span class="n">mtab</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">alloc</span> <span class="o">=</span> <span class="n">Arena_Alloc</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">free</span> <span class="o">=</span> <span class="n">Arena_Free</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">Allocator</span><span class="p">){.</span><span class="n">m</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">mtab</span><span class="p">,</span> <span class="p">.</span><span class="n">self</span> <span class="o">=</span> <span class="n">arena</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet id="s-arena-alloc" editor="basic">
</codapi-snippet>
<p>Usage example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">buf</span><span class="p">[</span><span class="mi">1024</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">Arena</span> <span class="n">arena</span> <span class="o">=</span> <span class="nf">NewArena</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">Allocator</span> <span class="n">allocator</span> <span class="o">=</span> <span class="nf">Arena_Allocator</span><span class="p">(</span><span class="o">&amp;</span><span class="n">arena</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Allocate a single integer.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">AllocResult</span> <span class="n">res</span> <span class="o">=</span> <span class="nf">Alloc</span><span class="p">(</span><span class="n">allocator</span><span class="p">,</span> <span class="kt">int64_t</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="n">err</span> <span class="o">!=</span> <span class="n">Error_None</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Error: %d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">res</span><span class="p">.</span><span class="n">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kt">int64_t</span><span class="o">*</span> <span class="n">x</span> <span class="o">=</span> <span class="n">res</span><span class="p">.</span><span class="n">ptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="o">*</span><span class="n">x</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// No need for Free.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Allocate an array of integers.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="kt">size_t</span> <span class="n">n</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">AllocResult</span> <span class="n">res</span> <span class="o">=</span> <span class="nf">AllocN</span><span class="p">(</span><span class="n">allocator</span><span class="p">,</span> <span class="kt">int64_t</span><span class="p">,</span> <span class="n">n</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="n">err</span> <span class="o">!=</span> <span class="n">Error_None</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Error: %d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">res</span><span class="p">.</span><span class="n">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kt">int64_t</span><span class="o">*</span> <span class="n">arr</span> <span class="o">=</span> <span class="n">res</span><span class="p">.</span><span class="n">ptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">arr</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// No need for FreeN.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">Arena_Reset</span><span class="p">(</span><span class="o">&amp;</span><span class="n">arena</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet sandbox="gcc" editor="basic" depends-on="s-arena-alloc" template="header.c" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ok
</span></span></code></pre></div><p>Nice!</p>
<h3 id="error-handling">Error handling</h3>
<p>As shown in the examples above, the allocation method returns an error if something goes wrong. While checking for errors might not be as convenient as it is in Zig or Odin, it's still pretty straightforward:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Allocator</span> <span class="n">allocator</span> <span class="o">=</span> <span class="nf">LibcAllocator</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="n">n</span> <span class="o">=</span> <span class="n">SIZE_MAX</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">AllocResult</span> <span class="n">res</span> <span class="o">=</span> <span class="nf">AllocN</span><span class="p">(</span><span class="n">allocator</span><span class="p">,</span> <span class="kt">int64_t</span><span class="p">,</span> <span class="n">n</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="n">err</span> <span class="o">!=</span> <span class="n">Error_None</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Allocation failed: %d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">res</span><span class="p">.</span><span class="n">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">FreeN</span><span class="p">(</span><span class="n">allocator</span><span class="p">,</span> <span class="n">res</span><span class="p">.</span><span class="n">ptr</span><span class="p">,</span> <span class="n">n</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet sandbox="gcc" editor="basic" depends-on="s-libc-alloc" template="header.c" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Allocation failed: 2 (exit status 1)
</span></span></code></pre></div><p><a href="alloc.c">source</a></p>
<h2 id="final-thoughts">Final thoughts</h2>
<p>Here's an informal table comparing allocation APIs in the languages we've discussed:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">          Single object   Collection
</span></span><span class="line"><span class="cl">        ┌──────────────────────────────────────────┐
</span></span><span class="line"><span class="cl">Rust    │ Box::new(42)    vec![0; 100]             │
</span></span><span class="line"><span class="cl">        │                                          │
</span></span><span class="line"><span class="cl">Zig     │ a.create(i32)   a.alloc(i32, 100)        │
</span></span><span class="line"><span class="cl">        │                                          │
</span></span><span class="line"><span class="cl">Odin    │ new(int)        make([]int, 100)         │
</span></span><span class="line"><span class="cl">        │ new(int, a)     make([]int, 100, a)      │
</span></span><span class="line"><span class="cl">        │                                          │
</span></span><span class="line"><span class="cl">C3      │ mem::new(int)   mem::new_array(int, 100) │
</span></span><span class="line"><span class="cl">        │                                          │
</span></span><span class="line"><span class="cl">Hare    │ alloc(42)       alloc([0...], 100)       │
</span></span><span class="line"><span class="cl">        │                                          │
</span></span><span class="line"><span class="cl">C       │ Alloc(a, int)   AllocN(a, int, 100)      │
</span></span><span class="line"><span class="cl">        └──────────────────────────────────────────┘
</span></span></code></pre></div><p>In Zig, you always have to specify the allocator. In Odin, passing an allocator is optional. In C3, some functions require you to pass an allocator, while others just use the global one. In Hare, there's a single global allocator.</p>
<p>As we've seen, there's nothing magical about the allocators used in modern languages. While they're definitely more ergonomic and safe than C, there's nothing stopping us from using the same techniques in plain C.</p>
<script defer src="/modules/codapi/snippet.js"></script>
]]></content:encoded></item><item><title>(Un)portable defer in C</title><link>https://antonz.org/defer-in-c/</link><pubDate>Thu, 05 Feb 2026 12:00:00 +0000</pubDate><guid>https://antonz.org/defer-in-c/</guid><description>Eight ways to implement defer in C.</description><content:encoded><![CDATA[<p>Modern system programming languages, from Hare to Zig, seem to agree that <code>defer</code> is a must-have feature. It's hard to argue with that, because <code>defer</code> makes it much easier to free memory and other resources correctly, which is crucial in languages without garbage collection.</p>
<p>The situation in C is different. There was a <a href="https://open-std.org/jtc1/sc22/wg14/www/docs/n2895.htm">N2895 proposal</a> by Jens Gustedt and Robert Seacord in 2021, but it was not accepted for C23. Now, there's another <a href="https://thephd.dev/_vendor/future_cxx/technical%20specification/C%20-%20defer/C%20-%20defer%20Technical%20Specification.pdf">N3734 proposal</a> by JeanHeyd Meneide, which will probably be accepted in the next standard version.</p>
<p>Since <code>defer</code> isn't part of the standard, people have created lots of different implementations. Let's take a quick look at them and see if we can find the best one.</p>
<p><a href="#c23gcc">C23/GCC</a> •
<a href="#c11gcc">C11/GCC</a> •
<a href="#gccclang">GCC/Clang</a> •
<a href="#msvc">MSVC</a> •
<a href="#long-jump">Long jump</a> •
<a href="#for-loop">For loop</a> •
<a href="#stack">Stack</a> •
<a href="#simplified-gccclang">Simplified GCC/Clang</a> •
<a href="#final-thoughts">Final thoughts</a></p>
<h2 id="c23gcc">C23/GCC</h2>
<p><a href="https://gustedt.wordpress.com/2025/01/06/simple-defer-ready-to-use/">Jens Gustedt</a> offers this brief version:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define defer __DEFER(__COUNTER__)
</span></span></span><span class="line"><span class="cl"><span class="cp">#define __DEFER(N) __DEFER_(N)
</span></span></span><span class="line"><span class="cl"><span class="cp">#define __DEFER_(N) __DEFER__(__DEFER_FUNCTION_##N, __DEFER_VARIABLE_##N)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#define __DEFER__(F, V)        \
</span></span></span><span class="line"><span class="cl"><span class="cp">    auto void F(int*);         \
</span></span></span><span class="line"><span class="cl"><span class="cp">    [[gnu::cleanup(F)]] int V; \
</span></span></span><span class="line"><span class="cl"><span class="cp">    auto void F(int*)
</span></span></span></code></pre></div><codapi-snippet id="s-gcc23" editor="basic">
</codapi-snippet>
<p>Usage example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">loud_free</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">p</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;freeing %p</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">free</span><span class="p">(</span><span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span><span class="o">*</span> <span class="n">p</span> <span class="o">=</span> <span class="nf">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">p</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">defer</span> <span class="p">{</span> <span class="nf">loud_free</span><span class="p">(</span><span class="n">p</span><span class="p">);</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;p = %d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="o">*</span><span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet sandbox="gcc" editor="basic" depends-on="s-gcc23" template="header.c" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">p = 42
</span></span><span class="line"><span class="cl">freeing 0x127e05b30
</span></span></code></pre></div><p>This approach combines C23 attribute syntax (<code>[[attribute]]</code>) with GCC-specific features: nested functions (<code>auto void F(int*)</code>) and the <code>cleanup</code> attribute. It also uses the non-standard <code>__COUNTER__</code> macro (supported by GCC, Clang, and MSVC), which expands to an automatically increasing integer value.</p>
<div class="boxed">
<p><strong>Nested functions and cleanup in GCC</strong></p>
<p>A <em>nested function</em> (also known as a local function) is a function defined inside another function:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">outer</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">inner</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">x</span> <span class="o">+=</span> <span class="mi">10</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">inner</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Nested functions can access variables from the enclosing scope, similar to closures in other languages, but they are not first-class citizens and cannot be passed around like function pointers.</p>
<p>The <code>cleanup</code> attribute runs a function when the variable goes out of scope:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">safe_free</span><span class="p">(</span><span class="kt">int</span> <span class="o">**</span><span class="n">ptr</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">ptr</span> <span class="o">||</span> <span class="o">!*</span><span class="n">ptr</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">free</span><span class="p">(</span><span class="o">*</span><span class="n">ptr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">__attribute__</span><span class="p">((</span><span class="nf">cleanup</span><span class="p">(</span><span class="n">safe_free</span><span class="p">)))</span> <span class="kt">int</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="nf">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">p</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// safe_free(&amp;p) will be called automatically
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// when p goes out of scope.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>The function should take one parameter, which is a pointer to a type that's compatible with the variable. If the function returns a value, it will be ignored.</p>
</div>
<p>On the plus side, this version works just like you'd expect <code>defer</code> to work. On the downside, it's only available in C23+ and only works with GCC (not even Clang supports it, because of the nested function).</p>
<p>Another downside is that using nested functions requires an <em>executable stack</em>, which security experts strongly discourage.</p>
<div class="boxed">
<p><strong>Executable stack vulnerability</strong></p>
<p>When we use nested functions in GCC, the compiler often creates <em>trampolines</em> (small pieces of machine code) on the stack at runtime. These trampolines let the nested function access variables from the parent function's scope. For the CPU to run these code fragments, the stack's memory pages need to be marked as executable.</p>
<p>An executable stack is a serious security risk because it makes buffer overflow attacks much easier. In these attacks, a hacker sends more data than a program can handle, which overwrites the stack with harmful &quot;shellcode&quot;. If the stack non-executable (which is the standard today), the CPU won't run that code and the program will just crash. But since our <code>defer</code> macro makes the stack executable, an attacker can jump straight to their injected code and run it, giving them complete control over the process.</p>
</div>
<h2 id="c11gcc">C11/GCC</h2>
<p>We can easily adapt the above version to use C11:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define defer _DEFER(__COUNTER__)
</span></span></span><span class="line"><span class="cl"><span class="cp">#define _DEFER(N) __DEFER(N)
</span></span></span><span class="line"><span class="cl"><span class="cp">#define __DEFER(N) ___DEFER(__DEFER_FUNC_##N, __DEFER_VAR_##N)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#define ___DEFER(F, V)                                         \
</span></span></span><span class="line"><span class="cl"><span class="cp">    auto void F(void*);                                        \
</span></span></span><span class="line"><span class="cl"><span class="cp">    __attribute__((cleanup(F))) int V __attribute__((unused)); \
</span></span></span><span class="line"><span class="cl"><span class="cp">    auto void F(void* _dummy_ptr)
</span></span></span></code></pre></div><codapi-snippet id="s-gcc11" editor="basic">
</codapi-snippet>
<p>Usage example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span><span class="o">*</span> <span class="n">p</span> <span class="o">=</span> <span class="nf">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">p</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">defer</span> <span class="p">{</span> <span class="nf">loud_free</span><span class="p">(</span><span class="n">p</span><span class="p">);</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;p = %d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="o">*</span><span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet sandbox="gcc" editor="basic" depends-on="s-gcc11" template="defer.c" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">p = 42
</span></span><span class="line"><span class="cl">freeing 0x127e05b30
</span></span></code></pre></div><p>The main downside remains: it's GCC-only.</p>
<h2 id="gccclang">GCC/Clang</h2>
<p>Clang fully supports the <code>cleanup</code> attribute, but it doesn't support nested functions. Instead, it offers the <em>blocks</em> extension, which works somewhat similar:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">outer</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">__block</span> <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="n">inner</span><span class="p">)(</span><span class="kt">void</span><span class="p">)</span> <span class="o">=</span> <span class="o">^</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">x</span> <span class="o">+=</span> <span class="mi">10</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">inner</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>We can use Clang blocks to make a <code>defer</code> version that works with both GCC and Clang:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#if defined(__clang__)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// Clang implementation.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define _DEFER_CONCAT(a, b) a##b
</span></span></span><span class="line"><span class="cl"><span class="cp">#define _DEFER_NAME(a, b) _DEFER_CONCAT(a, b)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kr">inline</span> <span class="kt">void</span> <span class="nf">_defer_cleanup</span><span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^*</span><span class="n">block</span><span class="p">)(</span><span class="kt">void</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">block</span><span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="n">block</span><span class="p">)();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#define defer                                                                   \
</span></span></span><span class="line"><span class="cl"><span class="cp">    __attribute__((unused)) void (^_DEFER_NAME(_defer_var_, __COUNTER__))(void) \
</span></span></span><span class="line"><span class="cl"><span class="cp">        __attribute__((cleanup(_defer_cleanup))) = ^
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#elif defined(__GNUC__)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// GCC implementation.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define defer _DEFER(__COUNTER__)
</span></span></span><span class="line"><span class="cl"><span class="cp">#define _DEFER(N) __DEFER(N)
</span></span></span><span class="line"><span class="cl"><span class="cp">#define __DEFER(N) ___DEFER(__DEFER_FUNC_##N, __DEFER_VAR_##N)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#define ___DEFER(F, V)                                         \
</span></span></span><span class="line"><span class="cl"><span class="cp">    auto void F(void*);                                        \
</span></span></span><span class="line"><span class="cl"><span class="cp">    __attribute__((cleanup(F))) int V __attribute__((unused)); \
</span></span></span><span class="line"><span class="cl"><span class="cp">    auto void F(void* _dummy_ptr)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#else
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// Runtime error for unsupported compilers.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define defer assert(!&#34;unsupported compiler&#34;);
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span></code></pre></div><codapi-snippet id="s-gcclang" editor="basic">
</codapi-snippet>
<p>Usage example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span><span class="o">*</span> <span class="n">p</span> <span class="o">=</span> <span class="nf">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">p</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">defer</span> <span class="p">{</span> <span class="nf">loud_free</span><span class="p">(</span><span class="n">p</span><span class="p">);</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;p = %d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="o">*</span><span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet sandbox="gcc" editor="basic" depends-on="s-gcclang" template="defer.c" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">p = 42
</span></span><span class="line"><span class="cl">freeing 0x127e05b30
</span></span></code></pre></div><p>Now it works with Clang, but there are several things to be aware of:</p>
<ol>
<li>We must compile with <code>-fblocks</code>.</li>
<li>We must put a <code>;</code> after the closing brace in the deferred block: <code>defer { ... };</code>.</li>
<li>If we need to modify a variable inside the <code>defer</code> block, the variable must be declared with <code>__block</code>:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">__block</span> <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">defer</span> <span class="p">{</span> <span class="n">x</span> <span class="o">+=</span> <span class="mi">10</span><span class="p">;</span> <span class="p">};</span>
</span></span></code></pre></div><p>On the plus side, this implementation works with both GCC and Clang. The downside is that it's still not standard C, and won't work with other compilers like MSVC.</p>
<h2 id="msvc">MSVC</h2>
<p>MSVC, of course, doesn't support the cleanup attribute. But it provides &quot;structured exception handling&quot; with the <code>__try</code> and <code>__finally</code> keywords:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span><span class="o">*</span> <span class="n">p</span> <span class="o">=</span> <span class="nf">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">p</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kr">__try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;p = %d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="o">*</span><span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kr">__finally</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">loud_free</span><span class="p">(</span><span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The code in the <code>__finally</code> block will always run, no matter how the <code>__try</code> block exits — whether it finishes normally, returns early, or crashes (for example, from a null pointer dereference).</p>
<p>This isn't the <code>defer</code> we're looking for, but it's a decent alternative if you're only programming for Windows.</p>
<h2 id="long-jump">Long jump</h2>
<p>There are well-known <code>defer</code> implementations by <a href="https://gitlab.inria.fr/gustedt/defer">Jens Gustedt</a> and <a href="https://github.com/moon-chilled/Defer">moon-chilled</a> that use <code>setjmp</code> and <code>longjmp</code>. I'm mentioning them for completeness, but honestly, I would never use them in production. The first one is extremely large, and the second one is extremely hacky. Also, I'd rather not use long jumps unless it's absolutely necessary.</p>
<p>Still, here's a usage example from Gustedt's library:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">guard</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="o">*</span> <span class="k">const</span> <span class="n">p</span> <span class="o">=</span> <span class="nf">malloc</span><span class="p">(</span><span class="mi">25</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">p</span><span class="p">)</span> <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">defer</span> <span class="nf">free</span><span class="p">(</span><span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="o">*</span> <span class="k">const</span> <span class="n">q</span> <span class="o">=</span> <span class="nf">malloc</span><span class="p">(</span><span class="mi">25</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">q</span><span class="p">)</span> <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">defer</span> <span class="nf">free</span><span class="p">(</span><span class="n">q</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nf">mtx_lock</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mut</span><span class="p">)</span><span class="o">==</span><span class="n">thrd_error</span><span class="p">)</span> <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">defer</span> <span class="nf">mtx_unlock</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mut</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Here, all deferred statements run at the end of the guarded block, no matter how we exit the block (normally or through <code>break</code>).</p>
<h2 id="for-loop">For loop</h2>
<p>The <a href="https://github.com/stclib/STC">stc</a> library probably has the simplest <code>defer</code> implementation ever:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define defer(...) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    for (int _c_i3 = 0; _c_i3++ == 0; __VA_ARGS__)
</span></span></span></code></pre></div><codapi-snippet id="s-loop" editor="basic">
</codapi-snippet>
<p>Usage example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span><span class="o">*</span> <span class="n">p</span> <span class="o">=</span> <span class="nf">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">p</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">defer</span><span class="p">(</span><span class="nf">loud_free</span><span class="p">(</span><span class="n">p</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;p = %d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="o">*</span><span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet sandbox="gcc" editor="basic" depends-on="s-loop" template="defer.c" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">p = 42
</span></span><span class="line"><span class="cl">freeing 0x127e05b30
</span></span></code></pre></div><p>Here, the deferred statement is passed as <code>__VA_ARGS__</code> and is used as the loop increment. The &quot;defer-aware&quot; block of code is the loop body. Since the increment runs after the body, the deferred statement executes after the main code.</p>
<p>This approach works with all mainstream compilers, but it falls apart if you try to exit early with <code>break</code> or <code>return</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span><span class="o">*</span> <span class="n">p</span> <span class="o">=</span> <span class="nf">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">p</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">defer</span><span class="p">(</span><span class="nf">loud_free</span><span class="p">(</span><span class="n">p</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">p</span> <span class="o">==</span> <span class="mi">42</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;early exit, defer is not called</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;p = %d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="o">*</span><span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet sandbox="gcc" editor="basic" depends-on="s-loop" template="defer.c" output>
</codapi-snippet>
<pre tabindex="0"><code class="language-ok" data-lang="ok">early exit, defer is not called
</code></pre><h2 id="stack">Stack</h2>
<p><a href="https://github.com/grassator/defer-for-c">Dmitriy Kubyshkin</a> provides a <code>defer</code> implementation that adds a &quot;stack frame&quot; of deferred calls to any function that needs them. Here's a simplified version:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define countof(A) ((sizeof(A)) / (sizeof((A)[0])))
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// Deferred function and its argument.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">struct</span> <span class="n">_defer_ctx</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">fn</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">arg</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Calls all deferred functions in LIFO order.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kr">inline</span> <span class="kt">void</span> <span class="nf">_defer_drain</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="k">struct</span> <span class="n">_defer_ctx</span><span class="o">*</span> <span class="n">it</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="k">struct</span> <span class="n">_defer_ctx</span><span class="o">*</span> <span class="n">end</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;</span> <span class="n">it</span> <span class="o">!=</span> <span class="n">end</span><span class="p">;</span> <span class="n">it</span><span class="o">++</span><span class="p">)</span> <span class="n">it</span><span class="o">-&gt;</span><span class="nf">fn</span><span class="p">(</span><span class="n">it</span><span class="o">-&gt;</span><span class="n">arg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Initializes the defer stack with the given size
</span></span></span><span class="line"><span class="cl"><span class="c1">// for the current function.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define defers(n)                     \
</span></span></span><span class="line"><span class="cl"><span class="cp">    struct {                          \
</span></span></span><span class="line"><span class="cl"><span class="cp">        struct _defer_ctx* first;     \
</span></span></span><span class="line"><span class="cl"><span class="cp">        struct _defer_ctx items[(n)]; \
</span></span></span><span class="line"><span class="cl"><span class="cp">    } _deferred = {&amp;_deferred.items[(n)], {0}}
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// Pushes a deferred function call onto the stack.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define defer(_fn, _arg)                              \
</span></span></span><span class="line"><span class="cl"><span class="cp">    do {                                              \
</span></span></span><span class="line"><span class="cl"><span class="cp">        if (_deferred.first &lt;= &amp;_deferred.items[0]) { \
</span></span></span><span class="line"><span class="cl"><span class="cp">            assert(!&#34;defer stack overflow&#34;);          \
</span></span></span><span class="line"><span class="cl"><span class="cp">        }                                             \
</span></span></span><span class="line"><span class="cl"><span class="cp">        struct _defer_ctx* d = --_deferred.first;     \
</span></span></span><span class="line"><span class="cl"><span class="cp">        d-&gt;fn = (void (*)(void*))(_fn);               \
</span></span></span><span class="line"><span class="cl"><span class="cp">        d-&gt;arg = (void*)(_arg);                       \
</span></span></span><span class="line"><span class="cl"><span class="cp">    } while (0)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// Calls all deferred functions and returns from the current function.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define returnd                                          \
</span></span></span><span class="line"><span class="cl"><span class="cp">    while (                                              \
</span></span></span><span class="line"><span class="cl"><span class="cp">        _defer_drain(                                    \
</span></span></span><span class="line"><span class="cl"><span class="cp">            _deferred.first,                             \
</span></span></span><span class="line"><span class="cl"><span class="cp">            &amp;_deferred.items[countof(_deferred.items)]), \
</span></span></span><span class="line"><span class="cl"><span class="cp">        1) return
</span></span></span></code></pre></div><codapi-snippet id="s-stack" editor="basic">
</codapi-snippet>
<p>Usage example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// The function supports up to 16 deferred calls.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">defers</span><span class="p">(</span><span class="mi">16</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">int</span><span class="o">*</span> <span class="n">p</span> <span class="o">=</span> <span class="nf">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">p</span><span class="p">)</span> <span class="n">returnd</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">defer</span><span class="p">(</span><span class="n">loud_free</span><span class="p">,</span> <span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;p = %d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="o">*</span><span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// We must exit through returnd to
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// ensure deferred functions are called.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">returnd</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet sandbox="gcc" editor="basic" depends-on="s-stack" template="defer.c" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">p = 42
</span></span><span class="line"><span class="cl">freeing 0x127e05b30
</span></span></code></pre></div><p>This version works with all mainstream compilers. Also, unlike the STC version, defers run correctly in case of early exit:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">defers</span><span class="p">(</span><span class="mi">16</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">int</span><span class="o">*</span> <span class="n">p</span> <span class="o">=</span> <span class="nf">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">p</span><span class="p">)</span> <span class="n">returnd</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">defer</span><span class="p">(</span><span class="n">loud_free</span><span class="p">,</span> <span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">p</span> <span class="o">==</span> <span class="mi">42</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;early exit</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">returnd</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;p = %d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="o">*</span><span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">returnd</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet sandbox="gcc" editor="basic" depends-on="s-stack" template="defer.c" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">early exit
</span></span><span class="line"><span class="cl">freeing 0x127e05b30
</span></span></code></pre></div><p>Unfortunately, there are some drawbacks:</p>
<ul>
<li>Defer only supports single-function calls, not code blocks.</li>
<li>We always have to call <code>defers</code> at the start of the function and exit using <code>returnd</code>. In the original implementation, Dmitriy overrides the <code>return</code> keyword, but this won't compile with strict compile flags (which I think we should always use).</li>
<li>The deferred function runs before the return value is evaluated, not after.</li>
</ul>
<h2 id="simplified-gccclang">Simplified GCC/Clang</h2>
<p>The Stack version above doesn't support deferring code blocks. In my opinion, that's not a problem, since most defers are just &quot;free this resource&quot; actions, which only need a single function call with one argument.</p>
<p>If we accept this limitation, we can simplify the GCC/Clang version by dropping GCC's nested functions and Clang's blocks:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define _DEFER_CONCAT(a, b) a##b
</span></span></span><span class="line"><span class="cl"><span class="cp">#define _DEFER_NAME(a, b) _DEFER_CONCAT(a, b)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// Deferred function and its argument.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">struct</span> <span class="n">_defer_ctx</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">fn</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">arg</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Calls the deferred function with its argument.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kr">inline</span> <span class="kt">void</span> <span class="nf">_defer_cleanup</span><span class="p">(</span><span class="k">struct</span> <span class="n">_defer_ctx</span><span class="o">*</span> <span class="n">ctx</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">ctx</span><span class="o">-&gt;</span><span class="n">fn</span><span class="p">)</span> <span class="n">ctx</span><span class="o">-&gt;</span><span class="nf">fn</span><span class="p">(</span><span class="n">ctx</span><span class="o">-&gt;</span><span class="n">arg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Create a deferred function call for the current scope.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define defer(fn, ptr)                                      \
</span></span></span><span class="line"><span class="cl"><span class="cp">    struct _defer_ctx _DEFER_NAME(_defer_var_, __COUNTER__) \
</span></span></span><span class="line"><span class="cl"><span class="cp">        __attribute__((cleanup(_defer_cleanup))) =          \
</span></span></span><span class="line"><span class="cl"><span class="cp">            {(void (*)(void*))(fn), (void*)(ptr)}
</span></span></span></code></pre></div><codapi-snippet id="s-gcclang-simple" editor="basic">
</codapi-snippet>
<p>Works like a charm:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span><span class="o">*</span> <span class="n">p</span> <span class="o">=</span> <span class="nf">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">p</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">defer</span><span class="p">(</span><span class="n">loud_free</span><span class="p">,</span> <span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;p = %d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="o">*</span><span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet sandbox="gcc" editor="basic" depends-on="s-gcclang-simple" template="defer.c" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">p = 42
</span></span><span class="line"><span class="cl">freeing 0x127e05b30
</span></span></code></pre></div><h2 id="final-thoughts">Final thoughts</h2>
<p>Personally, I like the simpler GCC/Clang version better. Not having MSVC support isn't a big deal, since we can run GCC on Windows or use the Zig compiler, which works just fine.</p>
<p>But if I really need to support GCC, Clang, and MSVC — I'd probably go with the Stack version.</p>
<p>Anyway, I don't think we need to wait for <code>defer</code> to be added to the C standard. We already have <code>defer</code> at home!</p>
<script defer src="/modules/codapi/snippet.js"></script>
]]></content:encoded></item><item><title>Interfaces and traits in C</title><link>https://antonz.org/interfaces-in-c/</link><pubDate>Thu, 22 Jan 2026 12:00:00 +0000</pubDate><guid>https://antonz.org/interfaces-in-c/</guid><description>Implemented with structs and function pointers.</description><content:encoded><![CDATA[<p>Everyone likes interfaces in Go and traits in Rust. Polymorphism without class-based hierarchies or inheritance seems to be the sweet spot. What if we try to implement this in C?</p>
<p><a href="#interfaces-in-go">Interfaces in Go</a> •
<a href="#traits-in-rust">Traits in Rust</a> •
<a href="#toy-example">Toy example</a> •
<a href="#interface-definition">Interface definition</a> •
<a href="#interface-data">Interface data</a> •
<a href="#method-table">Method table</a> •
<a href="#method-table-in-implementor">Method table in implementor</a> •
<a href="#type-assertions">Type assertions</a> •
<a href="#separate-self">Separate self</a> •
<a href="#final-thoughts">Final thoughts</a></p>
<h2 id="interfaces-in-go">Interfaces in Go</h2>
<p>An interface in Go is a convenient way to define a contract for some useful behavior. Take, for example, the honored <code>io.Reader</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Reader is the interface that wraps the basic Read method.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Reader</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Read reads up to len(p) bytes into p. It returns the number of bytes
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// read (0 &lt;= n &lt;= len(p)) and any error encountered.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">Read</span><span class="p">(</span><span class="nx">p</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Anything that can read data into a byte slice provided by the caller is a <code>Reader</code>. Quite handy, because the code doesn't need to care where the data comes from — whether it's memory, the file system, or the network. All that matters is that it can read the data into a slice:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// work processes the data read from r.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">work</span><span class="p">(</span><span class="nx">r</span> <span class="nx">io</span><span class="p">.</span><span class="nx">Reader</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">buf</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">n</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">r</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="nx">buf</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="o">&amp;&amp;</span> <span class="nx">err</span> <span class="o">!=</span> <span class="nx">io</span><span class="p">.</span><span class="nx">EOF</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">return</span> <span class="nx">n</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet id="s-work-go" editor="basic">
</codapi-snippet>
<p>We can provide any kind of reader:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">total</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl">    <span class="nx">b</span> <span class="o">:=</span> <span class="nx">bytes</span><span class="p">.</span><span class="nf">NewBufferString</span><span class="p">(</span><span class="s">&#34;hello world&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// bytes.Buffer implements io.Reader, so we can use it with work.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">total</span> <span class="o">+=</span> <span class="nf">work</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">total</span> <span class="o">+=</span> <span class="nf">work</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;total =&#34;</span><span class="p">,</span> <span class="nx">total</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet sandbox="go" editor="basic" depends-on="s-work-go" template="header.go" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">total = 11
</span></span></code></pre></div><p>Go's interfaces are structural, which is similar to duck typing. A type doesn't need to explicitly state that it implements <code>io.Reader</code>; it just needs to have a <code>Read</code> method:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Zeros is an infinite stream of zero bytes.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Zeros</span> <span class="kd">struct</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">z</span> <span class="nx">Zeros</span><span class="p">)</span> <span class="nf">Read</span><span class="p">(</span><span class="nx">p</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">clear</span><span class="p">(</span><span class="nx">p</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="nx">p</span><span class="p">),</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet id="s-zeros-go" editor="basic">
</codapi-snippet>
<p>The Go compiler and runtime take care of the rest:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">total</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">z</span> <span class="nx">Zeros</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Zeros implements io.Reader, so we can use it with work.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">total</span> <span class="o">+=</span> <span class="nf">work</span><span class="p">(</span><span class="nx">z</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">total</span> <span class="o">+=</span> <span class="nf">work</span><span class="p">(</span><span class="nx">z</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;total =&#34;</span><span class="p">,</span> <span class="nx">total</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet sandbox="go" editor="basic" depends-on="s-zeros-go s-work-go" template="header.go" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">total = 16
</span></span></code></pre></div><h2 id="traits-in-rust">Traits in Rust</h2>
<p>A trait in Rust is also a way to define a contract for certain behavior. Here's the <code>std::io::Read</code> trait:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// The Read trait allows for reading bytes from a source.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="n">Read</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Readers are defined by one required method, read(). Each call to read()
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="c1">// will attempt to pull bytes from this source into a provided buffer.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="k">fn</span> <span class="nf">read</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">buf</span>: <span class="kp">&amp;</span><span class="nc">mut</span><span class="w"> </span><span class="p">[</span><span class="kt">u8</span><span class="p">])</span><span class="w"> </span>-&gt; <span class="nc">std</span>::<span class="n">io</span>::<span class="nb">Result</span><span class="o">&lt;</span><span class="kt">usize</span><span class="o">&gt;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>Unlike in Go, a type must explicitly state that it implements a trait:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// An infinite stream of zero bytes.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">struct</span> <span class="nc">Zeros</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">impl</span><span class="w"> </span><span class="n">io</span>::<span class="n">Read</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">Zeros</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">read</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">buf</span>: <span class="kp">&amp;</span><span class="nc">mut</span><span class="w"> </span><span class="p">[</span><span class="kt">u8</span><span class="p">])</span><span class="w"> </span>-&gt; <span class="nc">io</span>::<span class="nb">Result</span><span class="o">&lt;</span><span class="kt">usize</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">buf</span><span class="p">.</span><span class="n">fill</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nb">Ok</span><span class="p">(</span><span class="n">buf</span><span class="p">.</span><span class="n">len</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><codapi-snippet id="s-zeros-rs" editor="basic">
</codapi-snippet>
<p>The Rust compiler takes care of the rest:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rs" data-lang="rs"><span class="line"><span class="cl"><span class="c1">// Processes the data read from r.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">fn</span> <span class="nf">work</span><span class="p">(</span><span class="n">r</span>: <span class="kp">&amp;</span><span class="nc">mut</span><span class="w"> </span><span class="k">dyn</span><span class="w"> </span><span class="n">io</span>::<span class="n">Read</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">usize</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">buf</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="mi">8</span><span class="p">];</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">match</span><span class="w"> </span><span class="n">r</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="n">buf</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nb">Ok</span><span class="p">(</span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">n</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nb">Err</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="fm">panic!</span><span class="p">(</span><span class="s">&#34;Error: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">e</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">total</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Zeros</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Zeros implements Read, so we can use it with work.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="n">total</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">work</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="n">z</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">total</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">work</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="n">z</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;total = </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">total</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><codapi-snippet sandbox="rust" editor="basic" depends-on="s-zeros-rs" template="header.rs" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">total = 16
</span></span></code></pre></div><p>Either way, whether it's Go or Rust, the caller only cares about the contract (defined as an interface or trait), not the specific implementation.</p>
<h2 id="toy-example">Toy example</h2>
<p>Let's make an even simpler version of <code>Reader</code> — one without any error handling (Go):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Reader an interface that wraps the basic Read method.
</span></span></span><span class="line"><span class="cl"><span class="c1">// Read reads up to len(p) bytes into p.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Reader</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">Read</span><span class="p">(</span><span class="nx">p</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet id="s-reader-go" editor="basic">
</codapi-snippet>
<p>Usage example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Zeros is an infinite stream of zero bytes.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Zeros</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">total</span> <span class="kt">int</span> <span class="c1">// total number of bytes read
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Read reads len(p) bytes into p.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">z</span> <span class="o">*</span><span class="nx">Zeros</span><span class="p">)</span> <span class="nf">Read</span><span class="p">(</span><span class="nx">p</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">clear</span><span class="p">(</span><span class="nx">p</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">z</span><span class="p">.</span><span class="nx">total</span> <span class="o">+=</span> <span class="nb">len</span><span class="p">(</span><span class="nx">p</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="nx">p</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// work processes the data read from r.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">work</span><span class="p">(</span><span class="nx">r</span> <span class="nx">Reader</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">buf</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">r</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="nx">buf</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">z</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="nx">Zeros</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nf">work</span><span class="p">(</span><span class="nx">z</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nf">work</span><span class="p">(</span><span class="nx">z</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;total =&#34;</span><span class="p">,</span> <span class="nx">z</span><span class="p">.</span><span class="nx">total</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet sandbox="go" editor="basic" depends-on="s-reader-go" template="header.go" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">total = 16
</span></span></code></pre></div><p>Let's see how we can do this in C!</p>
<h2 id="interface-definition">Interface definition</h2>
<p>The main building blocks in C are structs and functions, so let's use them. Our <code>Reader</code> will be a struct with a single field called <code>Read</code>. This field will be a pointer to a function with the right signature:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// An interface that wraps the basic Read method.
</span></span></span><span class="line"><span class="cl"><span class="c1">// Read reads up to len(p) bytes into p.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="p">(</span><span class="o">*</span><span class="n">Read</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">Reader</span><span class="p">;</span>
</span></span></code></pre></div><codapi-snippet id="s-1-reader" editor="basic">
</codapi-snippet>
<p>To make <code>Zeros</code> fully dynamic, let's turn it into a struct with a <code>Read</code> function pointer (I know, I know — just bear with me):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// An infinite stream of zero bytes.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="p">(</span><span class="o">*</span><span class="n">Read</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="n">total</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">Zeros</span><span class="p">;</span>
</span></span></code></pre></div><codapi-snippet id="s-1-zeros" editor="basic">
</codapi-snippet>
<p>Here's the <code>Zeros_Read</code> &quot;method&quot; implementation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Reads up to len(p) bytes into p.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">size_t</span> <span class="nf">Zeros_Read</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Zeros</span><span class="o">*</span> <span class="n">z</span> <span class="o">=</span> <span class="p">(</span><span class="n">Zeros</span><span class="o">*</span><span class="p">)</span><span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">len</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">p</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">z</span><span class="o">-&gt;</span><span class="n">total</span> <span class="o">+=</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet id="s-1-zeros-read" editor="basic">
</codapi-snippet>
<p>The <code>work</code> is pretty obvious:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Does some work reading from r.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">size_t</span> <span class="nf">work</span><span class="p">(</span><span class="n">Reader</span><span class="o">*</span> <span class="n">r</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">buf</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">r</span><span class="o">-&gt;</span><span class="nf">Read</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet id="s-1-work" editor="basic">
</codapi-snippet>
<p>And, finally, the <code>main</code> function:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Zeros</span> <span class="n">z</span> <span class="o">=</span> <span class="p">{.</span><span class="n">Read</span> <span class="o">=</span> <span class="n">Zeros_Read</span><span class="p">,</span> <span class="p">.</span><span class="n">total</span> <span class="o">=</span> <span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Reader</span><span class="o">*</span> <span class="n">r</span> <span class="o">=</span> <span class="p">(</span><span class="n">Reader</span><span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">z</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">work</span><span class="p">(</span><span class="n">r</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">work</span><span class="p">(</span><span class="n">r</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;total = %zu</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">z</span><span class="p">.</span><span class="n">total</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet sandbox="gcc" editor="basic" depends-on="s-1-reader s-1-zeros s-1-zeros-read s-1-work" template="header.c" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">total = 16
</span></span></code></pre></div><p>See how easy it is to turn a <code>Zeros</code> into a <code>Reader</code>: all we need is <code>(Reader*)&amp;z</code>. Pretty cool, right?</p>
<p>Not really. Actually, this implementation is seriously flawed in almost every way (except for the <code>Reader</code> definition).</p>
<p><strong>Memory overhead</strong>. Each <code>Zeros</code> instance has its own function pointers (8 bytes per function on a 64-bit system) as &quot;methods&quot;, which isn't practical even if there are only a few of them. Regular objects should store data, not functions.</p>
<p><strong>Layout dependency</strong>. Converting from <code>Zeros*</code> to <code>Reader*</code> like <code>(Reader*)&amp;z</code> only works if both structures have the same <code>Read</code> field as their first member. If we try to implement another interface:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Reader interface.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="p">(</span><span class="o">*</span><span class="n">Read</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">Reader</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Closer interface.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">Close</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">Closer</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Zeros implements both Reader and Closer.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="p">(</span><span class="o">*</span><span class="n">Read</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">Close</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="n">total</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">Zeros</span><span class="p">;</span>
</span></span></code></pre></div><p>Everything will fall apart:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="nx">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Zeros</span> <span class="nx">z</span> <span class="p">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="nx">Read</span> <span class="p">=</span> <span class="nx">Zeros_Read</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="nx">Close</span> <span class="p">=</span> <span class="nx">Zeros_Close</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="nx">total</span> <span class="p">=</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Closer</span><span class="o">*</span> <span class="nx">c</span> <span class="p">=</span> <span class="p">(</span><span class="nx">Closer</span><span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="nx">z</span><span class="p">;</span>  <span class="c1">// (X)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">c</span><span class="o">-</span><span class="p">&gt;</span><span class="nf">Close</span><span class="p">(</span><span class="nx">c</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Segmentation fault: 11
</span></span></code></pre></div><p><code>Closer</code> and <code>Zeros</code> have different layouts, so type conversion in ⓧ is invalid and causes undefined behavior.</p>
<p><strong>Lack of type safety</strong>. Using a <code>void*</code> as the receiver in <code>Zeros_Read</code> means the caller can pass any type, and the compiler won't even show a warning:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">buf</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="nf">Zeros_Read</span><span class="p">(</span><span class="o">&amp;</span><span class="n">x</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">));</span>  <span class="c1">// bad decision
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">size_t</span> <span class="nf">Zeros_Read</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Zeros</span><span class="o">*</span> <span class="n">z</span> <span class="o">=</span> <span class="p">(</span><span class="n">Zeros</span><span class="o">*</span><span class="p">)</span><span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">z</span><span class="o">-&gt;</span><span class="n">total</span> <span class="o">+=</span> <span class="n">len</span><span class="p">;</span>                   <span class="c1">// consequences
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">return</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Abort trap: 6
</span></span></code></pre></div><p>C isn't a particularly type-safe language, but this is just too much. Let's try something else.</p>
<h2 id="interface-data">Interface data</h2>
<p>A better way is to store a reference to the actual object in the interface:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// An interface that wraps the basic Read method.
</span></span></span><span class="line"><span class="cl"><span class="c1">// Read reads up to len(p) Zeros into p.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="p">(</span><span class="o">*</span><span class="n">Read</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">Reader</span><span class="p">;</span>
</span></span></code></pre></div><codapi-snippet id="s-2-reader" editor="basic">
</codapi-snippet>
<blockquote>
<p>We could have the <code>Read</code> method in the interface take a <code>Reader</code> instead of a <code>void*</code>, but that would make the implementation more complicated without any real benefits. So, I'll keep it as <code>void*</code>.</p>
</blockquote>
<p>Then <code>Zeros</code> will only have its own fields:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// An infinite stream of zero bytes.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="n">total</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">Zeros</span><span class="p">;</span>
</span></span></code></pre></div><codapi-snippet id="s-2-zeros" editor="basic">
</codapi-snippet>
<p>We can make the <code>Zeros_Read</code> method type-safe:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Reads len(p) bytes into p.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">size_t</span> <span class="nf">Zeros_Read</span><span class="p">(</span><span class="n">Zeros</span><span class="o">*</span> <span class="n">z</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">len</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">p</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">256</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">z</span><span class="o">-&gt;</span><span class="n">total</span> <span class="o">+=</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet id="s-2-zeros-read" editor="basic">
</codapi-snippet>
<p>To make this work, we add a <code>Zeros_Reader</code> method that returns the instance wrapped in a <code>Reader</code> interface:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Returns a Reader implementation for Zeros.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Reader</span> <span class="nf">Zeros_Reader</span><span class="p">(</span><span class="n">Zeros</span><span class="o">*</span> <span class="n">z</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">Reader</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">Read</span> <span class="o">=</span> <span class="p">(</span><span class="kt">size_t</span> <span class="p">(</span><span class="o">*</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span><span class="p">,</span> <span class="kt">size_t</span><span class="p">))</span><span class="n">Zeros_Read</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">self</span> <span class="o">=</span> <span class="n">z</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet id="s-2-zeros-reader" editor="basic">
</codapi-snippet>
<div class="boxed">
<p><strong>Casting function pointers</strong></p>
<p>Technically, casting a function pointer that takes a <code>Zeros*</code> to one that takes a <code>void*</code> is undefined behavior in standard C. The standards-compliant way is to accept <code>void*</code> in <code>Zeros_Read</code> and cast it to <code>Zeros*</code>, as we did in the first version of the program:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">size_t</span> <span class="nf">Zeros_Read</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Zeros</span><span class="o">*</span> <span class="n">z</span> <span class="o">=</span> <span class="p">(</span><span class="n">Zeros</span><span class="o">*</span><span class="p">)</span><span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Reader</span> <span class="nf">Zeros_Reader</span><span class="p">(</span><span class="n">Zeros</span><span class="o">*</span> <span class="n">z</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">static</span> <span class="k">const</span> <span class="n">ReaderTable</span> <span class="n">impl</span> <span class="o">=</span> <span class="p">{.</span><span class="n">Read</span> <span class="o">=</span> <span class="n">Zeros_Read</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">Reader</span><span class="p">){.</span><span class="n">mtab</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">impl</span><span class="p">,</span> <span class="p">.</span><span class="n">self</span> <span class="o">=</span> <span class="n">z</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>In practice, the cast works on virtually all architectures because pointers have the same representation. So I'll continue casting for the rest of the article.</p>
</div>
<p>The <code>work</code> and <code>main</code> functions remain quite simple:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Does some work reading from r.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">size_t</span> <span class="nf">work</span><span class="p">(</span><span class="n">Reader</span> <span class="n">r</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">buf</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">r</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">self</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Zeros</span> <span class="n">z</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Reader</span> <span class="n">r</span> <span class="o">=</span> <span class="nf">Zeros_Reader</span><span class="p">(</span><span class="o">&amp;</span><span class="n">z</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">work</span><span class="p">(</span><span class="n">r</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">work</span><span class="p">(</span><span class="n">r</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;total = %zu</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">z</span><span class="p">.</span><span class="n">total</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet sandbox="gcc" editor="basic" depends-on="s-2-reader s-2-zeros s-2-zeros-read s-2-zeros-reader" template="header.c" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">total = 16
</span></span></code></pre></div><p>This approach is much better than the previous one:</p>
<ul>
<li>The <code>Zeros</code> struct is lean and doesn't have any interface-related fields.</li>
<li>The <code>Zeros_Read</code> method takes a <code>Zeros*</code> instead of a <code>void*</code>.</li>
<li>The cast from <code>Zeros</code> to <code>Reader</code> is handled inside the <code>Zeros_Reader</code> method.</li>
<li>We can implement multiple interfaces if needed.</li>
</ul>
<blockquote>
<p>Since our <code>Zeros</code> type now knows about the <code>Reader</code> interface (through the <code>Zeros_Reader</code> method), our implementation is more like a basic version of a Rust dynamic trait than a true Go interface. For simplicity, I'll keep using the term &quot;interface&quot;.</p>
</blockquote>
<p>There is one downside, though: each <code>Reader</code> instance has its own function pointer for every interface method. Since <code>Reader</code> only has one method, this isn't an issue. But if an interface has a dozen methods and the program uses a lot of these interface instances, it can become a problem.</p>
<p>Let's fix this.</p>
<h2 id="method-table">Method table</h2>
<p>Let's extract interface methods into a separate strucute — the method table. The interface references its methods though the <code>mtab</code> field:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// An interface that wraps the basic Read method.
</span></span></span><span class="line"><span class="cl"><span class="c1">// Read reads up to len(p) bytes into p.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="p">(</span><span class="o">*</span><span class="n">Read</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">ReaderTable</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="n">ReaderTable</span><span class="o">*</span> <span class="n">mtab</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">Reader</span><span class="p">;</span>
</span></span></code></pre></div><codapi-snippet id="s-3-reader" editor="basic">
</codapi-snippet>
<p><code>Zeros</code> and <code>Zeros_Read</code> don't change at all:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// An infinite stream of zero bytes.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="n">total</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">Zeros</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Reads len(p) bytes into p.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">size_t</span> <span class="nf">Zeros_Read</span><span class="p">(</span><span class="n">Zeros</span><span class="o">*</span> <span class="n">z</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">len</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">p</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">256</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">z</span><span class="o">-&gt;</span><span class="n">total</span> <span class="o">+=</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet id="s-3-zeros" editor="basic">
</codapi-snippet>
<p>The <code>Zeros_Reader</code> method initializes the static method table and assigns it to the interface instance:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Returns a Reader implementation for Zeros.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Reader</span> <span class="nf">Zeros_Reader</span><span class="p">(</span><span class="n">Zeros</span><span class="o">*</span> <span class="n">z</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// The method table is only initialized once.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">static</span> <span class="k">const</span> <span class="n">ReaderTable</span> <span class="n">impl</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">Read</span> <span class="o">=</span> <span class="p">(</span><span class="kt">size_t</span> <span class="p">(</span><span class="o">*</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span><span class="p">,</span> <span class="kt">size_t</span><span class="p">))</span><span class="n">Zeros_Read</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">Reader</span><span class="p">){.</span><span class="n">mtab</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">impl</span><span class="p">,</span> <span class="p">.</span><span class="n">self</span> <span class="o">=</span> <span class="n">z</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet id="s-3-zeros-reader" editor="basic">
</codapi-snippet>
<p>The only difference in <code>work</code> is that it calls the <code>Read</code> method on the interface indirectly using the method table (<code>r.mtab-&gt;Read</code> instead of <code>r.Read</code>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Does some work reading from r.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">size_t</span> <span class="nf">work</span><span class="p">(</span><span class="n">Reader</span> <span class="n">r</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">buf</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">r</span><span class="p">.</span><span class="n">mtab</span><span class="o">-&gt;</span><span class="nf">Read</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">self</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet id="s-3-work" editor="basic">
</codapi-snippet>
<p><code>main</code> stays the same:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Zeros</span> <span class="n">z</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Reader</span> <span class="n">r</span> <span class="o">=</span> <span class="nf">Zeros_Reader</span><span class="p">(</span><span class="o">&amp;</span><span class="n">z</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">work</span><span class="p">(</span><span class="n">r</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">work</span><span class="p">(</span><span class="n">r</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;total = %zu</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">z</span><span class="p">.</span><span class="n">total</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet sandbox="gcc" editor="basic" depends-on="s-3-reader s-3-zeros s-3-zeros-reader s-3-work" template="header.c" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">total = 16
</span></span></code></pre></div><p>Now the <code>Reader</code> instance always has a single pointer field for its methods. So even for large interfaces, it only uses 16 bytes (<code>mtab</code> + <code>self</code> fields). This approach also keeps all the benefits from the previous version:</p>
<ul>
<li>Lightweight <code>Zeros</code> structure.</li>
<li>Easy conversion from <code>Zeros</code> to <code>Reader</code>.</li>
<li>Supports multiple interfaces.</li>
</ul>
<p>We can even add a separate <code>Reader_Read</code> helper so the client doesn't have to worry about <code>r.mtab-&gt;Read</code> implementation detail:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Reads len(p) bytes into p.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">size_t</span> <span class="nf">Reader_Read</span><span class="p">(</span><span class="n">Reader</span> <span class="n">r</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">r</span><span class="p">.</span><span class="n">mtab</span><span class="o">-&gt;</span><span class="nf">Read</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">self</span><span class="p">,</span> <span class="n">p</span><span class="p">,</span> <span class="n">len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Does some work reading from r.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">size_t</span> <span class="nf">work</span><span class="p">(</span><span class="n">Reader</span> <span class="n">r</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">buf</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nf">Reader_Read</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Nice!</p>
<h2 id="method-table-in-implementor">Method table in implementor</h2>
<p>There's another approach I've seen out there. I don't like it, but it's still worth mentioning for completeness.</p>
<p>Instead of embedding the <code>Reader</code> method table in the interface, we can place it in the implementation (<code>Zeros</code>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// An interface that wraps the basic Read method.
</span></span></span><span class="line"><span class="cl"><span class="c1">// Read reads up to len(p) bytes into p.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="p">(</span><span class="o">*</span><span class="n">Read</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">ReaderTable</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="n">ReaderTable</span><span class="o">*</span> <span class="n">Reader</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// An infinite stream of zero bytes.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Reader</span> <span class="n">mtab</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="n">total</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">Zeros</span><span class="p">;</span>
</span></span></code></pre></div><codapi-snippet id="s-4-zeros" editor="basic">
</codapi-snippet>
<p>We initialize the method table in the <code>Zeros</code> constructor:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Returns a new Zeros instance.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Zeros</span> <span class="nf">NewZeros</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">static</span> <span class="k">const</span> <span class="n">ReaderTable</span> <span class="n">impl</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">Read</span> <span class="o">=</span> <span class="p">(</span><span class="kt">size_t</span> <span class="p">(</span><span class="o">*</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span><span class="p">,</span> <span class="kt">size_t</span><span class="p">))</span><span class="n">Zeros_Read</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">Zeros</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">mtab</span> <span class="o">=</span> <span class="p">(</span><span class="n">Reader</span><span class="p">)</span><span class="o">&amp;</span><span class="n">impl</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">total</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet id="s-4-new-zeros" editor="basic">
</codapi-snippet>
<p><code>work</code> now takes a <code>Reader</code> pointer:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Does some work reading from r.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">size_t</span> <span class="nf">work</span><span class="p">(</span><span class="n">Reader</span><span class="o">*</span> <span class="n">r</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">buf</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="o">*</span><span class="n">r</span><span class="p">)</span><span class="o">-&gt;</span><span class="nf">Read</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet id="s-4-work" editor="basic">
</codapi-snippet>
<p>And <code>main</code> converts <code>Zeros*</code> to <code>Reader*</code> with a simple type cast:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Zeros</span> <span class="n">z</span> <span class="o">=</span> <span class="nf">NewZeros</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Reader</span><span class="o">*</span> <span class="n">r</span> <span class="o">=</span> <span class="p">(</span><span class="n">Reader</span><span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">z</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">work</span><span class="p">(</span><span class="n">r</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">work</span><span class="p">(</span><span class="n">r</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;total = %zu</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">z</span><span class="p">.</span><span class="n">total</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet sandbox="gcc" editor="basic" depends-on="s-4-zeros s-2-zeros-read s-4-new-zeros s-4-work" template="header.c" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">total = 16
</span></span></code></pre></div><p>This keeps <code>Zeros</code> pretty lightweight, only adding one extra <code>mtab</code> field. But the <code>(Reader*)&amp;z</code> cast only works because <code>Reader mtab</code> is the first field in <code>Zeros</code>. If we try to implement a second interface, things will break — just like in the very first solution.</p>
<p>I think the &quot;method table in the interface&quot; approach is much better.</p>
<h2 id="type-assertions">Type assertions</h2>
<p>Go has an <code>io.Copy</code> function that copies data from a source (a reader) to a destination (a writer):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Copy</span><span class="p">(</span><span class="nx">dst</span> <span class="nx">Writer</span><span class="p">,</span> <span class="nx">src</span> <span class="nx">Reader</span><span class="p">)</span> <span class="p">(</span><span class="nx">written</span> <span class="kt">int64</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span>
</span></span></code></pre></div><p>There's an interesting comment in its documentation:</p>
<blockquote>
<p>If <code>src</code> implements <code>WriterTo</code>, the copy is implemented by calling <code>src.WriteTo(dst)</code>. Otherwise, if <code>dst</code> implements <code>ReaderFrom</code>, the copy is implemented by calling <code>dst.ReadFrom(src)</code>.</p>
</blockquote>
<p>Here's what the function looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Copy</span><span class="p">(</span><span class="nx">dst</span> <span class="nx">Writer</span><span class="p">,</span> <span class="nx">src</span> <span class="nx">Reader</span><span class="p">)</span> <span class="p">(</span><span class="nx">written</span> <span class="kt">int64</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// If the reader has a WriteTo method, use it to do the copy.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// Avoids an allocation and a copy.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="nx">wt</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">src</span><span class="p">.(</span><span class="nx">WriterTo</span><span class="p">);</span> <span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">wt</span><span class="p">.</span><span class="nf">WriteTo</span><span class="p">(</span><span class="nx">dst</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Similarly, if the writer has a ReadFrom method, use it to do the copy.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="nx">rf</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">dst</span><span class="p">.(</span><span class="nx">ReaderFrom</span><span class="p">);</span> <span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">rf</span><span class="p">.</span><span class="nf">ReadFrom</span><span class="p">(</span><span class="nx">src</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// The default implementation using regular Reader and Writer.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p><code>src.(WriterTo)</code> is a type assertion that checks if the <code>src</code> reader is not just a <code>Reader</code>, but also implements the <code>WriterTo</code> interface. The Go runtime handles these kinds of dynamic type checks.</p>
<p>Can we do something like this in C? I'd prefer not to make it fully dynamic, since trying to recreate parts of the Go runtime in C probably isn't a good idea.</p>
<p>What we can do is add an optional <code>AsWriterTo</code> method to the <code>Reader</code> interface:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// An interface that wraps the basic Read method.
</span></span></span><span class="line"><span class="cl"><span class="c1">// Read reads up to len(p) bytes into p.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// required
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">size_t</span> <span class="p">(</span><span class="o">*</span><span class="n">Read</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// optional
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">WriterTo</span> <span class="p">(</span><span class="o">*</span><span class="n">AsWriterTo</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">ReaderTable</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="n">ReaderTable</span><span class="o">*</span> <span class="n">mtab</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">Reader</span><span class="p">;</span>
</span></span></code></pre></div><p>Then we can easily check if a given <code>Reader</code> is also a <code>WriterTo</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">work</span><span class="p">(</span><span class="n">Reader</span> <span class="n">r</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Check if r implements WriterTo.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">mtab</span><span class="o">-&gt;</span><span class="n">AsWriterTo</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">WriterTo</span> <span class="n">wt</span> <span class="o">=</span> <span class="n">r</span><span class="p">.</span><span class="n">mtab</span><span class="o">-&gt;</span><span class="nf">AsWriterTo</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">self</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Use r as WriterTo...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Use r as a regular Reader...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Still, this feels a bit like a hack. I'd rather avoid using type assertions unless it's really necessary.</p>
<h2 id="separate-self">Separate self</h2>
<p>Some C programmers don't like the &quot;method table + data&quot; approach to interfaces because it feels too heavy and too &quot;object-oriented&quot;. An alternative is to just use a method table as the interface:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// An interface that groups the basic Read and Close methods.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Read reads up to len(p) bytes into p.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">size_t</span> <span class="p">(</span><span class="o">*</span><span class="n">Read</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Close frees resources associated with the reader.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">Close</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">ReadCloser</span><span class="p">;</span>
</span></span></code></pre></div><codapi-snippet id="s-6-reader" editor="basic">
</codapi-snippet>
<p>The <code>Zeros</code> type and its methods are exactly the same as before (I just changed <code>Zeros*</code> to <code>void*</code> to follow the standard):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// An infinite stream of zero bytes.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="n">total</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">Zeros</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Reads len(p) bytes into p.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">size_t</span> <span class="nf">Zeros_Read</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Zeros</span><span class="o">*</span> <span class="n">z</span> <span class="o">=</span> <span class="p">(</span><span class="n">Zeros</span><span class="o">*</span><span class="p">)</span><span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">len</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">p</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">256</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">z</span><span class="o">-&gt;</span><span class="n">total</span> <span class="o">+=</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Closes the Zeros reader.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">Zeros_Close</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// No resources to free for Zeros.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet id="s-6-zeros" editor="basic">
</codapi-snippet>
<p>Since the interface no longer holds the data, converting from <code>Zeros</code> to <code>Reader</code> is now a constant instead of a function:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// ReadCloser implementation for Zeros.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="n">ReadCloser</span> <span class="n">Zeros_ReadCloser</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">Read</span> <span class="o">=</span> <span class="n">Zeros_Read</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">Close</span> <span class="o">=</span> <span class="n">Zeros_Close</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><codapi-snippet id="s-6-zeros-reader" editor="basic">
</codapi-snippet>
<p>Unfortunately, this approach makes <code>work</code> a bit more complicated because it now has to take the instance as a separate parameter:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Does some work reading from r, then closes it.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">size_t</span> <span class="nf">work</span><span class="p">(</span><span class="n">ReadCloser</span> <span class="n">r</span><span class="p">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">buf</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="n">n</span> <span class="o">=</span> <span class="n">r</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">r</span><span class="p">.</span><span class="nf">Close</span><span class="p">(</span><span class="n">self</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">n</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Zeros</span> <span class="n">z</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="nf">work</span><span class="p">(</span><span class="n">Zeros_ReadCloser</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">z</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;total = %zu</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">z</span><span class="p">.</span><span class="n">total</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet sandbox="gcc" editor="basic" depends-on="s-6-reader s-6-zeros s-6-zeros-reader" template="header.c" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">total = 8
</span></span></code></pre></div><p>This approach keeps the interface and implementation fairly simple, but it puts more responsibility on the caller, who now has to keep track of the instance and pass it in when calling interface methods.</p>
<h2 id="final-thoughts">Final thoughts</h2>
<p>Interfaces (dynamic traits, really) in C are possible, but they're not as simple or elegant as in Go or Rust. The method table approach we discussed is a good starting point. It's memory-efficient, as type-safe as possible given C's limitations, and supports polymorphic behavior.</p>
<p>Here's the full source code if you are interested:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdint.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// An interface that wraps the basic Read method.
</span></span></span><span class="line"><span class="cl"><span class="c1">// Read reads up to len(p) bytes into p.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="p">(</span><span class="o">*</span><span class="n">Read</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">ReaderTable</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="n">ReaderTable</span><span class="o">*</span> <span class="n">mtab</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">self</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">Reader</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Reads len(p) bytes into p.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">size_t</span> <span class="nf">Reader_Read</span><span class="p">(</span><span class="n">Reader</span> <span class="n">r</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">r</span><span class="p">.</span><span class="n">mtab</span><span class="o">-&gt;</span><span class="nf">Read</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">self</span><span class="p">,</span> <span class="n">p</span><span class="p">,</span> <span class="n">len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// An infinite stream of zero bytes.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="n">total</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">Zeros</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Reads len(p) bytes into p.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">size_t</span> <span class="nf">Zeros_Read</span><span class="p">(</span><span class="n">Zeros</span><span class="o">*</span> <span class="n">z</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">len</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">p</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">256</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">z</span><span class="o">-&gt;</span><span class="n">total</span> <span class="o">+=</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Returns a Reader implementation for Zeros.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Reader</span> <span class="nf">Zeros_Reader</span><span class="p">(</span><span class="n">Zeros</span><span class="o">*</span> <span class="n">z</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// The method table is only initialized once.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">static</span> <span class="k">const</span> <span class="n">ReaderTable</span> <span class="n">impl</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">Read</span> <span class="o">=</span> <span class="p">(</span><span class="kt">size_t</span> <span class="p">(</span><span class="o">*</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span><span class="p">,</span> <span class="kt">size_t</span><span class="p">))</span><span class="n">Zeros_Read</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">Reader</span><span class="p">){.</span><span class="n">mtab</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">impl</span><span class="p">,</span> <span class="p">.</span><span class="n">self</span> <span class="o">=</span> <span class="n">z</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Does some work reading from r.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">size_t</span> <span class="nf">work</span><span class="p">(</span><span class="n">Reader</span> <span class="n">r</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">buf</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nf">Reader_Read</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Zeros</span> <span class="n">z</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Reader</span> <span class="n">r</span> <span class="o">=</span> <span class="nf">Zeros_Reader</span><span class="p">(</span><span class="o">&amp;</span><span class="n">z</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">work</span><span class="p">(</span><span class="n">r</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">work</span><span class="p">(</span><span class="n">r</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;total = %zu</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">z</span><span class="p">.</span><span class="n">total</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet sandbox="gcc" editor="basic" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">total = 16
</span></span></code></pre></div><p>Cheers!</p>
<script defer src="/modules/codapi/snippet.js"></script>
]]></content:encoded></item></channel></rss>