<?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>Fri, 03 Apr 2026 13:00:00 +0000</lastBuildDate><atom:link href="https://antonz.org/index.xml" rel="self" type="application/rss+xml"/><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. To keep things simple, there are no channels, goroutines, closures, or generics.</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="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">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="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>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="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_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><p>Methods on named primitive types are also supported.</p>
<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></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="k">const</span> <span class="n">main_ServerState</span> <span class="n">main_StateIdle</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="k">const</span> <span class="n">main_ServerState</span> <span class="n">main_StateConnected</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="k">const</span> <span class="n">main_ServerState</span> <span class="n">main_StateError</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="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">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">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></span><span class="line"><span class="cl"><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">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">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>Errors use the <code>so_Error</code> type (a 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">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><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>So only supports sentinel errors, which are defined at the package level using <code>errors.New</code> (implemented as compiler built-in):</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>), local error variables (<code>errors.New</code> inside functions), 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 scope.</p>
<p>The scope can be either a 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>Or a bare block (unlike 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">blockScope</span><span class="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="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="c1">// xclose(&amp;state) runs here, at block end
</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="c1">// state is already closed here
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>Deferred calls are emitted inline (before returns, panics, and scope 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><p>Defer is not supported inside other scopes like <code>for</code> or <code>if</code>.</p>
<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>
<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 generic methods. Concurrency features might be added, but only after the non-concurrent parts of the language and the standard library are stable.</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-and-usage">Installation and 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/solod/tree/main/example">So by example</a> •
<a href="https://codapi.org/so">Playground</a> •
<a href="https://github.com/solod-dev/solod/blob/main/bench/README.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><item><title>Go 1.26 interactive tour</title><link>https://antonz.org/go-1-26/</link><pubDate>Mon, 05 Jan 2026 13:00:00 +0000</pubDate><guid>https://antonz.org/go-1-26/</guid><description>New with expressions, type-safe error checking, and faster everything.</description><content:encoded><![CDATA[<p>Go 1.26 is out, so it's a good time to explore what's new. The official <a href="https://tip.golang.org/doc/go1.26">release notes</a> are pretty dry, so I prepared an interactive version with lots of examples showing what has changed and what the new behavior is.</p>
<p>Read on and see!</p>
<p><a href="#new-expr">new(expr)</a> •
<a href="#recursive-type-constraints">Recursive type constraints</a> •
<a href="#errors-astype">Type-safe error checking</a> •
<a href="#gc">Green Tea GC</a> •
<a href="#cgo">Faster cgo and syscalls</a> •
<a href="#malloc">Faster memory allocation</a> •
<a href="#simd">Vectorized operations</a> •
<a href="#runtime-secret">Secret mode</a> •
<a href="#crypto-reader">Reader-less cryptography</a> •
<a href="#crypto-hpke">Hybrid public key encryption</a> •
<a href="#pprof-goroutineleak">Goroutine leak profile</a> •
<a href="#runtime-goroutine-metrics">Goroutine metrics</a> •
<a href="#reflect-iter">Reflective iterators</a> •
<a href="#bytes-buffer-peek">Peek into a buffer</a> •
<a href="#os-process-handle">Process handle</a> •
<a href="#signal-cause">Signal as cause</a> •
<a href="#netip-prefix-compare">Compare IP subnets</a> •
<a href="#net-dialer-context">Context-aware dialing</a> •
<a href="#httptest-example">Fake example.com</a> •
<a href="#fmt-errorf">Optimized fmt.Errorf</a> •
<a href="#io-readall">Optimized io.ReadAll</a> •
<a href="#slog-multihandler">Multiple log handlers</a> •
<a href="#testing-artifacts">Test artifacts</a> •
<a href="#modernized-go-fix">Modernized go fix</a> •
<a href="#final-thoughts">Final thoughts</a></p>
<blockquote>
<p>This article is based on the official release notes from The Go Authors and the Go source code, licensed under the BSD-3-Clause license. This is not an exhaustive list; see the official release notes for that.</p>
</blockquote>
<p>I provide links to the documentation (𝗗), proposals (𝗣), commits (𝗖𝗟), and authors (𝗔) for the features described. Check them out for motivation, usage, and implementation details. I also have dedicated guides (𝗚) for some of the features.</p>
<p>Error handling is often skipped to keep things simple. Don't do this in production ツ</p>
<h2 id="new-expr">
    <a class="anchor" href="#new-expr">#</a>
     new(expr) 
</h2>
<p>Previously, you could only use the <code>new</code> built-in with 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="nx">p</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">*</span><span class="nx">p</span> <span class="p">=</span> <span class="mi">42</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="o">*</span><span class="nx">p</span><span class="p">)</span>
</span></span></code></pre></div><codapi-snippet sandbox="go:dev" editor="basic" template="main.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">42
</span></span></code></pre></div><p>Now you can also use it with expressions:</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">// Pointer to a int variable with the value 42.
</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="mi">42</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="o">*</span><span class="nx">p</span><span class="p">)</span>
</span></span></code></pre></div><codapi-snippet sandbox="go:dev" editor="basic" template="main.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">42
</span></span></code></pre></div><p>If the argument <code>expr</code> is an expression of type T, then <code>new(expr)</code> allocates a variable of type T, initializes it to the value of <code>expr</code>, and returns its address, a value of type <code>*T</code>.</p>
<p>This feature is especially helpful if you use pointer fields in a struct to represent optional values that you marshal to JSON or Protobuf:</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">Cat</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 class="s">`json:&#34;name&#34;`</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Fed</span>  <span class="o">*</span><span class="kt">bool</span>  <span class="s">`json:&#34;is_fed&#34;`</span> <span class="c1">// you can never be sure
</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="nx">cat</span> <span class="o">:=</span> <span class="nx">Cat</span><span class="p">{</span><span class="nx">Name</span><span class="p">:</span> <span class="s">&#34;Mittens&#34;</span><span class="p">,</span> <span class="nx">Fed</span><span class="p">:</span> <span class="nb">new</span><span class="p">(</span><span class="kc">true</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl"><span class="nx">data</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Marshal</span><span class="p">(</span><span class="nx">cat</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="nb">string</span><span class="p">(</span><span class="nx">data</span><span class="p">))</span>
</span></span></code></pre></div><codapi-snippet sandbox="go:dev" editor="basic" template="main.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">{&#34;name&#34;:&#34;Mittens&#34;,&#34;is_fed&#34;:true}
</span></span></code></pre></div><p>You can use <code>new</code> with composite 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="nx">s</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="mi">11</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">13</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="o">*</span><span class="nx">s</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">Person</span> <span class="kd">struct</span><span class="p">{</span> <span class="nx">name</span> <span class="kt">string</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><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="nx">name</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">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="o">*</span><span class="nx">p</span><span class="p">)</span>
</span></span></code></pre></div><codapi-snippet sandbox="go:dev" editor="basic" template="main.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">[11 12 13]
</span></span><span class="line"><span class="cl">{alice}
</span></span></code></pre></div><p>And function calls:</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">f</span> <span class="o">:=</span> <span class="kd">func</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span> <span class="k">return</span> <span class="s">&#34;go&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">p</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="nf">f</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="o">*</span><span class="nx">p</span><span class="p">)</span>
</span></span></code></pre></div><codapi-snippet sandbox="go:dev" editor="basic" template="main.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">go
</span></span></code></pre></div><p>Passing <code>nil</code> is still not allowed:</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">p</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">// compilation error
</span></span></span></code></pre></div><p>𝗗 <a href="https://tip.golang.org/ref/spec#Allocation">spec</a> •
𝗣 <a href="https://go.dev/issue/45624">45624</a> •
𝗖𝗟 <a href="https://go.dev/cl/704935">704935</a>, <a href="https://go.dev/cl/704737">704737</a>, <a href="https://go.dev/cl/704955">704955</a>, <a href="https://go.dev/cl/705157">705157</a> •
𝗔 <a href="https://github.com/adonovan">Alan Donovan</a></p>
<h2 id="recursive-type-constraints">
    <a class="anchor" href="#recursive-type-constraints">#</a>
     Recursive type constraints 
</h2>
<p>Generic functions and types take types as parameters:</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">// A list of values.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">List</span><span class="p">[</span><span class="nx">T</span> <span class="nx">any</span><span class="p">]</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="c1">// Reverses a slice in-place.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nx">Reverse</span><span class="p">[</span><span class="nx">T</span> <span class="nx">any</span><span class="p">](</span><span class="nx">s</span> <span class="p">[]</span><span class="nx">T</span><span class="p">)</span>
</span></span></code></pre></div><p>We can further restrict these type parameters by using type constraints:</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">// The map key must have a comparable type.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Map</span><span class="p">[</span><span class="nx">K</span> <span class="nx">comparable</span><span class="p">,</span> <span class="nx">V</span> <span class="nx">any</span><span class="p">]</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="c1">// S is a slice with values of a comparable type,
</span></span></span><span class="line"><span class="cl"><span class="c1">// or a type derived from such a slice (e.g., type MySlice []int).
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nx">Compact</span><span class="p">[</span><span class="nx">S</span> <span class="p">~[]</span><span class="nx">E</span><span class="p">,</span> <span class="nx">E</span> <span class="nx">comparable</span><span class="p">](</span><span class="nx">s</span> <span class="nx">S</span><span class="p">)</span> <span class="nx">S</span>
</span></span></code></pre></div><p>Previously, type constraints couldn't directly or indirectly refer back to the generic 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="kd">type</span> <span class="nx">T</span><span class="p">[</span><span class="nx">P</span> <span class="nx">T</span><span class="p">[</span><span class="nx">P</span><span class="p">]]</span> <span class="kd">struct</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// compile error:
</span></span></span><span class="line"><span class="cl"><span class="c1">// invalid recursive type: T refers to itself
</span></span></span></code></pre></div><p>Now they can:</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">T</span><span class="p">[</span><span class="nx">P</span> <span class="nx">T</span><span class="p">[</span><span class="nx">P</span><span class="p">]]</span> <span class="kd">struct</span><span class="p">{}</span>
</span></span></code></pre></div><codapi-snippet sandbox="go:dev" editor="basic" template="type.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">ok
</span></span></code></pre></div><p>A typical use case is a generic type that supports operations with arguments or results of the same type as itself:</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">// A value that can be compared to other values
</span></span></span><span class="line"><span class="cl"><span class="c1">// of the same type using the less-than operation.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Ordered</span><span class="p">[</span><span class="nx">T</span> <span class="nx">Ordered</span><span class="p">[</span><span class="nx">T</span><span class="p">]]</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">Less</span><span class="p">(</span><span class="nx">T</span><span class="p">)</span> <span class="kt">bool</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet id="s-type-ordered" editor="basic">
</codapi-snippet>
<p>Now we can create a generic container with <code>Ordered</code> values and use it with any type that implements <code>Less</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">// A tree stores comparable values.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Tree</span><span class="p">[</span><span class="nx">T</span> <span class="nx">Ordered</span><span class="p">[</span><span class="nx">T</span><span class="p">]]</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">nodes</span> <span class="p">[]</span><span class="nx">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="c1">// netip.Addr has a Less method with the right signature,
</span></span></span><span class="line"><span class="cl"><span class="c1">// so it meets the requirements for Ordered[netip.Addr].
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">t</span> <span class="o">:=</span> <span class="nx">Tree</span><span class="p">[</span><span class="nx">netip</span><span class="p">.</span><span class="nx">Addr</span><span class="p">]{}</span>
</span></span><span class="line"><span class="cl"><span class="nx">_</span> <span class="p">=</span> <span class="nx">t</span>
</span></span></code></pre></div><codapi-snippet sandbox="go:dev" editor="basic" template="type.go" depends-on="s-type-ordered" 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>This makes Go's generics a bit more expressive.</p>
<p>𝗣 <a href="https://go.dev/issue/68162">68162</a>, <a href="https://go.dev/issue/75883">75883</a> •
𝗖𝗟 <a href="https://go.dev/cl/711420">711420</a>, <a href="https://go.dev/cl/711422">711422</a> •
𝗔 <a href="https://github.com/griesemer">Robert Griesemer</a></p>
<h2 id="errors-astype">
    <a class="anchor" href="#errors-astype">#</a>
     Type-safe error checking 
</h2>
<p>The new <code>errors.AsType</code> function is a generic version of <code>errors.As</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">// go 1.13+
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">As</span><span class="p">(</span><span class="nx">err</span> <span class="kt">error</span><span class="p">,</span> <span class="nx">target</span> <span class="nx">any</span><span class="p">)</span> <span class="kt">bool</span>
</span></span><span class="line"><span class="cl"><span class="c1">// go 1.26+
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nx">AsType</span><span class="p">[</span><span class="nx">E</span> <span class="kt">error</span><span class="p">](</span><span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">(</span><span class="nx">E</span><span class="p">,</span> <span class="kt">bool</span><span class="p">)</span>
</span></span></code></pre></div><p>It's type-safe and easier to use:</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">// using errors.As
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">target</span> <span class="o">*</span><span class="nx">AppError</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">As</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">target</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">Println</span><span class="p">(</span><span class="s">&#34;application error:&#34;</span><span class="p">,</span> <span class="nx">target</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" template="errors-1.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">application error: database is down
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// using errors.AsType
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="nx">target</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">errors</span><span class="p">.</span><span class="nx">AsType</span><span class="p">[</span><span class="o">*</span><span class="nx">AppError</span><span class="p">](</span><span class="nx">err</span><span class="p">);</span> <span class="nx">ok</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;application error:&#34;</span><span class="p">,</span> <span class="nx">target</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:dev" editor="basic" template="errors-1.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">application error: database is down
</span></span></code></pre></div><p><code>AsType</code> is especially handy when checking for multiple types of errors. It makes the code shorter and keeps error variables scoped to their <code>if</code> blocks:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">connErr</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">errors</span><span class="p">.</span><span class="nx">AsType</span><span class="p">[</span><span class="o">*</span><span class="nx">net</span><span class="p">.</span><span class="nx">OpError</span><span class="p">](</span><span class="nx">err</span><span class="p">);</span> <span class="nx">ok</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;Network operation failed:&#34;</span><span class="p">,</span> <span class="nx">connErr</span><span class="p">.</span><span class="nx">Op</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">dnsErr</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">errors</span><span class="p">.</span><span class="nx">AsType</span><span class="p">[</span><span class="o">*</span><span class="nx">net</span><span class="p">.</span><span class="nx">DNSError</span><span class="p">](</span><span class="nx">err</span><span class="p">);</span> <span class="nx">ok</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;DNS resolution failed:&#34;</span><span class="p">,</span> <span class="nx">dnsErr</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 class="k">else</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;Unknown error&#34;</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:dev" editor="basic" template="errors-2.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">DNS resolution failed: antonz.org
</span></span></code></pre></div><p>Another issue with <code>As</code> is that it uses reflection and can cause runtime panics if used incorrectly (like if you pass a non-pointer or a type that doesn't implement <code>error</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">// using errors.As
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">target</span> <span class="nx">AppError</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">As</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">target</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">Println</span><span class="p">(</span><span class="s">&#34;application error:&#34;</span><span class="p">,</span> <span class="nx">target</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" template="errors-1.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">panic: errors: *target must be interface or implement error
</span></span></code></pre></div><p><code>AsType</code> doesn't cause a runtime panic; it gives a clear compile-time error 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">// using errors.AsType
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="nx">target</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">errors</span><span class="p">.</span><span class="nx">AsType</span><span class="p">[</span><span class="nx">AppError</span><span class="p">](</span><span class="nx">err</span><span class="p">);</span> <span class="nx">ok</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;application error:&#34;</span><span class="p">,</span> <span class="nx">target</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:dev" editor="basic" template="errors-1.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">./main.go:24:32: AppError does not satisfy error (method Error has pointer receiver)
</span></span></code></pre></div><p><code>AsType</code> doesn't use <code>reflect</code>, executes faster, and allocates less than <code>As</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">goos: darwin
</span></span><span class="line"><span class="cl">goarch: arm64
</span></span><span class="line"><span class="cl">cpu: Apple M1
</span></span><span class="line"><span class="cl">BenchmarkAs-8        12606744    95.62 ns/op    40 B/op    2 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkAsType-8    37961869    30.26 ns/op    24 B/op    1 allocs/op
</span></span></code></pre></div><p><a href="https://github.com/golang/go/blob/release-branch.go1.26/src/errors/wrap_test.go">source</a></p>
<p>Since <code>AsType</code> can handle everything that <code>As</code> does, it's a recommended drop-in replacement for new code.</p>
<p>𝗗 <a href="https://pkg.go.dev/errors@go1.26.0#AsType">errors.AsType</a> •
𝗣 <a href="https://go.dev/issue/51945">51945</a> •
𝗖𝗟 <a href="https://go.dev/cl/707235">707235</a> •
𝗔 <a href="https://github.com/jub0bs">Julien Cretel</a></p>
<h2 id="gc">
    <a class="anchor" href="#gc">#</a>
     Green Tea garbage collector 
</h2>
<p>The new garbage collector (first introduced as experimental in 1.25) is designed to make memory management more efficient on modern computers with many CPU cores.</p>
<h3 id="motivation">Motivation</h3>
<p>Go's traditional garbage collector algorithm operates on graph, treating objects as nodes and pointers as edges, without considering their physical location in memory. The scanner jumps between distant memory locations, causing frequent cache misses.</p>
<p>As a result, the CPU spends too much time waiting for data to arrive from memory. More than 35% of the time spent scanning memory is wasted just stalling while waiting for memory accesses. As computers get more CPU cores, this problem gets even worse.</p>
<h3 id="implementation">Implementation</h3>
<p>Green Tea shifts the focus from being processor-centered to being memory-aware. Instead of scanning individual objects, it scans memory in contiguous 8 KiB blocks called <em>spans</em>. The algorithm focuses on small objects (up to 512 bytes) because they are the most common and hardest to scan efficiently.</p>
<p>Each span is divided into equal slots based on its assigned <em>size class</em>, and it only contains objects of that size class. For example, if a span is assigned to the 32-byte size class, the whole block is split into 32-byte slots, and objects are placed directly into these slots, each starting at the beginning of its slot. Because of this fixed layout, the garbage collector can easily find an object's metadata using simple address arithmetic, without checking the size of each object it finds.</p>
<p>When the algorithm finds an object that needs to be scanned, it marks the object's location in its span but doesn't scan it immediately. Instead, it waits until there are several objects in the same span that need scanning. Then, when the garbage collector processes that span, it scans multiple objects at once. This is much faster than going over the same area of memory multiple times.</p>
<p>To make better use of CPU cores, GC workers share the workload by stealing tasks from each other. Each worker has its own local queue of spans to scan, and if a worker is idle, it can grab tasks from the queues of other busy workers. This decentralized approach removes the need for a central global list, prevents delays, and reduces contention between CPU cores.</p>
<p>Green Tea uses vectorized CPU instructions (only on amd64 architectures) to process memory spans in bulk when there are enough objects.</p>
<h3 id="benchmarks">Benchmarks</h3>
<p>Benchmark results vary, but the Go team expects a 10–40% reduction in garbage collection overhead in real-world programs that rely heavily on the garbage collector. Plus, with vectorized implementation, an extra 10% reduction in GC overhead when running on CPUs like Intel Ice Lake or AMD Zen 4 and newer.</p>
<blockquote>
<p>Unfortunately, I couldn't find any public benchmark results from the Go team for the latest version of Green Tea, and I wasn't able to create a good synthetic benchmark myself. So, no details this time :(</p>
</blockquote>
<p>The new garbage collector is enabled by default. To use the old garbage collector, set <code>GOEXPERIMENT=nogreenteagc</code> at build time (this option is expected to be removed in Go 1.27).</p>
<p>𝗣 <a href="https://go.dev/issue/73581">73581</a> •
𝗔 <a href="https://github.com/mknyszek">Michael Knyszek</a></p>
<h2 id="cgo">
    <a class="anchor" href="#cgo">#</a>
     Faster cgo and syscalls 
</h2>
<p>In the Go runtime, a <em>processor</em> (often referred to as a P) is a resource required to run the code. For a thread (a <em>machine</em> or M) to execute a goroutine (G), it must first acquire a processor.</p>
<p>Processors move through different states. They can be <code>running</code> (executing code), <code>idle</code> (waiting for work), or <code>gcstop</code> (paused because of the garbage collection).</p>
<p>Previously, processors had a state called <code>syscall</code> used when a goroutine is making a system or cgo call. Now, this state has been removed. Instead of using a separate processor state, the system now checks the status of the goroutine assigned to the processor to see if it's involved in a system call.</p>
<p>This reduces internal runtime overhead and simplifies code paths for cgo and syscalls. The Go release notes say -30% in cgo runtime overhead, and the commit mentions an 18% sec/op improvement:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">goos: linux
</span></span><span class="line"><span class="cl">goarch: amd64
</span></span><span class="line"><span class="cl">pkg: internal/runtime/cgobench
</span></span><span class="line"><span class="cl">cpu: AMD EPYC 7B13
</span></span><span class="line"><span class="cl">                   │ before.out  │             after.out              │
</span></span><span class="line"><span class="cl">                   │   sec/op    │   sec/op     vs base               │
</span></span><span class="line"><span class="cl">CgoCall-64           43.69n ± 1%   35.83n ± 1%  -17.99% (p=0.002 n=6)
</span></span><span class="line"><span class="cl">CgoCallParallel-64   5.306n ± 1%   5.338n ± 1%        ~ (p=0.132 n=6)
</span></span></code></pre></div><p>I decided to run the <a href="https://github.com/golang/go/blob/release-branch.go1.26/src/internal/runtime/cgobench/bench_test.go">CgoCall</a> benchmarks locally as well:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">goos: darwin
</span></span><span class="line"><span class="cl">goarch: arm64
</span></span><span class="line"><span class="cl">cpu: Apple M1
</span></span><span class="line"><span class="cl">                      │ go1_25.txt  │             go1_26.txt              │
</span></span><span class="line"><span class="cl">                      │   sec/op    │   sec/op     vs base                │
</span></span><span class="line"><span class="cl">CgoCall-8               28.55n ± 4%   19.02n ± 2%  -33.40% (p=0.000 n=10)
</span></span><span class="line"><span class="cl">CgoCallWithCallback-8   72.76n ± 5%   57.38n ± 2%  -21.14% (p=0.000 n=10)
</span></span><span class="line"><span class="cl">geomean                 45.58n        33.03n       -27.53%
</span></span></code></pre></div><p>Either way, both a 20% and a 30% improvement are pretty impressive.</p>
<p>And here are the results from a local syscall benchmark:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">goos: darwin
</span></span><span class="line"><span class="cl">goarch: arm64
</span></span><span class="line"><span class="cl">cpu: Apple M1
</span></span><span class="line"><span class="cl">          │ go1_25.txt  │             go1_26.txt             │
</span></span><span class="line"><span class="cl">          │   sec/op    │   sec/op     vs base               │
</span></span><span class="line"><span class="cl">Syscall-8   195.6n ± 4%   178.1n ± 1%  -8.95% (p=0.000 n=10)
</span></span></code></pre></div><details>
    <summary>source</summary>
<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">BenchmarkSyscall</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="nx">b</span><span class="p">.</span><span class="nf">Loop</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">_</span><span class="p">,</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">_</span> <span class="p">=</span> <span class="nx">syscall</span><span class="p">.</span><span class="nf">Syscall</span><span class="p">(</span><span class="nx">syscall</span><span class="p">.</span><span class="nx">SYS_GETPID</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></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></details>
<p>That's pretty good too.</p>
<p>𝗖𝗟 <a href="https://go.dev/cl/646198">646198</a> •
𝗔 <a href="https://github.com/mknyszek">Michael Knyszek</a></p>
<h2 id="malloc">
    <a class="anchor" href="#malloc">#</a>
     Faster memory allocation 
</h2>
<p>The Go runtime now has specialized versions of its memory allocation function for small objects (from 1 to 512 bytes). It uses jump tables to quickly choose the right function for each size, instead of relying on a single general-purpose implementation.</p>
<blockquote>
<p>The Go release notes say &quot;the compiler will now generate calls to size-specialized memory allocation routines&quot;. But based on the code, that's not completely accurate: the compiler still emits calls to the general-purpose <code>mallocgc</code> function. Then, at runtime, <code>mallocgc</code> dispatches those calls to the new specialized allocation functions.</p>
</blockquote>
<p>This change reduces the cost of small object memory allocations by up to 30%. The Go team expects the overall improvement to be ~1% in real allocation-heavy programs.</p>
<p>I couldn't find any existing benchmarks, so I came up with my own. And indeed, running it on Go 1.25 compared to 1.26 shows a significant improvement:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">goos: darwin
</span></span><span class="line"><span class="cl">goarch: arm64
</span></span><span class="line"><span class="cl">cpu: Apple M1
</span></span><span class="line"><span class="cl">           │  go1_25.txt   │              go1_26.txt              │
</span></span><span class="line"><span class="cl">           │    sec/op     │    sec/op     vs base                │
</span></span><span class="line"><span class="cl">Alloc1-8      8.190n ±  6%   6.594n ± 28%  -19.48% (p=0.011 n=10)
</span></span><span class="line"><span class="cl">Alloc8-8      8.648n ± 16%   7.522n ±  4%  -13.02% (p=0.000 n=10)
</span></span><span class="line"><span class="cl">Alloc64-8     15.70n ± 15%   12.57n ±  4%  -19.88% (p=0.000 n=10)
</span></span><span class="line"><span class="cl">Alloc128-8    56.80n ±  4%   17.56n ±  4%  -69.08% (p=0.000 n=10)
</span></span><span class="line"><span class="cl">Alloc512-8    81.50n ± 10%   55.24n ±  5%  -32.23% (p=0.000 n=10)
</span></span><span class="line"><span class="cl">geomean       21.99n         14.33n        -34.83%
</span></span></code></pre></div><details>
    <summary>source</summary>
<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">sink</span> <span class="o">*</span><span class="kt">byte</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">benchmarkAlloc</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">,</span> <span class="nx">size</span> <span class="kt">int</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="nf">ReportAllocs</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="nx">b</span><span class="p">.</span><span class="nf">Loop</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">obj</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="nx">sink</span> <span class="p">=</span> <span class="o">&amp;</span><span class="nx">obj</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="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">BenchmarkAlloc1</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span>   <span class="p">{</span> <span class="nf">benchmarkAlloc</span><span class="p">(</span><span class="nx">b</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkAlloc8</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span>   <span class="p">{</span> <span class="nf">benchmarkAlloc</span><span class="p">(</span><span class="nx">b</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkAlloc64</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span>  <span class="p">{</span> <span class="nf">benchmarkAlloc</span><span class="p">(</span><span class="nx">b</span><span class="p">,</span> <span class="mi">64</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkAlloc128</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span> <span class="nf">benchmarkAlloc</span><span class="p">(</span><span class="nx">b</span><span class="p">,</span> <span class="mi">128</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkAlloc512</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span> <span class="nf">benchmarkAlloc</span><span class="p">(</span><span class="nx">b</span><span class="p">,</span> <span class="mi">512</span><span class="p">)</span> <span class="p">}</span>
</span></span></code></pre></div></details>
<p>The new implementation is enabled by default. You can disable it by setting <code>GOEXPERIMENT=nosizespecializedmalloc</code> at build time (this option is expected to be removed in Go 1.27).</p>
<p>𝗖𝗟 <a href="https://go.dev/cl/665835">665835</a> •
𝗔 <a href="https://github.com/matloob">Michael Matloob</a></p>
<h2 id="simd">
    <a class="anchor" href="#simd">#</a>
     Vectorized operations (experimental) 
</h2>
<p>The new <code>simd/archsimd</code> package provides access to architecture-specific vectorized operations (SIMD — single instruction, multiple data). This is a low-level package that exposes hardware-specific functionality. It currently only supports amd64 platforms.</p>
<p>Because different CPU architectures have very different SIMD operations, it's hard to create a single portable API that works for all of them. So the Go team decided to start with a low-level, architecture-specific API first, giving &quot;power users&quot; immediate access to SIMD features on the most common server platform — amd64.</p>
<p>The package defines vector types as structs, like <code>Int8x16</code> (a 128-bit SIMD vector with sixteen 8-bit integers) and <code>Float64x8</code> (a 512-bit SIMD vector with eight 64-bit floats). These match the hardware's vector registers. The package supports vectors that are 128, 256, or 512 bits wide.</p>
<p>Most operations are defined as methods on vector types. They usually map directly to hardware instructions with zero overhead.</p>
<p>To give you a taste, here's a custom function that uses SIMD instructions to add 32-bit float vectors:</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">Add</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">float32</span><span class="p">)</span> <span class="p">[]</span><span class="kt">float32</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">len</span><span class="p">(</span><span class="nx">b</span><span class="p">)</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;slices of different length&#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="c1">// If AVX-512 isn&#39;t supported, fall back to scalar addition,
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// since the Float32x16.Add method needs the AVX-512 instruction set.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">!</span><span class="nx">archsimd</span><span class="p">.</span><span class="nx">X86</span><span class="p">.</span><span class="nf">AVX512</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">fallbackAdd</span><span class="p">(</span><span class="nx">a</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><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">res</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">float32</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="nx">a</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="nx">n</span> <span class="o">:=</span> <span class="nb">len</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. SIMD loop: Process 16 elements at a time.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">for</span> <span class="nx">i</span> <span class="o">&lt;=</span> <span class="nx">n</span><span class="o">-</span><span class="mi">16</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Load 16 elements from a and b vectors.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nx">va</span> <span class="o">:=</span> <span class="nx">archsimd</span><span class="p">.</span><span class="nf">LoadFloat32x16Slice</span><span class="p">(</span><span class="nx">a</span><span class="p">[</span><span class="nx">i</span> <span class="p">:</span> <span class="nx">i</span><span class="o">+</span><span class="mi">16</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">        <span class="nx">vb</span> <span class="o">:=</span> <span class="nx">archsimd</span><span class="p">.</span><span class="nf">LoadFloat32x16Slice</span><span class="p">(</span><span class="nx">b</span><span class="p">[</span><span class="nx">i</span> <span class="p">:</span> <span class="nx">i</span><span class="o">+</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="c1">// Add all 16 elements in a single instruction
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="c1">// and store the results in the result vector.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nx">vSum</span> <span class="o">:=</span> <span class="nx">va</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="nx">vb</span><span class="p">)</span> <span class="c1">// translates to VADDPS asm instruction
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nx">vSum</span><span class="p">.</span><span class="nf">StoreSlice</span><span class="p">(</span><span class="nx">res</span><span class="p">[</span><span class="nx">i</span> <span class="p">:</span> <span class="nx">i</span><span class="o">+</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="nx">i</span> <span class="o">+=</span> <span class="mi">16</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">// 2. Scalar tail: Process any remaining elements (0-15).
</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">i</span> <span class="p">&lt;</span> <span class="nx">n</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">res</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="p">=</span> <span class="nx">a</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">+</span> <span class="nx">b</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></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">res</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet id="s-simd-add" editor="basic">
</codapi-snippet>
<p>Let's try it on two vectors:</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">a</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">float32</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">6</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 class="mi">10</span><span class="p">,</span> <span class="mi">11</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="mi">15</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">17</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nx">b</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">float32</span><span class="p">{</span><span class="mi">17</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">15</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">11</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nx">res</span> <span class="o">:=</span> <span class="nf">Add</span><span class="p">(</span><span class="nx">a</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">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">res</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:dev" editor="basic" template="simd.go" depends-on="s-simd-add" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">[18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18]
</span></span></code></pre></div><p>Common operations in the <code>archsimd</code> package include:</p>
<ul>
<li><code>Load</code> a vector from array/slice, or <code>Store</code> a vector to array/slice.</li>
<li>Arithmetic: <code>Add</code>, <code>Sub</code>, <code>Mul</code>, <code>Div</code>, <code>DotProduct</code>.</li>
<li>Bitwise: <code>And</code>, <code>Or</code>, <code>Not</code>, <code>Xor</code>, <code>Shift</code>.</li>
<li>Comparison: <code>Equal</code>, <code>Greater</code>, <code>Less</code>, <code>Min</code>, <code>Max</code>.</li>
<li>Conversion: <code>As</code>, <code>SaturateTo</code>, <code>TruncateTo</code>.</li>
<li>Masking: <code>Compress</code>, <code>Masked</code>, <code>Merge</code>.</li>
<li>Rearrangement: <code>Permute</code>.</li>
</ul>
<p>The package uses only AVX instructions, not SSE.</p>
<p>Here's a simple benchmark for adding two vectors (both the &quot;plain&quot; and SIMD versions use pre-allocated slices):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">goos: linux
</span></span><span class="line"><span class="cl">goarch: amd64
</span></span><span class="line"><span class="cl">cpu: AMD EPYC 9575F 64-Core Processor
</span></span><span class="line"><span class="cl">BenchmarkAddPlain/1k-2         	 1517698	       889.9 ns/op	13808.74 MB/s
</span></span><span class="line"><span class="cl">BenchmarkAddPlain/65k-2        	   23448	     52613 ns/op	14947.46 MB/s
</span></span><span class="line"><span class="cl">BenchmarkAddPlain/1m-2         	    2047	   1005628 ns/op	11932.84 MB/s
</span></span><span class="line"><span class="cl">BenchmarkAddSIMD/1k-2          	36594340	        33.58 ns/op	365949.74 MB/s
</span></span><span class="line"><span class="cl">BenchmarkAddSIMD/65k-2         	  410742	      3199 ns/op	245838.52 MB/s
</span></span><span class="line"><span class="cl">BenchmarkAddSIMD/1m-2          	   12955	     94228 ns/op	127351.33 MB/s
</span></span></code></pre></div><p><a href="https://go.dev/play/p/4_nCOcP3voQ?v=gotip">source</a></p>
<p>The package is experimental and can be enabled by setting <code>GOEXPERIMENT=simd</code> at build time.</p>
<p>𝗗 <a href="https://pkg.go.dev/simd/archsimd@go1.26.0">simd/archsimd</a> •
𝗣 <a href="https://go.dev/issue/73787">73787</a> •
𝗖𝗟 <a href="https://go.dev/cl/701915">701915</a>, <a href="https://go.dev/cl/712880">712880</a>, <a href="https://go.dev/cl/729900">729900</a>, <a href="https://go.dev/cl/732020">732020</a> •
𝗔 <a href="https://github.com/JunyangShao">Junyang Shao</a>, <a href="https://github.com/seankhliao">Sean Liao</a>, <a href="https://github.com/tmthrgd">Tom Thorogood</a></p>
<h2 id="runtime-secret">
    <a class="anchor" href="#runtime-secret">#</a>
     Secret mode (experimental) 
</h2>
<p>Cryptographic protocols like WireGuard or TLS have a property called &quot;forward secrecy&quot;. This means that even if an attacker gains access to long-term secrets (like a private key in TLS), they shouldn't be able to decrypt past communication sessions. To make this work, ephemeral keys (temporary keys used to negotiate the session) need to be erased from memory immediately after the handshake. If there's no reliable way to clear this memory, these keys could stay there indefinitely. An attacker who finds them later could re-derive the session key and decrypt past traffic, breaking forward secrecy.</p>
<p>In Go, the runtime manages memory, and it doesn't guarantee when or how memory is cleared. Sensitive data might remain in heap allocations or stack frames, potentially exposed in core dumps or through memory attacks. Developers often have to use unreliable &quot;hacks&quot; with reflection to try to zero out internal buffers in cryptographic libraries. Even so, some data might still stay in memory where the developer can't reach or control it.</p>
<p>The Go team's solution to this problem is the new <code>runtime/secret</code> package. It lets you run a function in <em>secret mode</em>. After the function finishes, it immediately erases (zeroes out) the registers and stack it used. Heap allocations made by the function are erased as soon as the garbage collector decides they are no longer reachable.</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">secret</span><span class="p">.</span><span class="nf">Do</span><span class="p">(</span><span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Generate an ephemeral key and
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// use it to negotiate the session.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">})</span>
</span></span></code></pre></div><p>This helps make sure sensitive information doesn't stay in memory longer than needed, lowering the risk of attackers getting to it.</p>
<p>Here's an example that shows how <code>secret.Do</code> might be used in a more or less realistic setting. Let's say you want to generate a session key while keeping the ephemeral private key and shared secret safe:</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">// DeriveSessionKey does an ephemeral key exchange to create a session key.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">DeriveSessionKey</span><span class="p">(</span><span class="nx">peerPublicKey</span> <span class="o">*</span><span class="nx">ecdh</span><span class="p">.</span><span class="nx">PublicKey</span><span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="nx">ecdh</span><span class="p">.</span><span class="nx">PublicKey</span><span class="p">,</span> <span class="p">[]</span><span class="kt">byte</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">pubKey</span> <span class="o">*</span><span class="nx">ecdh</span><span class="p">.</span><span class="nx">PublicKey</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">sessionKey</span> <span class="p">[]</span><span class="kt">byte</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">err</span> <span class="kt">error</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Use secret.Do to contain the sensitive data during the handshake.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// The ephemeral private key and the raw shared secret will be
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// wiped out when this function finishes.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">secret</span><span class="p">.</span><span class="nf">Do</span><span class="p">(</span><span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 1. Generate an ephemeral private key.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="c1">// This is highly sensitive; if leaked later, forward secrecy is broken.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nx">privKey</span><span class="p">,</span> <span class="nx">e</span> <span class="o">:=</span> <span class="nx">ecdh</span><span class="p">.</span><span class="nf">P256</span><span class="p">().</span><span class="nf">GenerateKey</span><span class="p">(</span><span class="nx">rand</span><span class="p">.</span><span class="nx">Reader</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nx">e</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">e</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></span><span class="line"><span class="cl">        <span class="c1">// 2. Compute the shared secret (ECDH).
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="c1">// This raw secret is also highly sensitive.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nx">sharedSecret</span><span class="p">,</span> <span class="nx">e</span> <span class="o">:=</span> <span class="nx">privKey</span><span class="p">.</span><span class="nf">ECDH</span><span class="p">(</span><span class="nx">peerPublicKey</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nx">e</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">e</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></span><span class="line"><span class="cl">        <span class="c1">// 3. Derive the final session key (e.g., using HKDF).
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="c1">// We copy the result out; the inputs (privKey, sharedSecret)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="c1">// will be destroyed by secret.Do when they become unreachable.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nx">sessionKey</span> <span class="p">=</span> <span class="nf">performHKDF</span><span class="p">(</span><span class="nx">sharedSecret</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nx">pubKey</span> <span class="p">=</span> <span class="nx">privKey</span><span class="p">.</span><span class="nf">PublicKey</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">// The session key is returned for use, but the &#34;recipe&#34; to recreate it
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// is destroyed. Additionally, because the session key was allocated
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// inside the secret block, the runtime will automatically zero it out
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// when the application is finished using it.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">return</span> <span class="nx">pubKey</span><span class="p">,</span> <span class="nx">sessionKey</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><codapi-snippet id="s-secret-derive" editor="basic">
</codapi-snippet>
<p>Here, the ephemeral private key and the raw shared secret are effectively &quot;toxic waste&quot; — they are necessary to create the final session key, but dangerous to keep around.</p>
<p>If these values stay in the heap and an attacker later gets access to the application's memory (for example, via a core dump or a vulnerability like Heartbleed), they could use these intermediates to re-derive the session key and decrypt past conversations.</p>
<p>By wrapping the calculation in <code>secret.Do</code>, we make sure that as soon as the session key is created, the &quot;ingredients&quot; used to make it are permanently destroyed. This means that even if the server is compromised in the future, this specific past session can't be exposed, which ensures forward secrecy.</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="c1">// Generate a dummy peer public key.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">priv</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">ecdh</span><span class="p">.</span><span class="nf">P256</span><span class="p">().</span><span class="nf">GenerateKey</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">peerPubKey</span> <span class="o">:=</span> <span class="nx">priv</span><span class="p">.</span><span class="nf">PublicKey</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Derive the session key.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">pubKey</span><span class="p">,</span> <span class="nx">sessionKey</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">DeriveSessionKey</span><span class="p">(</span><span class="nx">peerPubKey</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;public key = %x...\n&#34;</span><span class="p">,</span> <span class="nx">pubKey</span><span class="p">.</span><span class="nf">Bytes</span><span class="p">()[:</span><span class="mi">16</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;error = %v\n&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">_</span> <span class="p">=</span> <span class="nx">sessionKey</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet sandbox="go:dev" editor="basic" template="secret.go" depends-on="s-secret-derive" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">public key = 04288d5ade66bab4320a86d80993f628...
</span></span><span class="line"><span class="cl">error = &lt;nil&gt;
</span></span></code></pre></div><p>The current <code>secret.Do</code> implementation only supports Linux (amd64 and arm64). On unsupported platforms, <code>Do</code> invokes the function directly. Also, trying to start a goroutine within the function causes a panic (this will be fixed in Go 1.27).</p>
<p>The <code>runtime/secret</code> package is mainly for developers who work on cryptographic libraries. Most apps should use higher-level libraries that use <code>secret.Do</code> behind the scenes.</p>
<p>The package is experimental and can be enabled by setting <code>GOEXPERIMENT=runtimesecret</code> at build time.</p>
<p>𝗗 <a href="https://pkg.go.dev/runtime/secret@go1.26.0">runtime/secret</a> •
𝗣 <a href="https://go.dev/issue/21865">21865</a> •
𝗖𝗟 <a href="https://go.dev/cl/704615">704615</a> •
𝗔 <a href="https://github.com/DanielMorsing">Daniel Morsing</a></p>
<h2 id="crypto-reader">
    <a class="anchor" href="#crypto-reader">#</a>
     Reader-less cryptography 
</h2>
<p>Current cryptographic APIs, like <code>ecdsa.GenerateKey</code> or <code>rand.Prime</code>, often accept an <code>io.Reader</code> as the source of random data:</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">// Generate a new ECDSA private key for the specified curve.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">key</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">ecdsa</span><span class="p">.</span><span class="nf">GenerateKey</span><span class="p">(</span><span class="nx">elliptic</span><span class="p">.</span><span class="nf">P256</span><span class="p">(),</span> <span class="nx">rand</span><span class="p">.</span><span class="nx">Reader</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="nx">key</span><span class="p">.</span><span class="nx">D</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Generate a 64-bit integer that is prime with high probability.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">prim</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">rand</span><span class="p">.</span><span class="nf">Prime</span><span class="p">(</span><span class="nx">rand</span><span class="p">.</span><span class="nx">Reader</span><span class="p">,</span> <span class="mi">64</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="nx">prim</span><span class="p">)</span>
</span></span></code></pre></div><codapi-snippet sandbox="go" editor="basic" template="crypto-main.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">31253152889057471714062019675387570049552680140182252615946165331094890182019
</span></span><span class="line"><span class="cl">17433987073571224703
</span></span></code></pre></div><p>These APIs don't commit to a specific way of using random bytes from the reader. Any change to underlying cryptographic algorithms can change the sequence or amount of bytes read. Because of this, if the application code (mistakenly) relies on a specific implementation in Go version X, it might fail or behave differently in version X+1.</p>
<p>The Go team chose a pretty bold solution to this problem. Now, most crypto APIs will just ignore the random <code>io.Reader</code> parameter and always use the system random source (<code>crypto/internal/sysrand.Read</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">// The reader parameter is no longer used, so you can just pass nil.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// Generate a new ECDSA private key for the specified curve.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">key</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">ecdsa</span><span class="p">.</span><span class="nf">GenerateKey</span><span class="p">(</span><span class="nx">elliptic</span><span class="p">.</span><span class="nf">P256</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">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">key</span><span class="p">.</span><span class="nx">D</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Generate a 64-bit integer that is prime with high probability.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">prim</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">rand</span><span class="p">.</span><span class="nf">Prime</span><span class="p">(</span><span class="kc">nil</span><span class="p">,</span> <span class="mi">64</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="nx">prim</span><span class="p">)</span>
</span></span></code></pre></div><codapi-snippet sandbox="go:dev" editor="basic" template="crypto-main.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">16265662996876675161677719946085651215874831846675169870638460773593241527197
</span></span><span class="line"><span class="cl">14874320216361938581
</span></span></code></pre></div><p>The change applies to the following <code>crypto</code> subpackages:</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">// crypto/dsa
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">GenerateKey</span><span class="p">(</span><span class="nx">priv</span> <span class="o">*</span><span class="nx">PrivateKey</span><span class="p">,</span> <span class="nx">rand</span> <span class="nx">io</span><span class="p">.</span><span class="nx">Reader</span><span class="p">)</span> <span class="kt">error</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// crypto/ecdh
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Curve</span> <span class="kd">interface</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="nf">GenerateKey</span><span class="p">(</span><span class="nx">rand</span> <span class="nx">io</span><span class="p">.</span><span class="nx">Reader</span><span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="nx">PrivateKey</span><span class="p">,</span> <span class="kt">error</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">// crypto/ecdsa
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">GenerateKey</span><span class="p">(</span><span class="nx">c</span> <span class="nx">elliptic</span><span class="p">.</span><span class="nx">Curve</span><span class="p">,</span> <span class="nx">rand</span> <span class="nx">io</span><span class="p">.</span><span class="nx">Reader</span><span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="nx">PrivateKey</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">func</span> <span class="nf">SignASN1</span><span class="p">(</span><span class="nx">rand</span> <span class="nx">io</span><span class="p">.</span><span class="nx">Reader</span><span class="p">,</span> <span class="nx">priv</span> <span class="o">*</span><span class="nx">PrivateKey</span><span class="p">,</span> <span class="nx">hash</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">([]</span><span class="kt">byte</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">func</span> <span class="nf">Sign</span><span class="p">(</span><span class="nx">rand</span> <span class="nx">io</span><span class="p">.</span><span class="nx">Reader</span><span class="p">,</span> <span class="nx">priv</span> <span class="o">*</span><span class="nx">PrivateKey</span><span class="p">,</span> <span class="nx">hash</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="nx">r</span><span class="p">,</span> <span class="nx">s</span> <span class="o">*</span><span class="nx">big</span><span class="p">.</span><span class="nx">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="kd">func</span> <span class="p">(</span><span class="nx">priv</span> <span class="o">*</span><span class="nx">PrivateKey</span><span class="p">)</span> <span class="nf">Sign</span><span class="p">(</span><span class="nx">rand</span> <span class="nx">io</span><span class="p">.</span><span class="nx">Reader</span><span class="p">,</span> <span class="nx">digest</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="nx">opts</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">SignerOpts</span><span class="p">)</span> <span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// crypto/rand
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Prime</span><span class="p">(</span><span class="nx">rand</span> <span class="nx">io</span><span class="p">.</span><span class="nx">Reader</span><span class="p">,</span> <span class="nx">bits</span> <span class="kt">int</span><span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="nx">big</span><span class="p">.</span><span class="nx">Int</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// crypto/rsa
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">GenerateKey</span><span class="p">(</span><span class="nx">random</span> <span class="nx">io</span><span class="p">.</span><span class="nx">Reader</span><span class="p">,</span> <span class="nx">bits</span> <span class="kt">int</span><span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="nx">PrivateKey</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">func</span> <span class="nf">GenerateMultiPrimeKey</span><span class="p">(</span><span class="nx">random</span> <span class="nx">io</span><span class="p">.</span><span class="nx">Reader</span><span class="p">,</span> <span class="nx">nprimes</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">bits</span> <span class="kt">int</span><span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="nx">PrivateKey</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">func</span> <span class="nf">EncryptPKCS1v15</span><span class="p">(</span><span class="nx">random</span> <span class="nx">io</span><span class="p">.</span><span class="nx">Reader</span><span class="p">,</span> <span class="nx">pub</span> <span class="o">*</span><span class="nx">PublicKey</span><span class="p">,</span> <span class="nx">msg</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span>
</span></span></code></pre></div><p><code>ed25519.GenerateKey(rand)</code> still uses the random reader if provided. But if <code>rand</code> is nil, it uses an internal secure source of random bytes instead of <code>crypto/rand.Reader</code> (which could be overridden).</p>
<p>To support deterministic testing, there's a new <code>testing/cryptotest</code> package with a single <code>SetGlobalRandom</code> function. It sets a global, deterministic cryptographic randomness source for the duration of the given test:</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">Test</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">cryptotest</span><span class="p">.</span><span class="nf">SetGlobalRandom</span><span class="p">(</span><span class="nx">t</span><span class="p">,</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">// All test runs will generate the same numbers.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">p1</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">rand</span><span class="p">.</span><span class="nf">Prime</span><span class="p">(</span><span class="kc">nil</span><span class="p">,</span> <span class="mi">32</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">p2</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">rand</span><span class="p">.</span><span class="nf">Prime</span><span class="p">(</span><span class="kc">nil</span><span class="p">,</span> <span class="mi">32</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">p3</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">rand</span><span class="p">.</span><span class="nf">Prime</span><span class="p">(</span><span class="kc">nil</span><span class="p">,</span> <span class="mi">32</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">got</span> <span class="o">:=</span> <span class="p">[</span><span class="mi">3</span><span class="p">]</span><span class="kt">int64</span><span class="p">{</span><span class="nx">p1</span><span class="p">.</span><span class="nf">Int64</span><span class="p">(),</span> <span class="nx">p2</span><span class="p">.</span><span class="nf">Int64</span><span class="p">(),</span> <span class="nx">p3</span><span class="p">.</span><span class="nf">Int64</span><span class="p">()}</span>
</span></span><span class="line"><span class="cl">    <span class="nx">want</span> <span class="o">:=</span> <span class="p">[</span><span class="mi">3</span><span class="p">]</span><span class="kt">int64</span><span class="p">{</span><span class="mi">3713413729</span><span class="p">,</span> <span class="mi">3540452603</span><span class="p">,</span> <span class="mi">4293217813</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">got</span> <span class="o">!=</span> <span class="nx">want</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;got %v, want %v&#34;</span><span class="p">,</span> <span class="nx">got</span><span class="p">,</span> <span class="nx">want</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="go:dev" command="test" editor="basic" template="crypto-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">PASS
</span></span></code></pre></div><p><code>SetGlobalRandom</code> affects <code>crypto/rand</code> and all implicit sources of cryptographic randomness in the <code>crypto/*</code> packages:</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">Test</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">cryptotest</span><span class="p">.</span><span class="nf">SetGlobalRandom</span><span class="p">(</span><span class="nx">t</span><span class="p">,</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="nx">t</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="s">&#34;rand.Read&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</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">got</span> <span class="p">[</span><span class="mi">4</span><span class="p">]</span><span class="kt">byte</span>
</span></span><span class="line"><span class="cl">        <span class="nx">rand</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="nx">got</span><span class="p">[:])</span>
</span></span><span class="line"><span class="cl">        <span class="nx">want</span> <span class="o">:=</span> <span class="p">[</span><span class="mi">4</span><span class="p">]</span><span class="kt">byte</span><span class="p">{</span><span class="mi">34</span><span class="p">,</span> <span class="mi">48</span><span class="p">,</span> <span class="mi">31</span><span class="p">,</span> <span class="mi">184</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nx">got</span> <span class="o">!=</span> <span class="nx">want</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;got %v, want %v&#34;</span><span class="p">,</span> <span class="nx">got</span><span class="p">,</span> <span class="nx">want</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="nx">t</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="s">&#34;rand.Int&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">got</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">rand</span><span class="p">.</span><span class="nf">Int</span><span class="p">(</span><span class="nx">rand</span><span class="p">.</span><span class="nx">Reader</span><span class="p">,</span> <span class="nx">big</span><span class="p">.</span><span class="nf">NewInt</span><span class="p">(</span><span class="mi">10000</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="kd">const</span> <span class="nx">want</span> <span class="p">=</span> <span class="mi">6185</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nx">got</span><span class="p">.</span><span class="nf">Int64</span><span class="p">()</span> <span class="o">!=</span> <span class="nx">want</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;got %v, want %v&#34;</span><span class="p">,</span> <span class="nx">got</span><span class="p">.</span><span class="nf">Int64</span><span class="p">(),</span> <span class="nx">want</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="p">}</span>
</span></span></code></pre></div><codapi-snippet sandbox="go:dev" command="test" editor="basic" template="crypto-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">PASS
</span></span></code></pre></div><p>To temporarily restore the old reader-respecting behavior, set <code>GODEBUG=cryptocustomrand=1</code> (this option will be removed in a future release).</p>
<p>𝗗 <a href="https://pkg.go.dev/testing/cryptotest@go1.26.0">testing/cryptotest</a> •
𝗣 <a href="https://go.dev/issue/70942">70942</a> •
𝗖𝗟 <a href="https://go.dev/cl/724480">724480</a> •
𝗔 <a href="https://github.com/FiloSottile">Filippo Valsorda</a>, <a href="https://github.com/qiulaidongfeng">qiulaidongfeng</a></p>
<h2 id="crypto-hpke">
    <a class="anchor" href="#crypto-hpke">#</a>
     Hybrid public key encryption 
</h2>
<p>The new <code>crypto/hpke</code> package implements Hybrid Public Key Encryption (HPKE) as specified in <a href="https://www.rfc-editor.org/rfc/rfc9180.html">RFC 9180</a>.</p>
<p>HPKE is a relatively new IETF standard for hybrid encryption. Traditional public-key encryption methods, like RSA, are slow and can only handle small amounts of data. HPKE improves on this by combining two types of encryption: it uses asymmetric cryptography (public/private keys) to safely create a shared secret, then uses fast symmetric encryption to protect the actual data. This lets you securely and quickly encrypt large files or messages, while still using the security benefits of public-key systems.</p>
<p>The &quot;asymmetric&quot; part of HPKE (called Key Encapsulation Mechanism or KEM) can use both traditional algorithms, such as those using elliptic curves, and new post-quantum algorithms, like ML-KEM. ML-KEM is designed to remain secure even against future quantum computers that could break traditional cryptography.</p>
<p>I'm not going to pretend I'm an expert in cryptography, so here's an example I took straight from the Go standard library documentation. It uses ML-KEM-X25519 for asymmetric cryptography (traditional X25519 combined with ML-KEM), AES-256 for symmetric encryption, and SHA-256 as a key hash 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">// Encrypt a single message from a sender to a recipient using the one-shot API.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">kem</span><span class="p">,</span> <span class="nx">kdf</span><span class="p">,</span> <span class="nx">aead</span> <span class="o">:=</span> <span class="nx">hpke</span><span class="p">.</span><span class="nf">MLKEM768X25519</span><span class="p">(),</span> <span class="nx">hpke</span><span class="p">.</span><span class="nf">HKDFSHA256</span><span class="p">(),</span> <span class="nx">hpke</span><span class="p">.</span><span class="nf">AES256GCM</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Recipient side
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nx">recipientPrivateKey</span> <span class="nx">hpke</span><span class="p">.</span><span class="nx">PrivateKey</span>
</span></span><span class="line"><span class="cl">    <span class="nx">publicKeyBytes</span>      <span class="p">[]</span><span class="kt">byte</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="nx">k</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">kem</span><span class="p">.</span><span class="nf">GenerateKey</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="nx">recipientPrivateKey</span> <span class="p">=</span> <span class="nx">k</span>
</span></span><span class="line"><span class="cl">    <span class="nx">publicKeyBytes</span> <span class="p">=</span> <span class="nx">k</span><span class="p">.</span><span class="nf">PublicKey</span><span class="p">().</span><span class="nf">Bytes</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">// Sender side
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">ciphertext</span> <span class="p">[]</span><span class="kt">byte</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">publicKey</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">kem</span><span class="p">.</span><span class="nf">NewPublicKey</span><span class="p">(</span><span class="nx">publicKeyBytes</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="nx">message</span> <span class="o">:=</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="s">&#34;secret message&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">ct</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">hpke</span><span class="p">.</span><span class="nf">Seal</span><span class="p">(</span><span class="nx">publicKey</span><span class="p">,</span> <span class="nx">kdf</span><span class="p">,</span> <span class="nx">aead</span><span class="p">,</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="s">&#34;public&#34;</span><span class="p">),</span> <span class="nx">message</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="nx">ciphertext</span> <span class="p">=</span> <span class="nx">ct</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">// Recipient side
</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">plaintext</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">hpke</span><span class="p">.</span><span class="nf">Open</span><span class="p">(</span><span class="nx">recipientPrivateKey</span><span class="p">,</span> <span class="nx">kdf</span><span class="p">,</span> <span class="nx">aead</span><span class="p">,</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="s">&#34;public&#34;</span><span class="p">),</span> <span class="nx">ciphertext</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="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;Decrypted: %s\n&#34;</span><span class="p">,</span> <span class="nx">plaintext</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:dev" editor="basic" template="crypto-hpke.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">Decrypted: secret message
</span></span></code></pre></div><p>As Filippo Valsorda (the cryptography engineer who maintains Go's crypto packages) says, HPKE is now the right way to do public key encryption.</p>
<p>𝗗 <a href="https://pkg.go.dev/crypto/hpke@go1.26.0">crypto/hpke</a> •
𝗣 <a href="https://go.dev/issue/75300">75300</a> •
𝗔 <a href="https://github.com/FiloSottile">Filippo Valsorda</a></p>
<h2 id="pprof-goroutineleak">
    <a class="anchor" href="#pprof-goroutineleak">#</a>
     Goroutine leak profile (experimental) 
</h2>
<p>A leak occurs when one or more goroutines are indefinitely blocked on synchronization primitives like channels, while other goroutines continue running and the program as a whole keeps functioning. Here's a simple 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">leak</span><span class="p">()</span> <span class="o">&lt;-</span><span class="kd">chan</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">out</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">int</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">out</span> <span class="o">&lt;-</span> <span class="mi">42</span> <span class="c1">// leaks if nobody reads from out
</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">return</span> <span class="nx">out</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet id="s-leak" editor="basic">
</codapi-snippet>
<p>If we call <code>leak</code> and don't read from the output channel, the inner <code>leak</code> goroutine will stay blocked trying to send to the channel for the rest of the program:</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="nf">leak</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><codapi-snippet sandbox="go:dev" editor="basic" template="pprof.go" depends-on="s-leak" 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 deadlocks, leaks do not cause panics, so they are much harder to spot. Also, unlike data races, Go's tooling did not address them for a long time.</p>
<p>Things started to change in Go 1.24 with the introduction of the <code>synctest</code> package. Not many people talk about it, but <code>synctest</code> is a great tool for catching leaks during testing.</p>
<p>Go 1.26 adds a new experimental <code>goroutineleak</code> profile designed to report leaked goroutines in production. Here's how we can use it in the example above:</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">prof</span> <span class="o">:=</span> <span class="nx">pprof</span><span class="p">.</span><span class="nf">Lookup</span><span class="p">(</span><span class="s">&#34;goroutineleak&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nf">leak</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="nx">time</span><span class="p">.</span><span class="nf">Sleep</span><span class="p">(</span><span class="mi">50</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Millisecond</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">prof</span><span class="p">.</span><span class="nf">WriteTo</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span><span class="p">,</span> <span class="mi">2</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><codapi-snippet sandbox="go:dev" editor="basic" template="pprof.go" depends-on="s-leak" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">goroutine 7 [chan send (leaked)]:
</span></span><span class="line"><span class="cl">main.leak.func1()
</span></span><span class="line"><span class="cl">    /tmp/sandbox/main.go:16 +0x1e
</span></span><span class="line"><span class="cl">created by main.leak in goroutine 1
</span></span><span class="line"><span class="cl">    /tmp/sandbox/main.go:15 +0x67
</span></span></code></pre></div><p>As you can see, we have a nice goroutine stack trace that shows exactly where the leak happens.</p>
<p>The <code>goroutineleak</code> profile finds leaks by using the garbage collector's marking phase to check which blocked goroutines are still connected to active code. It starts with runnable goroutines, marks all sync objects they can reach, and keeps adding any blocked goroutines waiting on those objects. When it can't add any more, any blocked goroutines left are waiting on resources that can't be reached — so they're considered leaked.</p>
<details>
    <summary>Tell me more</summary>
<p>Here's the gist of it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">   [ Start: GC mark phase ]
</span></span><span class="line"><span class="cl">             │
</span></span><span class="line"><span class="cl">             │ 1. Collect live goroutines
</span></span><span class="line"><span class="cl">             v
</span></span><span class="line"><span class="cl">   ┌───────────────────────┐
</span></span><span class="line"><span class="cl">   │   Initial roots       │ &lt;────────────────┐
</span></span><span class="line"><span class="cl">   │ (runnable goroutines) │                  │
</span></span><span class="line"><span class="cl">   └───────────────────────┘                  │
</span></span><span class="line"><span class="cl">             │                                │
</span></span><span class="line"><span class="cl">             │ 2. Mark reachable memory       │
</span></span><span class="line"><span class="cl">             v                                │
</span></span><span class="line"><span class="cl">   ┌───────────────────────┐                  │
</span></span><span class="line"><span class="cl">   │   Reachable objects   │                  │
</span></span><span class="line"><span class="cl">   │  (channels, mutexes)  │                  │
</span></span><span class="line"><span class="cl">   └───────────────────────┘                  │
</span></span><span class="line"><span class="cl">             │                                │
</span></span><span class="line"><span class="cl">             │ 3a. Check blocked goroutines   │
</span></span><span class="line"><span class="cl">             v                                │
</span></span><span class="line"><span class="cl">   ┌───────────────────────┐          (Yes)   │
</span></span><span class="line"><span class="cl">   │ Is blocked G waiting  │ ─────────────────┘
</span></span><span class="line"><span class="cl">   │ on a reachable obj?   │ 3b. Add G to roots
</span></span><span class="line"><span class="cl">   └───────────────────────┘
</span></span><span class="line"><span class="cl">             │
</span></span><span class="line"><span class="cl">             │ (No - repeat until no new Gs found)
</span></span><span class="line"><span class="cl">             v
</span></span><span class="line"><span class="cl">   ┌───────────────────────┐
</span></span><span class="line"><span class="cl">   │   Remaining blocked   │
</span></span><span class="line"><span class="cl">   │      goroutines       │
</span></span><span class="line"><span class="cl">   └───────────────────────┘
</span></span><span class="line"><span class="cl">             │
</span></span><span class="line"><span class="cl">             │ 5. Report the leaks
</span></span><span class="line"><span class="cl">             v
</span></span><span class="line"><span class="cl">      [   LEAKED!   ]
</span></span><span class="line"><span class="cl"> (Blocked on unreachable
</span></span><span class="line"><span class="cl">  synchronization objects)
</span></span></code></pre></div><ol>
<li><em>Collect live goroutines</em>. Start with currently active (runnable or running) goroutines as roots. Ignore blocked goroutines for now.</li>
<li><em>Mark reachable memory</em>. Trace pointers from roots to find which synchronization objects (like channels or wait groups) are currently reachable by these roots.</li>
<li><em>Resurrect blocked goroutines</em>. Check all currently blocked goroutines. If a blocked goroutine is waiting for a synchronization resource that was just marked as reachable — add that goroutine to the roots.</li>
<li><em>Iterate</em>. Repeat steps 2 and 3 until there are no more new goroutines blocked on reachable objects.</li>
<li><em>Report the leaks</em>. Any goroutines left in the blocked state are waiting for resources that no active part of the program can access. They're considered leaked.</li>
</ol>
<p>For even more details, see the <a href="https://dl.acm.org/doi/pdf/10.1145/3676641.3715990">paper</a> by Saioc et al.</p>
</details>
<p>If you want to see how <code>goroutineleak</code> (and <code>synctest</code>) can catch typical leaks that often happen in production — check out my article on <a href="/detecting-goroutine-leaks/">goroutine leaks</a>.</p>
<p>The <code>goroutineleak</code> profile is experimental and can be enabled by setting <code>GOEXPERIMENT=goroutineleakprofile</code> at build time. Enabling the experiment also makes the profile available as a <a href="https://pkg.go.dev/net/http/pprof@go1.26.0">net/http/pprof</a> endpoint, <code>/debug/pprof/goroutineleak</code>.</p>
<p>According to the authors, the implementation is already production-ready. It's only marked as experimental so they can get feedback on the API, especially about making it a new profile.</p>
<p>𝗗 <a href="https://pkg.go.dev/runtime/pprof@go1.26.0">runtime/pprof</a> •
𝗚 <a href="/detecting-goroutine-leaks/">Detecting leaks</a> •
𝗣 <a href="https://go.dev/issue/74609">74609</a>, <a href="https://go.dev/issue/75280">75280</a> •
𝗖𝗟 <a href="https://go.dev/cl/688335">688335</a> •
𝗔 <a href="https://github.com/VladSaioc">Vlad Saioc</a></p>
<h2 id="runtime-goroutine-metrics">
    <a class="anchor" href="#runtime-goroutine-metrics">#</a>
     Goroutine metrics 
</h2>
<p>New metrics in the <code>runtime/metrics</code> package give better insight into goroutine scheduling:</p>
<ul>
<li>Total number of goroutines since the program started.</li>
<li>Number of goroutines in each state.</li>
<li>Number of active threads.</li>
</ul>
<p>Here's the full list:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">/sched/goroutines-created:goroutines
</span></span><span class="line"><span class="cl">    Count of goroutines created since program start.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">/sched/goroutines/not-in-go:goroutines
</span></span><span class="line"><span class="cl">    Approximate count of goroutines running
</span></span><span class="line"><span class="cl">    or blocked in a system call or cgo call.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">/sched/goroutines/runnable:goroutines
</span></span><span class="line"><span class="cl">    Approximate count of goroutines ready to execute,
</span></span><span class="line"><span class="cl">    but not executing.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">/sched/goroutines/running:goroutines
</span></span><span class="line"><span class="cl">    Approximate count of goroutines executing.
</span></span><span class="line"><span class="cl">    Always less than or equal to /sched/gomaxprocs:threads.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">/sched/goroutines/waiting:goroutines
</span></span><span class="line"><span class="cl">    Approximate count of goroutines waiting
</span></span><span class="line"><span class="cl">    on a resource (I/O or sync primitives).
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">/sched/threads/total:threads
</span></span><span class="line"><span class="cl">    The current count of live threads
</span></span><span class="line"><span class="cl">    that are owned by the Go runtime.
</span></span></code></pre></div><p>Per-state goroutine metrics can be linked to common production issues. For example, an increasing <em>waiting</em> count can show a lock contention problem. A high <em>not-in-go</em> count means goroutines are stuck in syscalls or cgo. A growing <em>runnable</em> backlog suggests the CPUs can't keep up with demand.</p>
<p>You can read the new metric values using the regular <code>metrics.Read</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="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="k">go</span> <span class="nf">work</span><span class="p">()</span> <span class="c1">// omitted for brevity
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">time</span><span class="p">.</span><span class="nf">Sleep</span><span class="p">(</span><span class="mi">100</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Millisecond</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;Goroutine metrics:&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printMetric</span><span class="p">(</span><span class="s">&#34;/sched/goroutines-created:goroutines&#34;</span><span class="p">,</span> <span class="s">&#34;Created&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printMetric</span><span class="p">(</span><span class="s">&#34;/sched/goroutines:goroutines&#34;</span><span class="p">,</span> <span class="s">&#34;Live&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printMetric</span><span class="p">(</span><span class="s">&#34;/sched/goroutines/not-in-go:goroutines&#34;</span><span class="p">,</span> <span class="s">&#34;Syscall/CGO&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printMetric</span><span class="p">(</span><span class="s">&#34;/sched/goroutines/runnable:goroutines&#34;</span><span class="p">,</span> <span class="s">&#34;Runnable&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printMetric</span><span class="p">(</span><span class="s">&#34;/sched/goroutines/running:goroutines&#34;</span><span class="p">,</span> <span class="s">&#34;Running&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printMetric</span><span class="p">(</span><span class="s">&#34;/sched/goroutines/waiting:goroutines&#34;</span><span class="p">,</span> <span class="s">&#34;Waiting&#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">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Thread metrics:&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printMetric</span><span class="p">(</span><span class="s">&#34;/sched/gomaxprocs:threads&#34;</span><span class="p">,</span> <span class="s">&#34;Max&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printMetric</span><span class="p">(</span><span class="s">&#34;/sched/threads/total:threads&#34;</span><span class="p">,</span> <span class="s">&#34;Live&#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">func</span> <span class="nf">printMetric</span><span class="p">(</span><span class="nx">name</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">descr</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">sample</span> <span class="o">:=</span> <span class="p">[]</span><span class="nx">metrics</span><span class="p">.</span><span class="nx">Sample</span><span class="p">{{</span><span class="nx">Name</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">metrics</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="nx">sample</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Assuming a uint64 value; don&#39;t do this in production.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// Instead, check sample[0].Value.Kind and handle accordingly.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;  %s: %v\n&#34;</span><span class="p">,</span> <span class="nx">descr</span><span class="p">,</span> <span class="nx">sample</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">Value</span><span class="p">.</span><span class="nf">Uint64</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:dev" editor="basic" template="metrics.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">Goroutine metrics:
</span></span><span class="line"><span class="cl">  Created: 57
</span></span><span class="line"><span class="cl">  Live: 21
</span></span><span class="line"><span class="cl">  Syscall/CGO: 0
</span></span><span class="line"><span class="cl">  Runnable: 0
</span></span><span class="line"><span class="cl">  Running: 1
</span></span><span class="line"><span class="cl">  Waiting: 20
</span></span><span class="line"><span class="cl">Thread metrics:
</span></span><span class="line"><span class="cl">  Max: 2
</span></span><span class="line"><span class="cl">  Live: 4
</span></span></code></pre></div><p>The per-state numbers (not-in-go + runnable + running + waiting) are not guaranteed to add up to the live goroutine count (<code>/sched/goroutines:goroutines</code>, available since Go 1.16).</p>
<p>All new metrics use <code>uint64</code> counters.</p>
<p>𝗗 <a href="https://pkg.go.dev/runtime/metrics@go1.26.0#hdr-Supported_metrics">runtime/metrics</a> •
𝗣 <a href="https://go.dev/issue/15490">15490</a> •
𝗖𝗟 <a href="https://go.dev/cl/690397">690397</a>, <a href="https://go.dev/cl/690398">690398</a>, <a href="https://go.dev/cl/690399">690399</a> •
𝗔 <a href="https://github.com/mknyszek">Michael Knyszek</a></p>
<h2 id="reflect-iter">
    <a class="anchor" href="#reflect-iter">#</a>
     Reflective iterators 
</h2>
<p>The new <code>Type.Fields</code> and <code>Type.Methods</code> methods in the <code>reflect</code> package return iterators for a type's fields and 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">// List the fields of a struct type.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">typ</span> <span class="o">:=</span> <span class="nx">reflect</span><span class="p">.</span><span class="nx">TypeFor</span><span class="p">[</span><span class="nx">http</span><span class="p">.</span><span class="nx">Client</span><span class="p">]()</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="nx">f</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">typ</span><span class="p">.</span><span class="nf">Fields</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">Println</span><span class="p">(</span><span class="nx">f</span><span class="p">.</span><span class="nx">Name</span><span class="p">,</span> <span class="nx">f</span><span class="p">.</span><span class="nx">Type</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:dev" editor="basic" template="reflect.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">Transport http.RoundTripper
</span></span><span class="line"><span class="cl">CheckRedirect func(*http.Request, []*http.Request) error
</span></span><span class="line"><span class="cl">Jar http.CookieJar
</span></span><span class="line"><span class="cl">Timeout time.Duration
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// List the methods of a struct type.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">typ</span> <span class="o">:=</span> <span class="nx">reflect</span><span class="p">.</span><span class="nx">TypeFor</span><span class="p">[</span><span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Client</span><span class="p">]()</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="nx">m</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">typ</span><span class="p">.</span><span class="nf">Methods</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">Println</span><span class="p">(</span><span class="nx">m</span><span class="p">.</span><span class="nx">Name</span><span class="p">,</span> <span class="nx">m</span><span class="p">.</span><span class="nx">Type</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:dev" editor="basic" template="reflect.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">CloseIdleConnections func(*http.Client)
</span></span><span class="line"><span class="cl">Do func(*http.Client, *http.Request) (*http.Response, error)
</span></span><span class="line"><span class="cl">Get func(*http.Client, string) (*http.Response, error)
</span></span><span class="line"><span class="cl">Head func(*http.Client, string) (*http.Response, error)
</span></span><span class="line"><span class="cl">Post func(*http.Client, string, string, io.Reader) (*http.Response, error)
</span></span><span class="line"><span class="cl">PostForm func(*http.Client, string, url.Values) (*http.Response, error)
</span></span></code></pre></div><p>The new methods <code>Type.Ins</code> and <code>Type.Outs</code> return iterators for the input and output parameters of a function 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="nx">typ</span> <span class="o">:=</span> <span class="nx">reflect</span><span class="p">.</span><span class="nx">TypeFor</span><span class="p">[</span><span class="nx">filepath</span><span class="p">.</span><span class="nx">WalkFunc</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;Inputs:&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="nx">par</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">typ</span><span class="p">.</span><span class="nf">Ins</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">Println</span><span class="p">(</span><span class="s">&#34;-&#34;</span><span class="p">,</span> <span class="nx">par</span><span class="p">.</span><span class="nf">Name</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">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Outputs:&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="nx">par</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">typ</span><span class="p">.</span><span class="nf">Outs</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">Println</span><span class="p">(</span><span class="s">&#34;-&#34;</span><span class="p">,</span> <span class="nx">par</span><span class="p">.</span><span class="nf">Name</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:dev" editor="basic" template="reflect.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">Input params:
</span></span><span class="line"><span class="cl">- string
</span></span><span class="line"><span class="cl">- FileInfo
</span></span><span class="line"><span class="cl">- error
</span></span><span class="line"><span class="cl">Output params:
</span></span><span class="line"><span class="cl">- error
</span></span></code></pre></div><p>The new methods <code>Value.Fields</code> and <code>Value.Methods</code> return iterators for a value's fields and methods. Each iteration yields both the type information (<code>StructField</code> or <code>Method</code>) and the value:</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">client</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">http</span><span class="p">.</span><span class="nx">Client</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="nx">val</span> <span class="o">:=</span> <span class="nx">reflect</span><span class="p">.</span><span class="nf">ValueOf</span><span class="p">(</span><span class="nx">client</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;Fields:&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="nx">f</span><span class="p">,</span> <span class="nx">v</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">val</span><span class="p">.</span><span class="nf">Elem</span><span class="p">().</span><span class="nf">Fields</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;- name=%s kind=%s\n&#34;</span><span class="p">,</span> <span class="nx">f</span><span class="p">.</span><span class="nx">Name</span><span class="p">,</span> <span class="nx">v</span><span class="p">.</span><span class="nf">Kind</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">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Methods:&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="nx">m</span><span class="p">,</span> <span class="nx">v</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">val</span><span class="p">.</span><span class="nf">Methods</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;- name=%s kind=%s\n&#34;</span><span class="p">,</span> <span class="nx">m</span><span class="p">.</span><span class="nx">Name</span><span class="p">,</span> <span class="nx">v</span><span class="p">.</span><span class="nf">Kind</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:dev" editor="basic" template="reflect.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">Fields:
</span></span><span class="line"><span class="cl">- name=Transport kind=interface
</span></span><span class="line"><span class="cl">- name=CheckRedirect kind=func
</span></span><span class="line"><span class="cl">- name=Jar kind=interface
</span></span><span class="line"><span class="cl">- name=Timeout kind=int64
</span></span><span class="line"><span class="cl">Methods:
</span></span><span class="line"><span class="cl">- name=CloseIdleConnections kind=func
</span></span><span class="line"><span class="cl">- name=Do kind=func
</span></span><span class="line"><span class="cl">- name=Get kind=func
</span></span><span class="line"><span class="cl">- name=Head kind=func
</span></span><span class="line"><span class="cl">- name=Post kind=func
</span></span><span class="line"><span class="cl">- name=PostForm kind=func
</span></span></code></pre></div><p>Previously, you could get all this information by using a for-range loop with <code>NumX</code> methods (which is what iterators do internally):</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 1.25
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">typ</span> <span class="o">:=</span> <span class="nx">reflect</span><span class="p">.</span><span class="nx">TypeFor</span><span class="p">[</span><span class="nx">http</span><span class="p">.</span><span class="nx">Client</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="o">:=</span> <span class="k">range</span> <span class="nx">typ</span><span class="p">.</span><span class="nf">NumField</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">field</span> <span class="o">:=</span> <span class="nx">typ</span><span class="p">.</span><span class="nf">Field</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">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">field</span><span class="p">.</span><span class="nx">Name</span><span class="p">,</span> <span class="nx">field</span><span class="p">.</span><span class="nx">Type</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:dev" editor="basic" template="reflect.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">Transport http.RoundTripper
</span></span><span class="line"><span class="cl">CheckRedirect func(*http.Request, []*http.Request) error
</span></span><span class="line"><span class="cl">Jar http.CookieJar
</span></span><span class="line"><span class="cl">Timeout time.Duration
</span></span></code></pre></div><p>Using an iterator is more concise. I hope it justifies the increased API surface.</p>
<p>𝗗 <a href="https://pkg.go.dev/reflect@go1.26.0">reflect</a> •
𝗣 <a href="https://go.dev/issue/66631">66631</a> •
𝗖𝗟 <a href="https://go.dev/cl/707356">707356</a> •
𝗔 <a href="https://github.com/Splizard">Quentin Quaadgras</a></p>
<h2 id="bytes-buffer-peek">
    <a class="anchor" href="#bytes-buffer-peek">#</a>
     Peek into a buffer 
</h2>
<p>The new <code>Buffer.Peek</code> method in the <code>bytes</code> package returns the next N bytes from the buffer without advancing it:</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">buf</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;I love bytes&#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">sample</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">buf</span><span class="p">.</span><span class="nf">Peek</span><span class="p">(</span><span class="mi">1</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;peek=%s err=%v\n&#34;</span><span class="p">,</span> <span class="nx">sample</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">buf</span><span class="p">.</span><span class="nf">Next</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="nx">sample</span><span class="p">,</span> <span class="nx">err</span> <span class="p">=</span> <span class="nx">buf</span><span class="p">.</span><span class="nf">Peek</span><span class="p">(</span><span class="mi">4</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;peek=%s err=%v\n&#34;</span><span class="p">,</span> <span class="nx">sample</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span></code></pre></div><codapi-snippet sandbox="go:dev" editor="basic" template="bytes.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">peek=I err=&lt;nil&gt;
</span></span><span class="line"><span class="cl">peek=love err=&lt;nil&gt;
</span></span></code></pre></div><p>If <code>Peek</code> returns fewer than N bytes, it also returns <code>io.EOF</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="nx">buf</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&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">sample</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">buf</span><span class="p">.</span><span class="nf">Peek</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">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;peek=%s err=%v\n&#34;</span><span class="p">,</span> <span class="nx">sample</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span></code></pre></div><codapi-snippet sandbox="go:dev" editor="basic" template="bytes.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">peek=hello err=EOF
</span></span></code></pre></div><p>The slice returned by <code>Peek</code> points to the buffer's content and stays valid until the buffer is changed. So, if you change the slice right away, it will affect future reads:</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">buf</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;car&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">sample</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">buf</span><span class="p">.</span><span class="nf">Peek</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">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;peek=%s err=%v\n&#34;</span><span class="p">,</span> <span class="nx">sample</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">sample</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="p">=</span> <span class="sc">&#39;t&#39;</span> <span class="c1">// changes the underlying buffer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nx">data</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">buf</span><span class="p">.</span><span class="nf">ReadBytes</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">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;data=%s err=%v\n&#34;</span><span class="p">,</span> <span class="nx">data</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span></code></pre></div><codapi-snippet sandbox="go:dev" editor="basic" template="bytes.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">peek=car err=&lt;nil&gt;
</span></span><span class="line"><span class="cl">data=cat err=EOF
</span></span></code></pre></div><p>The slice returned by <code>Peek</code> is only valid until the next call to a read or write method.</p>
<p>𝗗 <a href="https://pkg.go.dev/bytes@go1.26.0#Buffer.Peek">Buffer.Peek</a> •
𝗣 <a href="https://go.dev/issue/73794">73794</a> •
𝗖𝗟 <a href="https://go.dev/cl/674415">674415</a> •
𝗔 <a href="https://github.com/icholy">Ilia Choly</a></p>
<h2 id="os-process-handle">
    <a class="anchor" href="#os-process-handle">#</a>
     Process handle 
</h2>
<p>After you start a process in Go, you can access its ID:</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">attr</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">os</span><span class="p">.</span><span class="nx">ProcAttr</span><span class="p">{</span><span class="nx">Files</span><span class="p">:</span> <span class="p">[]</span><span class="o">*</span><span class="nx">os</span><span class="p">.</span><span class="nx">File</span><span class="p">{</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdin</span><span class="p">,</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span><span class="p">,</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">}}</span>
</span></span><span class="line"><span class="cl"><span class="nx">proc</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">StartProcess</span><span class="p">(</span><span class="s">&#34;/bin/echo&#34;</span><span class="p">,</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">&#34;echo&#34;</span><span class="p">,</span> <span class="s">&#34;hello&#34;</span><span class="p">},</span> <span class="nx">attr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">defer</span> <span class="nx">proc</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="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;pid =&#34;</span><span class="p">,</span> <span class="nx">proc</span><span class="p">.</span><span class="nx">Pid</span><span class="p">)</span>
</span></span></code></pre></div><codapi-snippet sandbox="go" editor="basic" template="os.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">pid = 41
</span></span><span class="line"><span class="cl">hello
</span></span></code></pre></div><p>Internally, the <code>os.Process</code> type uses a process <em>handle</em> instead of the PID (which is just an integer), if the operating system supports it. Specifically, in Linux it uses <em>pidfd</em>, which is a file descriptor that refers to a process. Using the handle instead of the PID makes sure that <code>Process</code> methods always work with the same OS process, and not a different process that just happens to have the same ID.</p>
<p>Previously, you couldn't access the process handle. Now you can, thanks to the new <code>Process.WithHandle</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">func</span> <span class="p">(</span><span class="nx">p</span> <span class="o">*</span><span class="nx">Process</span><span class="p">)</span> <span class="nf">WithHandle</span><span class="p">(</span><span class="nx">f</span> <span class="kd">func</span><span class="p">(</span><span class="nx">handle</span> <span class="kt">uintptr</span><span class="p">))</span> <span class="kt">error</span>
</span></span></code></pre></div><p><code>WithHandle</code> calls a specified function and passes a process handle as an argument:</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">attr</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">os</span><span class="p">.</span><span class="nx">ProcAttr</span><span class="p">{</span><span class="nx">Files</span><span class="p">:</span> <span class="p">[]</span><span class="o">*</span><span class="nx">os</span><span class="p">.</span><span class="nx">File</span><span class="p">{</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdin</span><span class="p">,</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span><span class="p">,</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">}}</span>
</span></span><span class="line"><span class="cl"><span class="nx">proc</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">StartProcess</span><span class="p">(</span><span class="s">&#34;/bin/echo&#34;</span><span class="p">,</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">&#34;echo&#34;</span><span class="p">,</span> <span class="s">&#34;hello&#34;</span><span class="p">},</span> <span class="nx">attr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">defer</span> <span class="nx">proc</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="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;pid =&#34;</span><span class="p">,</span> <span class="nx">proc</span><span class="p">.</span><span class="nx">Pid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">proc</span><span class="p">.</span><span class="nf">WithHandle</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">handle</span> <span class="kt">uintptr</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">Println</span><span class="p">(</span><span class="s">&#34;handle =&#34;</span><span class="p">,</span> <span class="nx">handle</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:dev" editor="basic" template="os.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">pid = 49
</span></span><span class="line"><span class="cl">handle = 6
</span></span><span class="line"><span class="cl">hello
</span></span></code></pre></div><p>The handle is guaranteed to refer to the process until the callback function returns, even if the process has already terminated. That's why it's implemented as a callback instead of a <code>Process.Handle</code> field or method.</p>
<p><code>WithHandle</code> is only supported on Linux 5.4+ and Windows. On other operating systems, it doesn't execute the callback and returns an <code>os.ErrNoHandle</code> error.</p>
<p>𝗗 <a href="https://pkg.go.dev/os@go1.26.0#Process.WithHandle">Process.WithHandle</a> •
𝗣 <a href="https://go.dev/issue/70352">70352</a> •
𝗖𝗟 <a href="https://go.dev/cl/699615">699615</a> •
𝗔 <a href="https://github.com/kolyshkin">Kir Kolyshkin</a></p>
<h2 id="signal-cause">
    <a class="anchor" href="#signal-cause">#</a>
     Signal as cause 
</h2>
<p><code>signal.NotifyContext</code> returns a context that gets canceled when any of the specified signals is received. Previously, the canceled context only showed the standard &quot;context canceled&quot; cause:</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 1.25
</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 context will be canceled on SIGINT signal.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">stop</span> <span class="o">:=</span> <span class="nx">signal</span><span class="p">.</span><span class="nf">NotifyContext</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Interrupt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">defer</span> <span class="nf">stop</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 SIGINT to self.
</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">_</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">FindProcess</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nf">Getpid</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="nx">_</span> <span class="p">=</span> <span class="nx">p</span><span class="p">.</span><span class="nf">Signal</span><span class="p">(</span><span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGINT</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 for SIGINT.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">&lt;-</span><span class="nx">ctx</span><span class="p">.</span><span class="nf">Done</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;err =&#34;</span><span class="p">,</span> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Err</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;cause =&#34;</span><span class="p">,</span> <span class="nx">context</span><span class="p">.</span><span class="nf">Cause</span><span class="p">(</span><span class="nx">ctx</span><span class="p">))</span>
</span></span></code></pre></div><codapi-snippet sandbox="go" editor="basic" template="signal.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">err = context canceled
</span></span><span class="line"><span class="cl">cause = context canceled
</span></span></code></pre></div><p>Now the context's cause shows exactly which signal was received:</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 1.26
</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 context will be canceled on SIGINT signal.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">stop</span> <span class="o">:=</span> <span class="nx">signal</span><span class="p">.</span><span class="nf">NotifyContext</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Interrupt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">defer</span> <span class="nf">stop</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 SIGINT to self.
</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">_</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">FindProcess</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nf">Getpid</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="nx">_</span> <span class="p">=</span> <span class="nx">p</span><span class="p">.</span><span class="nf">Signal</span><span class="p">(</span><span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGINT</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 for SIGINT.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">&lt;-</span><span class="nx">ctx</span><span class="p">.</span><span class="nf">Done</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;err =&#34;</span><span class="p">,</span> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Err</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;cause =&#34;</span><span class="p">,</span> <span class="nx">context</span><span class="p">.</span><span class="nf">Cause</span><span class="p">(</span><span class="nx">ctx</span><span class="p">))</span>
</span></span></code></pre></div><codapi-snippet sandbox="go:dev" editor="basic" template="signal.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">err = context canceled
</span></span><span class="line"><span class="cl">cause = interrupt signal received
</span></span></code></pre></div><p>The returned type, <code>signal.signalError</code>, is based on <code>string</code>, so it doesn't provide the actual <code>os.Signal</code> value — just its string representation.</p>
<p>𝗗 <a href="https://pkg.go.dev/os/signal@go1.26.0#NotifyContext">signal.NotifyContext</a> •
𝗣 <a href="https://go.dev/issue/60756">60756</a> •
𝗖𝗟 <a href="https://go.dev/cl/721700">721700</a> •
𝗔 <a href="https://github.com/FiloSottile">Filippo Valsorda</a></p>
<h2 id="netip-prefix-compare">
    <a class="anchor" href="#netip-prefix-compare">#</a>
     Compare IP subnets 
</h2>
<p>An IP address prefix represents an IP subnet. These prefixes are usually written in CIDR notation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">10.0.0.0/16
</span></span><span class="line"><span class="cl">127.0.0.0/8
</span></span><span class="line"><span class="cl">169.254.0.0/16
</span></span><span class="line"><span class="cl">203.0.113.0/24
</span></span></code></pre></div><p>In Go, an IP prefix is represented by the <code>netip.Prefix</code> type.</p>
<p>The new <code>Prefix.Compare</code> method lets you compare two IP prefixes, making it easy to sort them without having to write your own comparison 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="nx">prefixes</span> <span class="o">:=</span> <span class="p">[]</span><span class="nx">netip</span><span class="p">.</span><span class="nx">Prefix</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">netip</span><span class="p">.</span><span class="nf">MustParsePrefix</span><span class="p">(</span><span class="s">&#34;10.1.0.0/16&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nx">netip</span><span class="p">.</span><span class="nf">MustParsePrefix</span><span class="p">(</span><span class="s">&#34;203.0.113.0/24&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nx">netip</span><span class="p">.</span><span class="nf">MustParsePrefix</span><span class="p">(</span><span class="s">&#34;10.0.0.0/16&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nx">netip</span><span class="p">.</span><span class="nf">MustParsePrefix</span><span class="p">(</span><span class="s">&#34;169.254.0.0/16&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nx">netip</span><span class="p">.</span><span class="nf">MustParsePrefix</span><span class="p">(</span><span class="s">&#34;203.0.113.0/8&#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="nx">slices</span><span class="p">.</span><span class="nf">SortFunc</span><span class="p">(</span><span class="nx">prefixes</span><span class="p">,</span> <span class="nx">netip</span><span class="p">.</span><span class="nx">Prefix</span><span class="p">.</span><span class="nx">Compare</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">_</span><span class="p">,</span> <span class="nx">p</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">prefixes</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="nx">p</span><span class="p">.</span><span class="nf">String</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:dev" editor="basic" template="netip.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">10.0.0.0/16
</span></span><span class="line"><span class="cl">10.1.0.0/16
</span></span><span class="line"><span class="cl">169.254.0.0/16
</span></span><span class="line"><span class="cl">203.0.113.0/8
</span></span><span class="line"><span class="cl">203.0.113.0/24
</span></span></code></pre></div><p><code>Compare</code> orders two prefixes as follows:</p>
<ul>
<li>First by validity (invalid before valid).</li>
<li>Then by address family (IPv4 before IPv6).<br><code>10.0.0.0/8 &lt; ::/8</code></li>
<li>Then by masked IP address (network IP).<br><code>10.0.0.0/16 &lt; 10.1.0.0/16</code></li>
<li>Then by prefix length.<br><code>10.0.0.0/8 &lt; 10.0.0.0/16</code></li>
<li>Then by unmasked address (original IP).<br><code>10.0.0.0/8 &lt; 10.0.0.1/8</code></li>
</ul>
<p>This follows the same order as Python's <code>netaddr.IPNetwork</code> and the standard IANA (Internet Assigned Numbers Authority) convention.</p>
<p>𝗗 <a href="https://pkg.go.dev/net/netip@go1.26.0#Prefix.Compare">Prefix.Compare</a> •
𝗣 <a href="https://go.dev/issue/61642">61642</a> •
𝗖𝗟 <a href="https://go.dev/cl/700355">700355</a> •
𝗔 <a href="https://github.com/database64128">database64128</a></p>
<h2 id="net-dialer-context">
    <a class="anchor" href="#net-dialer-context">#</a>
     Context-aware dialing 
</h2>
<p>The <code>net</code> package has top-level functions for connecting to an address using different networks (protocols) — <code>DialTCP</code>, <code>DialUDP</code>, <code>DialIP</code>, and <code>DialUnix</code>. They were made before <code>context.Context</code> was introduced, so they don't support cancellation:</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">raddr</span><span class="p">,</span> <span class="nx">_</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:12345&#34;</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">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="nx">raddr</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;connected, err=%v\n&#34;</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">conn</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span></code></pre></div><codapi-snippet sandbox="go:dev" editor="basic" template="net.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">connected, err=&lt;nil&gt;
</span></span></code></pre></div><p>There's also a <code>net.Dialer</code> type with a general-purpose <code>DialContext</code> method. It supports cancellation and can be used to connect to any of the known networks:</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">d</span> <span class="nx">net</span><span class="p">.</span><span class="nx">Dialer</span>
</span></span><span class="line"><span class="cl"><span class="nx">ctx</span> <span class="o">:=</span> <span class="nx">context</span><span class="p">.</span><span class="nf">Background</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">d</span><span class="p">.</span><span class="nf">DialContext</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="s">&#34;tcp&#34;</span><span class="p">,</span> <span class="s">&#34;127.0.0.1:12345&#34;</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;connected, err=%v\n&#34;</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">conn</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span></code></pre></div><codapi-snippet sandbox="go:dev" editor="basic" template="net.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">connected, err=&lt;nil&gt;
</span></span></code></pre></div><p>However, <code>DialContext</code> a bit less efficient than network-specific functions like <code>net.DialTCP</code> — because of the extra overhead from address resolution and network type dispatching.</p>
<p>So, network-specific functions in the <code>net</code> package are more efficient, but they don't support cancellation. The <code>Dialer</code> type supports cancellation, but it's less efficient. The Go team decided to resolve this contradiction.</p>
<p>The new context-aware <code>Dialer</code> methods (<code>DialTCP</code>, <code>DialUDP</code>, <code>DialIP</code>, and <code>DialUnix</code>) combine the efficiency of the existing network-specific <code>net</code> functions with the cancellation capabilities of <code>Dialer.DialContext</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">var</span> <span class="nx">d</span> <span class="nx">net</span><span class="p">.</span><span class="nx">Dialer</span>
</span></span><span class="line"><span class="cl"><span class="nx">ctx</span> <span class="o">:=</span> <span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="nx">raddr</span> <span class="o">:=</span> <span class="nx">netip</span><span class="p">.</span><span class="nf">MustParseAddrPort</span><span class="p">(</span><span class="s">&#34;127.0.0.1:12345&#34;</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">d</span><span class="p">.</span><span class="nf">DialTCP</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="s">&#34;tcp&#34;</span><span class="p">,</span> <span class="nx">netip</span><span class="p">.</span><span class="nx">AddrPort</span><span class="p">{},</span> <span class="nx">raddr</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;connected, err=%v\n&#34;</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">conn</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span></code></pre></div><codapi-snippet sandbox="go:dev" editor="basic" template="net.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">connected, err=&lt;nil&gt;
</span></span></code></pre></div><p>I wouldn't say that having three different ways to dial is very convenient, but that's the price of backward compatibility.</p>
<p>𝗗 <a href="https://pkg.go.dev/net@go1.26.0#Dialer.DialTCP">net.Dialer</a> •
𝗣 <a href="https://go.dev/issue/49097">49097</a> •
𝗖𝗟 <a href="https://go.dev/cl/490975">490975</a> •
𝗔 <a href="https://github.com/fraenkel">Michael Fraenkel</a></p>
<h2 id="httptest-example">
    <a class="anchor" href="#httptest-example">#</a>
     Fake example.com 
</h2>
<p>The default <code>httptest.Server</code> certificate already lists <code>example.com</code> in its DNSNames (a list of hostnames or domain names that the certificate is authorized to secure). Because of this, <code>Server.Client</code> doesn't trust responses from the real <code>example.com</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">// go 1.25
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Test</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">handler</span> <span class="o">:=</span> <span class="nx">http</span><span class="p">.</span><span class="nf">HandlerFunc</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">w</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 class="p">})</span>
</span></span><span class="line"><span class="cl">    <span class="nx">srv</span> <span class="o">:=</span> <span class="nx">httptest</span><span class="p">.</span><span class="nf">NewTLSServer</span><span class="p">(</span><span class="nx">handler</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">defer</span> <span class="nx">srv</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">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">srv</span><span class="p">.</span><span class="nf">Client</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;https://example.com&#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="nx">t</span><span class="p">.</span><span class="nf">Fatal</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="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">--- FAIL: Test (0.29s)
</span></span><span class="line"><span class="cl">    main_test.go:19: Get &#34;https://example.com&#34;:
</span></span><span class="line"><span class="cl">    tls: failed to verify certificate:
</span></span><span class="line"><span class="cl">    x509: certificate signed by unknown authority
</span></span></code></pre></div><p>To fix this issue, the HTTP client returned by <code>httptest.Server.Client</code> now redirects requests for <code>example.com</code> and its subdomains to the test server:</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 1.26
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Test</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">handler</span> <span class="o">:=</span> <span class="nx">http</span><span class="p">.</span><span class="nf">HandlerFunc</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">w</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 class="p">})</span>
</span></span><span class="line"><span class="cl">    <span class="nx">srv</span> <span class="o">:=</span> <span class="nx">httptest</span><span class="p">.</span><span class="nf">NewTLSServer</span><span class="p">(</span><span class="nx">handler</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">defer</span> <span class="nx">srv</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">resp</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">srv</span><span class="p">.</span><span class="nf">Client</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;https://example.com&#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="nx">t</span><span class="p">.</span><span class="nf">Fatal</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="nx">body</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">io</span><span class="p">.</span><span class="nf">ReadAll</span><span class="p">(</span><span class="nx">resp</span><span class="p">.</span><span class="nx">Body</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">resp</span><span class="p">.</span><span class="nx">Body</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">if</span> <span class="nb">string</span><span class="p">(</span><span class="nx">body</span><span class="p">)</span> <span class="o">!=</span> <span class="s">&#34;hello&#34;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;Unexpected response body: %s&#34;</span><span class="p">,</span> <span class="nx">body</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="go:dev" command="test" editor="basic" template="http.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">PASS
</span></span></code></pre></div><p>𝗗 <a href="https://pkg.go.dev/net/http/httptest@go1.26.0#Server.Client">Server.Client</a> •
𝗖𝗟 <a href="https://go.dev/cl/666855">666855</a> •
𝗔 <a href="https://github.com/seankhliao">Sean Liao</a></p>
<h2 id="fmt-errorf">
    <a class="anchor" href="#fmt-errorf">#</a>
     Optimized fmt.Errorf 
</h2>
<p>People often point out that using <code>fmt.Errorf(&quot;x&quot;)</code> for plain strings causes more memory allocations than <code>errors.New(&quot;x&quot;)</code>. Because of this, some suggest switching code from <code>fmt.Errorf</code> to <code>errors.New</code> when formatting isn't needed.</p>
<p>The Go team disagrees. Here's a quote from Russ Cox:</p>
<blockquote>
<p>Using <code>fmt.Errorf(&quot;foo&quot;)</code> is completely fine, especially in a program where all the errors are constructed with <code>fmt.Errorf</code>. Having to mentally switch between two functions based on the argument is unnecessary noise.</p>
</blockquote>
<p>With the new Go release, this debate should finally be settled. For unformatted strings, <code>fmt.Errorf</code> now allocates less and generally matches the allocations for <code>errors.New</code>.</p>
<p>Specifically, <code>fmt.Errorf</code> goes from 2 allocations to 0 allocations for a non-escaping error, and from 2 allocations to 1 allocation for an escaping error:</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">_</span> <span class="p">=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;foo&#34;</span><span class="p">)</span>    <span class="c1">// non-escaping error
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">sink</span> <span class="p">=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;foo&#34;</span><span class="p">)</span> <span class="c1">// escaping error
</span></span></span></code></pre></div><p>This matches the allocations for <code>errors.New</code> in both cases.</p>
<p>The difference in CPU cost is also much smaller now. Previously, it was ~64ns vs. ~21ns for <code>fmt.Errorf</code> vs. <code>errors.New</code> for escaping errors, now it's ~25ns vs. ~21ns.</p>
<details>
    <summary>Tell me more</summary>
<p>Here are the &quot;before and after&quot; benchmarks for the <code>fmt.Errorf</code> change. The non-escaping case is called <code>local</code>, and the escaping case is called <code>sink</code>. If there's just a plain error string, it's <code>no-args</code>. If the error includes formatting, it's <code>int-arg</code>.</p>
<p>Seconds per operation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">goos: linux
</span></span><span class="line"><span class="cl">goarch: amd64
</span></span><span class="line"><span class="cl">pkg: fmt
</span></span><span class="line"><span class="cl">cpu: AMD EPYC 7B13
</span></span><span class="line"><span class="cl">                         │    old.txt    │        new.txt        │
</span></span><span class="line"><span class="cl">                         │      sec/op   │   sec/op     vs base  │
</span></span><span class="line"><span class="cl">Errorf/no-arsg/local-16     63.76n ± 1%     4.874n ± 0%  -92.36% (n=120)
</span></span><span class="line"><span class="cl">Errorf/no-args/sink-16      64.25n ± 1%     25.81n ± 0%  -59.83% (n=120)
</span></span><span class="line"><span class="cl">Errorf/int-arg/local-16     90.86n ± 1%     90.97n ± 1%        ~ (p=0.713 n=120)
</span></span><span class="line"><span class="cl">Errorf/int-arg/sink-16      91.81n ± 1%     91.10n ± 1%   -0.76% (p=0.036 n=120)
</span></span></code></pre></div><p>Bytes per operation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">                         │    old.txt    │        new.txt       │
</span></span><span class="line"><span class="cl">                         │       B/op    │    B/op     vs base  │
</span></span><span class="line"><span class="cl">Errorf/no-args/local-16      19.00 ± 0%      0.00 ± 0%  -100.00% (n=120)
</span></span><span class="line"><span class="cl">Errorf/no-args/sink-16       19.00 ± 0%     16.00 ± 0%   -15.79% (n=120)
</span></span><span class="line"><span class="cl">Errorf/int-arg/local-16      24.00 ± 0%     24.00 ± 0%         ~ (p=1.000 n=120)
</span></span><span class="line"><span class="cl">Errorf/int-arg/sink-16       24.00 ± 0%     24.00 ± 0%         ~ (p=1.000 n=120)
</span></span></code></pre></div><p>Allocations per operation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">                         │    old.txt    │        new.txt       │
</span></span><span class="line"><span class="cl">                         │    allocs/op  │  allocs/op   vs base │
</span></span><span class="line"><span class="cl">Errorf/no-args/local-16      2.000 ± 0%     0.000 ± 0%  -100.00% (n=120)
</span></span><span class="line"><span class="cl">Errorf/no-args/sink-16       2.000 ± 0%     1.000 ± 0%   -50.00% (n=120)
</span></span><span class="line"><span class="cl">Errorf/int-arg/local-16      2.000 ± 0%     2.000 ± 0%         ~ (p=1.000 n=120)
</span></span><span class="line"><span class="cl">Errorf/int-arg/sink-16       2.000 ± 0%     2.000 ± 0%         ~ (p=1.000 n=120)
</span></span></code></pre></div><p><a href="https://go.dev/play/p/rlRSO1ehx8O">source</a></p>
</details>
<p>If you're interested in the details, I highly recommend reading the CL — it's perfectly written.</p>
<p>𝗗 <a href="https://pkg.go.dev/fmt@go1.26.0#Errorf">fmt.Errorf</a> •
𝗖𝗟 <a href="https://go.dev/cl/708836">708836</a> •
𝗔 <a href="https://github.com/thepudds">thepudds</a></p>
<h2 id="io-readall">
    <a class="anchor" href="#io-readall">#</a>
     Optimized io.ReadAll 
</h2>
<p>Previously, <code>io.ReadAll</code> allocated a lot of intermediate memory as it grew its result slice to the size of the input data. Now, it uses intermediate slices of exponentially growing size, and then copies them into a final perfectly-sized slice at the end.</p>
<p>The new implementation is about twice as fast and uses roughly half the memory for a 65KiB input; it's even more efficient with larger inputs. Here are the geomean results comparing the old and new versions for different input sizes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">                      │     old     │      new       vs base    │
</span></span><span class="line"><span class="cl">          sec/op           132.2µ        66.32µ     -49.83%
</span></span><span class="line"><span class="cl">            B/op          645.4Ki       324.6Ki     -49.70%
</span></span><span class="line"><span class="cl">  final-capacity           178.3k        151.3k     -15.10%
</span></span><span class="line"><span class="cl">    excess-ratio            1.216         1.033     -15.10%
</span></span></code></pre></div><blockquote>
<p>See the full benchmark results in the commit. Unfortunately, the author didn't provide the benchmark source code.</p>
</blockquote>
<p>Ensuring the final slice is minimally sized is also quite helpful. The slice might persist for a long time, and the unused capacity in a backing array (as in the old version) would just waste memory.</p>
<p>As with the <code>fmt.Errorf</code> optimization, I recommend reading the CL — it's very good. Both changes come from <a href="https://github.com/thepudds">thepudds</a>, whose change descriptions are every reviewer's dream come true.</p>
<p>𝗗 <a href="https://pkg.go.dev/io@go1.26.0#ReadAll">io.ReadAll</a> •
𝗖𝗟 <a href="https://go.dev/cl/722500">722500</a> •
𝗔 <a href="https://github.com/thepudds">thepudds</a></p>
<h2 id="slog-multihandler">
    <a class="anchor" href="#slog-multihandler">#</a>
     Multiple log handlers 
</h2>
<p>The <code>log/slog</code> package, introduced in version 1.21, offers a reliable, production-ready logging solution. Since its release, many projects have switched from third-party logging packages to use it. However, it was missing one key feature: the ability to send log records to multiple handlers, such as stdout or a log file.</p>
<p>The new <code>MultiHandler</code> type solves this problem. It implements the standard <code>Handler</code> interface and calls all the handlers you set up.</p>
<p>For example, we can create a log handler that writes to stdout:</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">stdoutHandler</span> <span class="o">:=</span> <span class="nx">slog</span><span class="p">.</span><span class="nf">NewTextHandler</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
</span></span></code></pre></div><codapi-snippet id="s-slog-stdout" editor="basic">
</codapi-snippet>
<p>And another handler that writes to a file:</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">const</span> <span class="nx">flags</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nx">O_CREATE</span> <span class="p">|</span> <span class="nx">os</span><span class="p">.</span><span class="nx">O_WRONLY</span> <span class="p">|</span> <span class="nx">os</span><span class="p">.</span><span class="nx">O_APPEND</span>
</span></span><span class="line"><span class="cl"><span class="nx">file</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">OpenFile</span><span class="p">(</span><span class="s">&#34;/tmp/app.log&#34;</span><span class="p">,</span> <span class="nx">flags</span><span class="p">,</span> <span class="mo">0644</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">defer</span> <span class="nx">file</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="nx">fileHandler</span> <span class="o">:=</span> <span class="nx">slog</span><span class="p">.</span><span class="nf">NewJSONHandler</span><span class="p">(</span><span class="nx">file</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
</span></span></code></pre></div><codapi-snippet id="s-slog-file" editor="basic">
</codapi-snippet>
<p>Finally, combine them using a <code>MultiHandler</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">// MultiHandler that writes to both stdout and app.log.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">multiHandler</span> <span class="o">:=</span> <span class="nx">slog</span><span class="p">.</span><span class="nf">NewMultiHandler</span><span class="p">(</span><span class="nx">stdoutHandler</span><span class="p">,</span> <span class="nx">fileHandler</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">logger</span> <span class="o">:=</span> <span class="nx">slog</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">multiHandler</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Log a sample message.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">logger</span><span class="p">.</span><span class="nf">Info</span><span class="p">(</span><span class="s">&#34;login&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">slog</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="s">&#34;name&#34;</span><span class="p">,</span> <span class="s">&#34;whoami&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nx">slog</span><span class="p">.</span><span class="nf">Int</span><span class="p">(</span><span class="s">&#34;id&#34;</span><span class="p">,</span> <span class="mi">42</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:dev" editor="basic" template="slog.go" depends-on="s-slog-stdout s-slog-file" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">time=2025-12-31T11:46:14.521Z level=INFO msg=login name=whoami id=42
</span></span><span class="line"><span class="cl">{&#34;time&#34;:&#34;2025-12-31T11:46:14.521126342Z&#34;,&#34;level&#34;:&#34;INFO&#34;,&#34;msg&#34;:&#34;login&#34;,&#34;name&#34;:&#34;whoami&#34;,&#34;id&#34;:42}
</span></span></code></pre></div><blockquote>
<p>I'm also printing the file contents here to show the results.</p>
</blockquote>
<p>When the <code>MultiHandler</code> receives a log record, it sends it to each enabled handler one by one. If any handler returns an error, <code>MultiHandler</code> doesn't stop; instead, it combines all the errors using <code>errors.Join</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="nx">hInfo</span> <span class="o">:=</span> <span class="nx">slog</span><span class="p">.</span><span class="nf">NewTextHandler</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">slog</span><span class="p">.</span><span class="nx">HandlerOptions</span><span class="p">{</span><span class="nx">Level</span><span class="p">:</span> <span class="nx">slog</span><span class="p">.</span><span class="nx">LevelInfo</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">hErrorsOnly</span> <span class="o">:=</span> <span class="nx">slog</span><span class="p">.</span><span class="nf">NewTextHandler</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">slog</span><span class="p">.</span><span class="nx">HandlerOptions</span><span class="p">{</span><span class="nx">Level</span><span class="p">:</span> <span class="nx">slog</span><span class="p">.</span><span class="nx">LevelError</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">hBroken</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">BrokenHandler</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Handler</span><span class="p">:</span> <span class="nx">hInfo</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">fmt</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;broken handler&#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="nx">handler</span> <span class="o">:=</span> <span class="nx">slog</span><span class="p">.</span><span class="nf">NewMultiHandler</span><span class="p">(</span><span class="nx">hBroken</span><span class="p">,</span> <span class="nx">hInfo</span><span class="p">,</span> <span class="nx">hErrorsOnly</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">rec</span> <span class="o">:=</span> <span class="nx">slog</span><span class="p">.</span><span class="nf">NewRecord</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="nx">slog</span><span class="p">.</span><span class="nx">LevelInfo</span><span class="p">,</span> <span class="s">&#34;hello&#34;</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="c1">// Calls hInfo and hBroken, skips hErrorsOnly.
</span></span></span><span class="line"><span class="cl"><span class="c1">// Returns an error from hBroken.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">err</span> <span class="o">:=</span> <span class="nx">handler</span><span class="p">.</span><span class="nf">Handle</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">rec</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="nx">err</span><span class="p">)</span>
</span></span></code></pre></div><codapi-snippet sandbox="go:dev" editor="basic" template="slog.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">time=2025-12-31T13:32:52.110Z level=INFO msg=hello
</span></span><span class="line"><span class="cl">broken handler
</span></span></code></pre></div><p>The <code>Enable</code> method reports whether any of the configured handlers is enabled:</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">hInfo</span> <span class="o">:=</span> <span class="nx">slog</span><span class="p">.</span><span class="nf">NewTextHandler</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">slog</span><span class="p">.</span><span class="nx">HandlerOptions</span><span class="p">{</span><span class="nx">Level</span><span class="p">:</span> <span class="nx">slog</span><span class="p">.</span><span class="nx">LevelInfo</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">hErrors</span> <span class="o">:=</span> <span class="nx">slog</span><span class="p">.</span><span class="nf">NewTextHandler</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">slog</span><span class="p">.</span><span class="nx">HandlerOptions</span><span class="p">{</span><span class="nx">Level</span><span class="p">:</span> <span class="nx">slog</span><span class="p">.</span><span class="nx">LevelError</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">handler</span> <span class="o">:=</span> <span class="nx">slog</span><span class="p">.</span><span class="nf">NewMultiHandler</span><span class="p">(</span><span class="nx">hInfo</span><span class="p">,</span> <span class="nx">hErrors</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// hInfo is enabled.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">enabled</span> <span class="o">:=</span> <span class="nx">handler</span><span class="p">.</span><span class="nf">Enabled</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">slog</span><span class="p">.</span><span class="nx">LevelInfo</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="nx">enabled</span><span class="p">)</span>
</span></span></code></pre></div><codapi-snippet sandbox="go:dev" editor="basic" template="slog.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">true
</span></span></code></pre></div><p>Other methods — <code>WithAttr</code> and <code>WithGroup</code> — call the corresponding methods on each of the enabled handlers.</p>
<p>𝗗 <a href="https://pkg.go.dev/log/slog@go1.26.0#MultiHandler">slog.MultiHandler</a> •
𝗣 <a href="https://go.dev/issue/65954">65954</a> •
𝗖𝗟 <a href="https://go.dev/cl/692237">692237</a> •
𝗔 <a href="https://github.com/callthingsoff">Jes Cok</a></p>
<h2 id="testing-artifacts">
    <a class="anchor" href="#testing-artifacts">#</a>
     Test artifacts 
</h2>
<p>Test <em>artifacts</em> are files created by tests or benchmarks, such as execution logs, memory dumps, or analysis reports. They are important for debugging failures in remote environments (like CI), where developers can't step through the code manually.</p>
<p>Previously, the Go test framework and tools didn't support test artifacts. Now they do.</p>
<p>The new methods <code>T.ArtifactDir</code>, <code>B.ArtifactDir</code>, and <code>F.ArtifactDir</code> return a directory where you can write test output files:</p>
<div class="highlight" id="s-test-artifacts-1"><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">TestFunc</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">dir</span> <span class="o">:=</span> <span class="nx">t</span><span class="p">.</span><span class="nf">ArtifactDir</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="nx">logFile</span> <span class="o">:=</span> <span class="nx">filepath</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="nx">dir</span><span class="p">,</span> <span class="s">&#34;app.log&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">content</span> <span class="o">:=</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="s">&#34;Loading user_id=123...\nERROR: Connection failed\n&#34;</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">WriteFile</span><span class="p">(</span><span class="nx">logFile</span><span class="p">,</span> <span class="nx">content</span><span class="p">,</span> <span class="mo">0644</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">t</span><span class="p">.</span><span class="nf">Log</span><span class="p">(</span><span class="s">&#34;Saved app.log&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><codapi-snippet editor="basic">
</codapi-snippet>
<p>If you use <code>go test</code> with <code>-artifacts</code>, this directory will be inside the output directory (specified by <code>-outputdir</code>, or the current directory by default):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">go test -v -artifacts -outputdir=/tmp/output
</span></span></code></pre></div><codapi-snippet sandbox="go:dev" command="test-artifacts" template="testing.go" selector="#s-test-artifacts-1 code" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">=== RUN   TestFunc
</span></span><span class="line"><span class="cl">=== ARTIFACTS TestFunc /tmp/output/_artifacts/2933211134
</span></span><span class="line"><span class="cl">    artifacts_test.go:14: Saved app.log
</span></span><span class="line"><span class="cl">--- PASS: TestFunc (0.00s)
</span></span></code></pre></div><p>As you can see, the first time <code>ArtifactDir</code> is called, it writes the directory location to the test log, which is quite handy.</p>
<p>If you don't use <code>-artifacts</code>, artifacts are stored in a temporary directory which is deleted after the test completes.</p>
<p>Each test or subtest within each package has its own unique artifact directory. Subtest outputs are not stored inside the parent test's output directory — all artifact directories for a given package are created at the same 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="kd">func</span> <span class="nf">TestFunc</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">t</span><span class="p">.</span><span class="nf">ArtifactDir</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="nx">t</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="s">&#34;subtest 1&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">t</span><span class="p">.</span><span class="nf">ArtifactDir</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">t</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="s">&#34;subtest 2&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">t</span><span class="p">.</span><span class="nf">ArtifactDir</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="go:dev" command="test-artifacts" editor="basic" template="testing.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">=== RUN   TestFunc
</span></span><span class="line"><span class="cl">=== ARTIFACTS TestFunc /tmp/output/_artifacts/2878232317
</span></span><span class="line"><span class="cl">=== RUN   TestFunc/subtest_1
</span></span><span class="line"><span class="cl">=== ARTIFACTS TestFunc/subtest_1 /tmp/output/_artifacts/1651881503
</span></span><span class="line"><span class="cl">=== RUN   TestFunc/subtest_2
</span></span><span class="line"><span class="cl">=== ARTIFACTS TestFunc/subtest_2 /tmp/output/_artifacts/3341607601
</span></span></code></pre></div><p>The artifact directory path normally looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">&lt;output dir&gt;/_artifacts/&lt;test package&gt;/&lt;test name&gt;/&lt;random&gt;
</span></span></code></pre></div><p>But if this path can't be safely converted into a local file path (which, for some reason, always happens on my machine), the path will simply be:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">&lt;output dir&gt;/_artifacts/&lt;random&gt;
</span></span></code></pre></div><p>(which is what happens in the examples above)</p>
<p>Repeated calls to <code>ArtifactDir</code> in the same test or subtest return the same directory.</p>
<p>𝗗 <a href="https://pkg.go.dev/testing@go1.26.0#T.ArtifactDir">T.ArtifactDir</a> •
𝗣 <a href="https://go.dev/issue/71287">71287</a> •
𝗖𝗟 <a href="https://go.dev/cl/696399">696399</a> •
𝗔 <a href="https://github.com/neild">Damien Neil</a></p>
<h2 id="modernized-go-fix">
    <a class="anchor" href="#modernized-go-fix">#</a>
     Modernized go fix 
</h2>
<p>Over the years, the <code>go fix</code> command became a sad, neglected bag of rewrites for very ancient Go features. But now, it's making a comeback.</p>
<p>The new <code>go fix</code> is re-implemented using the Go <a href="https://pkg.go.dev/golang.org/x/tools/go/analysis">analysis framework</a> — the same one <code>go vet</code> uses.</p>
<p>While <code>go fix</code> and <code>go vet</code> now use the same infrastructure, they have different purposes and use different sets of analyzers:</p>
<ul>
<li>Vet is for reporting problems. Its analyzers describe actual issues, but they don't always suggest fixes, and the fixes aren't always safe to apply.</li>
<li>Fix is (mostly) for modernizing the code to use newer language and library features. Its analyzers produce fixes are always safe to apply, but don't necessarily indicate problems with the code.</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">usage: go fix [build flags] [-fixtool prog] [fix flags] [packages]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Fix runs the Go fix tool (cmd/fix) on the named packages
</span></span><span class="line"><span class="cl">and applies suggested fixes.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">It supports these flags:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  -diff
</span></span><span class="line"><span class="cl">        instead of applying each fix, print the patch as a unified diff
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">The -fixtool=prog flag selects a different analysis tool with
</span></span><span class="line"><span class="cl">alternative or additional fixers.
</span></span></code></pre></div><p>By default, <code>go fix</code> runs a full set of analyzers (currently, there are more than 20). To choose specific analyzers, use the <code>-NAME</code> flag for each one, or use <code>-NAME=false</code> to run all analyzers except the ones you turned off.</p>
<p>For example, here we only enable the <code>forvar</code> analyzer:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">go fix -forvar .
</span></span></code></pre></div><p>And here, we enable all analyzers except <code>omitzero</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">go fix -omitzero=false .
</span></span></code></pre></div><p>Currently, there's no way to suppress specific analyzers for certain files or sections of code.</p>
<p>To give you a taste of <code>go fix</code> analyzers, here's one of them in action. It replaces loops with <code>slices.Contains</code> or <code>slices.ContainsFunc</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">// before go fix
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">find</span><span class="p">(</span><span class="nx">s</span> <span class="p">[]</span><span class="kt">int</span><span class="p">,</span> <span class="nx">x</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</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">v</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">s</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">v</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">true</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="kc">false</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-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// after go fix
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">find</span><span class="p">(</span><span class="nx">s</span> <span class="p">[]</span><span class="kt">int</span><span class="p">,</span> <span class="nx">x</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">slices</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">s</span><span class="p">,</span> <span class="nx">x</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>If you're interested, check out the dedicated blog post for the <a href="/accepted/modernized-go-fix">full list of analyzers</a> with examples.</p>
<p>𝗗 <a href="https://pkg.go.dev/cmd/fix@go1.26.0">cmd/fix</a> •
𝗚 <a href="/accepted/modernized-go-fix/">go fix</a> •
𝗣 <a href="https://go.dev/issue/71859">71859</a> •
𝗔 <a href="https://github.com/adonovan">Alan Donovan</a></p>
<h2 id="final-thoughts">
    <a class="anchor" href="#final-thoughts">#</a>
     Final thoughts 
</h2>
<p>Go 1.26 is incredibly big — it's the largest release I've ever seen, and for good reason:</p>
<ul>
<li>It brings a lot of useful updates, like the improved <code>new</code> builtin, type-safe error checking, and goroutine leak detector.</li>
<li>There are also many performance upgrades, including the new garbage collector, faster cgo and memory allocation, and optimized <code>fmt.Errorf</code> and <code>io.ReadAll</code>.</li>
<li>On top of that, it adds quality-of-life features like multiple log handlers, test artifacts, and the updated <code>go fix</code> tool.</li>
<li>Finally, there are two specialized experimental packages: one with SIMD support and another with protected mode for forward secrecy.</li>
</ul>
<p>All in all, a great release!</p>
<p>You might be wondering about the <code>json/v2</code> package that was introduced as experimental in 1.25. It's still experimental and available with the <code>GOEXPERIMENT=jsonv2</code> flag.</p>
<p>P.S. To catch up on other Go releases, check out the <a href="/which-go/">Go features by version</a> list or explore the interactive tours for Go <a href="/go-1-25/">1.25</a> and <a href="/go-1-24/">1.24</a>.</p>
<p>P.P.S. Want to learn more about Go? Check out my interactive book on <a href="/go-concurrency/">concurrency</a></p>
<script defer src="/modules/codapi/snippet.js"></script>
]]></content:encoded></item><item><title>Fear is not advocacy</title><link>https://antonz.org/ai-advocacy/</link><pubDate>Sun, 04 Jan 2026 12:00:00 +0000</pubDate><guid>https://antonz.org/ai-advocacy/</guid><description>And you are going to be fine.</description><content:encoded><![CDATA[<p>AI advocates seem to be the only kind of technology advocates who feel this imminent urge to constantly criticize developers for not being excited enough about their tech.</p>
<p>It would be crazy if I presented new Go features like this:</p>
<blockquote>
<p>If you still don't use the <code>synctest</code> package, all your systems will eventually succumb to concurrency bugs.</p>
</blockquote>
<p>or</p>
<blockquote>
<p>If you don't use iterators, you have absolutely nothing interesting to build.</p>
</blockquote>
<p>The job of an advocate is to spark interest, not to reproach people or instill FOMO. And yet that's exactly what AI advocates do.</p>
<p>What a weird way to advocate.</p>
<h3 id="its-okay-not-to-be-early">It's okay not to be early</h3>
<p>This whole &quot;devote your life to AI right now, or you'll be out of a job soon&quot; narrative is false.</p>
<p>You don't have to be a world-class algorithm expert to write good software. You don't have to be a Linux expert to use containers. And you don't have to spend all your time now trying to become an expert in chasing ever-changing AI tech.</p>
<p>As with any new technology, developers adopting AI typically fall into four groups: early adopters, early majority, late majority, and laggards. Right now, AI advocates are trying to shame everyone into becoming early adopters. But it's perfectly okay to wait if you're sceptical. Being part of the late majority is a safe and reasonable choice. If anything, you'll have fewer bugs to deal with.</p>
<p>As the industry adopts AI practices, you'll naturally absorb just the right amount of them.</p>
<p>You are going to be fine.</p>
]]></content:encoded></item><item><title>2026</title><link>https://antonz.org/2026/</link><pubDate>Thu, 01 Jan 2026 00:00:00 +0000</pubDate><guid>https://antonz.org/2026/</guid><description/><content:encoded></content:encoded></item><item><title>'Better C' playgrounds</title><link>https://antonz.org/better-c/</link><pubDate>Fri, 26 Dec 2025 19:00:00 +0000</pubDate><guid>https://antonz.org/better-c/</guid><description>Playgrounds for C3, Hare, Odin, V, and Zig.</description><content:encoded><![CDATA[<p>I have a soft spot for the &quot;better C&quot; family of languages: C3, Hare, Odin, V, and Zig.</p>
<blockquote>
<p>I'm not saying these languages are actually better than C — they're just different. But I needed to come up with an umbrella term for them, and &quot;better C&quot; was the only thing that came to mind.</p>
</blockquote>
<p>I believe playgrounds and interactive documentation make programming languages easier for more people to learn. That's why I created online sandboxes for these langs. You can try them out below, <a href="https://github.com/nalgeon/codapi-js#readme">embed</a> them on your own website, or <a href="https://github.com/nalgeon/sandboxes">self-host</a> and customize them.</p>
<p>If you're already familiar with one of these languages, maybe you could even create an <a href="https://github.com/nalgeon/tryxinyminutes">interactive guide</a> for it? I'm happy to help if you want to give it a try.</p>
<p><a href="#c3">C3</a> •
<a href="#hare">Hare</a> •
<a href="#odin">Odin</a> •
<a href="#v">V</a> •
<a href="#zig">Zig</a> •
<a href="#editors">Editors</a></p>
<h2 id="c3">C3</h2>
<p>An ergonomic, safe, and familiar evolution of C.</p>
<pre tabindex="0"><code class="language-c3" data-lang="c3">import std::io;

fn void greet(String name)
{
    io::printfn(&#34;Hello, %s!&#34;, name);
}

fn void main()
{
    greet(&#34;World&#34;);
}
</code></pre><codapi-snippet sandbox="c3" 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">Hello, World!
</span></span></code></pre></div><p>⛫ <a href="https://c3-lang.org">homepage</a> •
αω <a href="https://c3-lang.org/getting-started/hello-world/">tutorial</a> •
⚘ <a href="https://discord.gg/qN76R87">community</a></p>
<h2 id="hare">Hare</h2>
<p>A systems programming language designed to be simple, stable, and robust.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hare" data-lang="hare"><span class="line"><span class="cl"><span class="kn">use</span><span class="w"> </span><span class="n">fmt</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="w"> </span><span class="n">greet</span><span class="p">(</span><span class="n">user</span><span class="o">:</span><span class="w"> </span><span class="kt">str</span><span class="p">)</span><span class="w"> </span><span class="kt">void</span><span class="w"> </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="n">fmt</span><span class="o">::</span><span class="n">printfln</span><span class="p">(</span><span class="s">&#34;Hello, {}!&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">user</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><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">export</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="n">main</span><span class="p">()</span><span class="w"> </span><span class="kt">void</span><span class="w"> </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="n">greet</span><span class="p">(</span><span class="s">&#34;World&#34;</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" output>
</codapi-snippet>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Hello, World!
</span></span></code></pre></div><p>⛫ <a href="https://harelang.org">homepage</a> •
αω <a href="https://harelang.org/tutorials/introduction/">tutorial</a> •
⚘ <a href="https://harelang.org/documentation/community/">community</a></p>
<h2 id="odin">Odin</h2>
<p>A high-performance, data-oriented systems programming language.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="n">main</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="kn">import</span><span class="w"> </span><span class="s">&#34;core:fmt&#34;</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">greet</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">proc</span><span class="p">(</span><span class="n">name</span><span class="o">:</span><span class="w"> </span><span class="kt">string</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">fmt</span><span class="p">.</span><span class="n">printf</span><span class="p">(</span><span class="s">&#34;Hello, %s!\n&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">name</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="n">main</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 class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">greet</span><span class="p">(</span><span class="s">&#34;World&#34;</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="odin" 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">Hello, World!
</span></span></code></pre></div><p>⛫ <a href="https://odin-lang.org/">homepage</a> •
αω <a href="https://codapi.org/try/odin">tutorial</a> •
⚘ <a href="https://odin-lang.org/community/">community</a></p>
<h2 id="v">V</h2>
<p>A language with C-level performance and rapid compilation speeds.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-v" data-lang="v"><span class="line"><span class="cl"><span class="kd">fn</span> <span class="nf">greet</span><span class="p">(</span><span class="nv">name</span> <span class="nb">string</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nb">println</span><span class="p">(</span><span class="s1">&#39;Hello, </span><span class="o">$</span><span class="p">{</span><span class="nv">name</span><span class="p">}</span><span class="s1">!&#39;</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">fn</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">greet</span><span class="p">(</span><span class="s2">&#34;World&#34;</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="vlang" 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">Hello, World!
</span></span></code></pre></div><p>⛫ <a href="https://vlang.io/">homepage</a> •
αω <a href="https://docs.vlang.io/introduction.html">tutorial</a> •
⚘ <a href="https://github.com/vlang/v/discussions">community</a></p>
<h2 id="zig">Zig</h2>
<p>A language designed for performance and explicit control with powerful metaprogramming.</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">std</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">@import</span><span class="p">(</span><span class="s">&#34;std&#34;</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="k">fn</span><span class="w"> </span><span class="n">greet</span><span class="p">(</span><span class="n">name</span><span class="o">:</span><span class="w"> </span><span class="p">[]</span><span class="kr">const</span><span class="w"> </span><span class="kt">u8</span><span class="p">)</span><span class="w"> </span><span class="kt">void</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">std</span><span class="p">.</span><span class="n">debug</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">&#34;Hello, {s}!</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="n">name</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="kr">pub</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="n">main</span><span class="p">()</span><span class="w"> </span><span class="kt">void</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">greet</span><span class="p">(</span><span class="s">&#34;World&#34;</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="zig" 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">Hello, World!
</span></span></code></pre></div><p>⛫ <a href="https://ziglang.org/">homepage</a> •
αω <a href="https://ziglang.org/learn/overview/">tutorial</a> •
⚘ <a href="https://ziglang.org/community/">community</a></p>
<h2 id="editors">Editors</h2>
<p>If you want to do more than just &quot;hello world,&quot; there are also <a href="https://codapi.org/embed/add/">full-size online editors</a>. They're pretty basic, but still can be useful.</p>
<script defer src="/modules/codapi/snippet.js"></script>
]]></content:encoded></item></channel></rss>