<?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>Gramos' Blog</title>
    <link>https://gramos.me/tags/zig/</link>
    <description>Recent content in Zig on gramos.me</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <managingEditor>Guillermo Ramos</managingEditor>
    <lastBuildDate>Fri, 26 Jun 2026 21:40:21 +0200</lastBuildDate><atom:link href="https://gramos.me/tags/zig/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Zig&#39;s Allocator: a pattern for the indecisive craftsman</title>
      <link>https://gramos.me/posts/zig-allocator/</link>
      <pubDate>Fri, 26 Jun 2026 21:40:21 +0200</pubDate>
      <author>Guillermo Ramos</author>
      <guid>https://gramos.me/posts/zig-allocator/</guid>
      <description>&lt;p&gt;In C and other low level, non-garbage-collected languages, there&amp;rsquo;s a question that often comes to
mind when writing functions that need to return data of a size not known at compile time:&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Should I ask the caller to provide the buffer, or should I just malloc?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Each approach has its tradeoffs. Use the stack and force the caller to manage memory? Or use the
heap and introduce allocation complexity and fragmentation? It&amp;rsquo;s not always a trivial decision, and
if you&amp;rsquo;re like me, these are the kind of things that can totally get you out of flow while you
consider the implications.&lt;/p&gt;</description>
      <content:encoded><![CDATA[ <p>In C and other low level, non-garbage-collected languages, there’s a question that often comes to
mind when writing functions that need to return data of a size not known at compile time:</p>
<p>“Should I ask the caller to provide the buffer, or should I just malloc?”</p>
<p>Each approach has its tradeoffs. Use the stack and force the caller to manage memory? Or use the
heap and introduce allocation complexity and fragmentation? It’s not always a trivial decision, and
if you’re like me, these are the kind of things that can totally get you out of flow while you
consider the implications.</p>
<p>One of the selling points of Zig is making Allocator (written in uppercase as it’s a type) a first
class citizen. On first sight it may look like a feature for high-performance finetuning, and it is,
but it’s even more powerful than that. It’s a way to avoid having to choose between heap and stack
altogether.</p>
<p>Let’s see an example. Here’s an excerpt from a project (saga) I’m working on:</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span>std<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span>@import(<span style="color:#a5d6ff">"std"</span>);<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span>Environ<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span>std.process.Environ;<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">pub</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span>Paths<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">struct</span><span style="color:#6e7681"> </span>{<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span>cfg_dir:<span style="color:#6e7681"> </span>[]<span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">u8</span>,<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span>data_dir:<span style="color:#6e7681"> </span>[]<span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">u8</span>,<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span>Err<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">error</span>{<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span>HomeNotDefined,<span style="color:#6e7681">  </span><span style="color:#8b949e;font-style:italic">// custom error</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span>};<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#ff7b72">pub</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">fn</span><span style="color:#6e7681"> </span><span style="color:#d2a8ff;font-weight:bold">init</span>(env:<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">*</span>Environ.Map)<span style="color:#6e7681"> </span>Err<span style="color:#ff7b72;font-weight:bold">!</span>@This()<span style="color:#6e7681"> </span>{<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#ff7b72">return</span><span style="color:#6e7681"> </span>.{<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">            </span>.cfg_dir<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span><span style="color:#d2a8ff;font-weight:bold">xdgGet</span>(<span style="color:#a5d6ff">".config"</span>,<span style="color:#6e7681"> </span><span style="color:#a5d6ff">"XDG_CONFIG_HOME"</span>,<span style="color:#6e7681"> </span>env),<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">            </span>.data_dir<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span><span style="color:#d2a8ff;font-weight:bold">xdgGet</span>(<span style="color:#a5d6ff">".local/share"</span>,<span style="color:#6e7681"> </span><span style="color:#a5d6ff">"XDG_DATA_HOME"</span>,<span style="color:#6e7681"> </span>env),<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span>};<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span>}<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#ff7b72">fn</span><span style="color:#6e7681"> </span><span style="color:#d2a8ff;font-weight:bold">xdgGet</span>(<span style="color:#ff7b72">comptime</span><span style="color:#6e7681"> </span>default:<span style="color:#6e7681"> </span>[]<span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">u8</span>,<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">              </span>envvar:<span style="color:#6e7681"> </span>[]<span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">u8</span>,<span style="color:#6e7681"> </span>env:<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">*</span>Environ.Map)<span style="color:#6e7681"> </span>Err<span style="color:#ff7b72;font-weight:bold">!</span>[]<span style="color:#ff7b72">u8</span><span style="color:#6e7681"> </span>{<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span>home<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span>env.<span style="color:#d2a8ff;font-weight:bold">get</span>(<span style="color:#a5d6ff">"HOME"</span>)<span style="color:#6e7681"> </span><span style="color:#ff7b72">orelse</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">return</span><span style="color:#6e7681"> </span>Err.HomeNotDefined;<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#ff7b72">return</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">if</span><span style="color:#6e7681"> </span>(env.<span style="color:#d2a8ff;font-weight:bold">get</span>(envvar))<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">|</span>xdgval<span style="color:#ff7b72;font-weight:bold">|</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">            </span><span style="color:#8b949e;font-style:italic">// Problem: This requires a place to store the formatted string:</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">            </span><span style="color:#8b949e;font-style:italic">//   xdgval ++ "/saga"</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">            </span><span style="color:#ff7b72">unreachable</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#ff7b72">else</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">            </span><span style="color:#8b949e;font-style:italic">// Problem: This requires a place to store the formatted string:</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">            </span><span style="color:#8b949e;font-style:italic">//   "$HOME/" ++ default ++ "/saga"</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">            </span><span style="color:#ff7b72">unreachable</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span>}<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span>};<span style="color:#6e7681">
</span></span></span></code></pre></div><p>This is the definition of a Paths struct, which is intended to store the paths to the project’s
directories (config + data), e.g.:</p>
<ul>
<li><code>.cfg_dir = "/home/user/.config/saga"</code></li>
<li><code>.data_dir = "/home/user/.local/share/saga"</code></li>
</ul>
<p>The fields themselves are string pointers, which in Zig means “slice of constant chars” (<code>[]const u8</code>).  The idea is to play nice and follow <a href="https://specifications.freedesktop.org/basedir/latest/#variables"  class="external-link" target="_blank" rel="noopener">the
standards</a>, trying to get them
from the environment or falling back to their default values. In either case, the strings’ size
won’t be known at compile time. So the code is missing the most important part: actually formatting
the strings somewhere. What are our options?</p>
<h2 id="asking-the-caller">
  Asking the caller
  
</h2>
<p>The <code>init</code> function could ask for two buffers from the caller:</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#ff7b72">pub</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span>Paths<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">struct</span><span style="color:#6e7681"> </span>{<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#8b949e;font-style:italic">// ...</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span>Err<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">error</span>{<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span>NoSpaceLeft,<span style="color:#6e7681">    </span><span style="color:#8b949e;font-style:italic">// buffer is too small</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span>HomeNotDefined,<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span>};<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#ff7b72">pub</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">fn</span><span style="color:#6e7681"> </span><span style="color:#d2a8ff;font-weight:bold">init</span>(cfg_buf:<span style="color:#6e7681"> </span>[]<span style="color:#ff7b72">u8</span>,<span style="color:#6e7681"> </span>data_buf:<span style="color:#6e7681"> </span>[]<span style="color:#ff7b72">u8</span>,<span style="color:#6e7681"> </span>env:<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">*</span>Environ.Map)<span style="color:#6e7681"> </span>Err<span style="color:#ff7b72;font-weight:bold">!</span>@This()<span style="color:#6e7681"> </span>{<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#ff7b72">return</span><span style="color:#6e7681"> </span>.{<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">            </span>.cfg_dir<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">try</span><span style="color:#6e7681"> </span><span style="color:#d2a8ff;font-weight:bold">xdgGet</span>(<span style="color:#a5d6ff">".config"</span>,<span style="color:#6e7681"> </span>cfg_buf,<span style="color:#6e7681"> </span><span style="color:#a5d6ff">"XDG_CONFIG_HOME"</span>,<span style="color:#6e7681"> </span>env),<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">            </span>.data_dir<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">try</span><span style="color:#6e7681"> </span><span style="color:#d2a8ff;font-weight:bold">xdgGet</span>(<span style="color:#a5d6ff">".local/share"</span>,<span style="color:#6e7681"> </span>data_buf,<span style="color:#6e7681"> </span><span style="color:#a5d6ff">"XDG_DATA_HOME"</span>,<span style="color:#6e7681"> </span>env),<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span>};<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span>}<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#ff7b72">fn</span><span style="color:#6e7681"> </span><span style="color:#d2a8ff;font-weight:bold">xdgGet</span>(<span style="color:#ff7b72">comptime</span><span style="color:#6e7681"> </span>default:<span style="color:#6e7681"> </span>[]<span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">u8</span>,<span style="color:#6e7681"> </span>buf:<span style="color:#6e7681"> </span>[]<span style="color:#ff7b72">u8</span>,<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">              </span>envvar:<span style="color:#6e7681"> </span>[]<span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">u8</span>,<span style="color:#6e7681"> </span>env:<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">*</span>Environ.Map)<span style="color:#6e7681"> </span>Err<span style="color:#ff7b72;font-weight:bold">!</span>[]<span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">u8</span><span style="color:#6e7681"> </span>{<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span>home<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span>env.<span style="color:#d2a8ff;font-weight:bold">get</span>(<span style="color:#a5d6ff">"HOME"</span>)<span style="color:#6e7681"> </span><span style="color:#ff7b72">orelse</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">return</span><span style="color:#6e7681"> </span>Err.HomeNotDefined;<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#8b949e;font-style:italic">// bufPrint is like sprintf(3) in C, formatting a string into a buffer</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#8b949e;font-style:italic">// (it's safe, as the slice already carries its length).</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#8b949e;font-style:italic">//</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#8b949e;font-style:italic">// Can throw NoSpaceLeft errors if the buffer is too small; the 'try'</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#8b949e;font-style:italic">// will propagate it through this function.</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#ff7b72">return</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">if</span><span style="color:#6e7681"> </span>(env.<span style="color:#d2a8ff;font-weight:bold">get</span>(envvar))<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">|</span>xdgval<span style="color:#ff7b72;font-weight:bold">|</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">            </span><span style="color:#ff7b72">try</span><span style="color:#6e7681"> </span>std.fmt.<span style="color:#d2a8ff;font-weight:bold">bufPrint</span>(buf,<span style="color:#6e7681"> </span><span style="color:#a5d6ff">"{s}/saga"</span>,<span style="color:#6e7681"> </span>.{xdgval})<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#ff7b72">else</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">            </span><span style="color:#8b949e;font-style:italic">// Notice the compile time join of the formatting string. Groovy.</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">            </span><span style="color:#ff7b72">try</span><span style="color:#6e7681"> </span>std.fmt.<span style="color:#d2a8ff;font-weight:bold">bufPrint</span>(buf,<span style="color:#6e7681"> </span><span style="color:#a5d6ff">"{s}/"</span><span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">++</span><span style="color:#6e7681"> </span>default<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">++</span><span style="color:#6e7681"> </span><span style="color:#a5d6ff">"/saga"</span>,<span style="color:#6e7681"> </span>.{home});<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span>}<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span>};<span style="color:#6e7681">
</span></span></span></code></pre></div><p>Here’s how we would call it:</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#ff7b72">pub</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">fn</span><span style="color:#6e7681"> </span><span style="color:#d2a8ff;font-weight:bold">main</span>(init:<span style="color:#6e7681"> </span>std.process.Init)<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">!</span><span style="color:#ff7b72">void</span><span style="color:#6e7681"> </span>{<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#8b949e;font-style:italic">// This syntax allocates the buffers without initializing them</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#ff7b72">var</span><span style="color:#6e7681"> </span>cfg_buf:<span style="color:#6e7681"> </span>[<span style="color:#a5d6ff">256</span>]<span style="color:#ff7b72">u8</span><span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span><span style="color:#79c0ff">undefined</span>;<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#ff7b72">var</span><span style="color:#6e7681"> </span>data_buf:<span style="color:#6e7681"> </span>[<span style="color:#a5d6ff">256</span>]<span style="color:#ff7b72">u8</span><span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span><span style="color:#79c0ff">undefined</span>;<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span>paths<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">try</span><span style="color:#6e7681"> </span>Paths.<span style="color:#d2a8ff;font-weight:bold">init</span>(<span style="color:#ff7b72;font-weight:bold">&</span>cfg_buf,<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">&</span>data_buf,<span style="color:#6e7681"> </span>init.environ_map);<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span>std.log.<span style="color:#d2a8ff;font-weight:bold">debug</span>(<span style="color:#a5d6ff">"Config dir: {s}"</span>,<span style="color:#6e7681"> </span>.{paths.cfg_dir});<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span>std.log.<span style="color:#d2a8ff;font-weight:bold">debug</span>(<span style="color:#a5d6ff">"Data dir: {s}"</span>,<span style="color:#6e7681"> </span>.{paths.data_dir});<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span>}<span style="color:#6e7681">
</span></span></span></code></pre></div><p>The downsides of this approach are evident: hardcoded sizes, wasted space, requiring one buffer per
field… but on the other hand, It Just Works (TM), it’s dead simple, and we are even enforcing an
upper bound to runtime memory requirements.</p>
<h2 id="allocating-memory-on-the-heap">
  Allocating memory on the heap
  
</h2>
<p>Good old <code>malloc(3)</code>. Or, almost. In C, the “malloc & company” family of functions is provided by
the libc. In Zig, <a href="https://ziglang.org/documentation/0.16.0/std/#std.mem.Allocator"  class="external-link" target="_blank" rel="noopener">Allocator</a> is an
interface and the standard library offers <a href="https://ziglang.org/documentation/0.16.0/std/#std.heap"  class="external-link" target="_blank" rel="noopener">several
implementations</a>, so before even trying to
allocate memory, you need to chose one. Functions needing to allocate memory can simply receive an
Allocator and ask it for memory, not needing to know the particular implementation.</p>
<p>Let’s see how the code would look like using this pattern:</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#ff7b72">pub</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span>Paths<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">struct</span><span style="color:#6e7681"> </span>{<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#8b949e;font-style:italic">// ...</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span>Err<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">error</span>{<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span>OutOfMemory,<span style="color:#6e7681">     </span><span style="color:#8b949e;font-style:italic">// allocator out of memory</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span>HomeNotDefined,<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span>};<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#ff7b72">pub</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">fn</span><span style="color:#6e7681"> </span><span style="color:#d2a8ff;font-weight:bold">init</span>(alloc:<span style="color:#6e7681"> </span>std.mem.Allocator,<span style="color:#6e7681"> </span>env:<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">*</span>Environ.Map)<span style="color:#6e7681"> </span>Err<span style="color:#ff7b72;font-weight:bold">!</span>@This()<span style="color:#6e7681"> </span>{<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#ff7b72">return</span><span style="color:#6e7681"> </span>.{<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">            </span>.cfg_dir<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">try</span><span style="color:#6e7681"> </span><span style="color:#d2a8ff;font-weight:bold">xdgGet</span>(<span style="color:#a5d6ff">".config"</span>,<span style="color:#6e7681"> </span>alloc,<span style="color:#6e7681"> </span><span style="color:#a5d6ff">"XDG_CONFIG_HOME"</span>,<span style="color:#6e7681"> </span>env),<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">            </span>.data_dir<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">try</span><span style="color:#6e7681"> </span><span style="color:#d2a8ff;font-weight:bold">xdgGet</span>(<span style="color:#a5d6ff">".local/share"</span>,<span style="color:#6e7681"> </span>alloc,<span style="color:#6e7681"> </span><span style="color:#a5d6ff">"XDG_DATA_HOME"</span>,<span style="color:#6e7681"> </span>env),<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span>};<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span>}<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#ff7b72">pub</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">fn</span><span style="color:#6e7681"> </span><span style="color:#d2a8ff;font-weight:bold">deinit</span>(this:<span style="color:#6e7681"> </span>@This(),<span style="color:#6e7681"> </span>alloc:<span style="color:#6e7681"> </span>std.mem.Allocator)<span style="color:#6e7681"> </span><span style="color:#ff7b72">void</span><span style="color:#6e7681"> </span>{<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span>alloc.<span style="color:#d2a8ff;font-weight:bold">free</span>(this.cfg_dir);<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span>alloc.<span style="color:#d2a8ff;font-weight:bold">free</span>(this.data_dir);<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span>}<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#ff7b72">fn</span><span style="color:#6e7681"> </span><span style="color:#d2a8ff;font-weight:bold">xdgGet</span>(<span style="color:#ff7b72">comptime</span><span style="color:#6e7681"> </span>default:<span style="color:#6e7681"> </span>[]<span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">u8</span>,<span style="color:#6e7681"> </span>alloc:<span style="color:#6e7681"> </span>std.mem.Allocator,<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">              </span>envvar:<span style="color:#6e7681"> </span>[]<span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">u8</span>,<span style="color:#6e7681"> </span>env:<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">*</span>Environ.Map)<span style="color:#6e7681"> </span>Err<span style="color:#ff7b72;font-weight:bold">!</span>[]<span style="color:#ff7b72">u8</span><span style="color:#6e7681"> </span>{<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span>home<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span>env.<span style="color:#d2a8ff;font-weight:bold">get</span>(<span style="color:#a5d6ff">"HOME"</span>)<span style="color:#6e7681"> </span><span style="color:#ff7b72">orelse</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">return</span><span style="color:#6e7681"> </span>Err.HomeNotDefined;<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#8b949e;font-style:italic">// Now instead of bufPrint we use allocPrint (asprintf(3) in C), which</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#8b949e;font-style:italic">// internally allocates memory as required from alloc, writes the string</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#8b949e;font-style:italic">// there, and returns a pointer.</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#8b949e;font-style:italic">//</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#8b949e;font-style:italic">// Can throw OutOfMemory errors.</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#ff7b72">return</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">if</span><span style="color:#6e7681"> </span>(env.<span style="color:#d2a8ff;font-weight:bold">get</span>(envvar))<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">|</span>xdgval<span style="color:#ff7b72;font-weight:bold">|</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">            </span><span style="color:#ff7b72">try</span><span style="color:#6e7681"> </span>std.fmt.<span style="color:#d2a8ff;font-weight:bold">allocPrint</span>(alloc,<span style="color:#6e7681"> </span><span style="color:#a5d6ff">"{s}/saga"</span>,<span style="color:#6e7681"> </span>.{xdgval})<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#ff7b72">else</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">            </span><span style="color:#ff7b72">try</span><span style="color:#6e7681"> </span>std.fmt.<span style="color:#d2a8ff;font-weight:bold">allocPrint</span>(alloc,<span style="color:#6e7681"> </span><span style="color:#a5d6ff">"{s}/"</span><span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">++</span><span style="color:#6e7681"> </span>default<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">++</span><span style="color:#6e7681"> </span><span style="color:#a5d6ff">"/saga"</span>,<span style="color:#6e7681"> </span>.{home});<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span>}<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span>};<span style="color:#6e7681">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#ff7b72">pub</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">fn</span><span style="color:#6e7681"> </span><span style="color:#d2a8ff;font-weight:bold">main</span>(init:<span style="color:#6e7681"> </span>std.process.Init)<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">!</span><span style="color:#ff7b72">void</span><span style="color:#6e7681"> </span>{<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#8b949e;font-style:italic">// A default General Purpose Allocator is provided here by Zig for convenience</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span>gpa<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span>init.gpa;<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span>paths<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">try</span><span style="color:#6e7681"> </span>Paths.<span style="color:#d2a8ff;font-weight:bold">init</span>(gpa,<span style="color:#6e7681"> </span>init.environ_map);<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#ff7b72">defer</span><span style="color:#6e7681"> </span>paths.<span style="color:#d2a8ff;font-weight:bold">deinit</span>(gpa);<span style="color:#6e7681"> </span><span style="color:#8b949e;font-style:italic">// Free memory used by `paths` when exiting this scope</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span>std.log.<span style="color:#d2a8ff;font-weight:bold">debug</span>(<span style="color:#a5d6ff">"Config dir: {s}"</span>,<span style="color:#6e7681"> </span>.{paths.cfg_dir});<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span>std.log.<span style="color:#d2a8ff;font-weight:bold">debug</span>(<span style="color:#a5d6ff">"Data dir: {s}"</span>,<span style="color:#6e7681"> </span>.{paths.data_dir});<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span>}<span style="color:#6e7681">
</span></span></span></code></pre></div><p>Here the memory is not being explicitly requested by us; it’s done by the
<a href="https://ziglang.org/documentation/0.16.0/std/#std.fmt.allocPrint"  class="external-link" target="_blank" rel="noopener">allocPrint</a> library function.
So, the allocator is passed through <code>init -> xdgGet -> allocPrint</code>. And now we need to expose a
<code>deinit</code> function to free the buffers.</p>
<p>This is probably the more standard approach: the caller does not need to worry about buffer sizes,
although the caller <strong>does</strong> need to remember to deinit every Paths struct to prevent memory leakage.</p>
<h2 id="the-plot-twist">
  The plot twist
  
</h2>
<p>Now in the last example, the used allocator was the GPA “General Purpose Allocator”, which works
just fine most of the time and has nice properties during development, such as detecting memory
leaks. But the caller still has the option to choose a different one, such as the raw
<a href="https://ziglang.org/documentation/0.16.0/std/#std.heap.page_allocator"  class="external-link" target="_blank" rel="noopener">page_allocator</a> or
converting the GPA to an
<a href="https://ziglang.org/documentation/0.16.0/std/#std.heap.ArenaAllocator"  class="external-link" target="_blank" rel="noopener">Arena</a> for higher
performance.</p>
<p>The key here is that an Allocator implementation does not even need to use the heap. An Allocator
can be any anything that implements its vtable: <a href="https://ziglang.org/documentation/0.16.0/std/#std.mem.Allocator.VTable"  class="external-link" target="_blank" rel="noopener">alloc, resize, remap, and
free</a>. And indeed there’s
one implementation in the standard library that uses any provided buffer as memory pool, including
one residing in the stack: the
<a href="https://ziglang.org/documentation/0.16.0/std/#std.heap.FixedBufferAllocator"  class="external-link" target="_blank" rel="noopener">FixedBufferAllocator</a>:</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#ff7b72">pub</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">fn</span><span style="color:#6e7681"> </span><span style="color:#d2a8ff;font-weight:bold">main</span>(init:<span style="color:#6e7681"> </span>std.process.Init)<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">!</span><span style="color:#ff7b72">void</span><span style="color:#6e7681"> </span>{<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#ff7b72">var</span><span style="color:#6e7681"> </span>buf:<span style="color:#6e7681"> </span>[<span style="color:#a5d6ff">256</span>]<span style="color:#ff7b72">u8</span><span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span><span style="color:#79c0ff">undefined</span>;<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#ff7b72">var</span><span style="color:#6e7681"> </span>fba<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span>std.heap.FixedBufferAllocator.<span style="color:#d2a8ff;font-weight:bold">init</span>(<span style="color:#ff7b72;font-weight:bold">&</span>buf);<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span>alloc<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span>fba.<span style="color:#d2a8ff;font-weight:bold">allocator</span>();<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#ff7b72">const</span><span style="color:#6e7681"> </span>paths<span style="color:#6e7681"> </span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#6e7681"> </span><span style="color:#ff7b72">try</span><span style="color:#6e7681"> </span>Paths.<span style="color:#d2a8ff;font-weight:bold">init</span>(alloc,<span style="color:#6e7681"> </span>init.environ_map);<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#8b949e;font-style:italic">// Notice we don't need to call `paths.deinit` anymore!</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span>std.log.<span style="color:#d2a8ff;font-weight:bold">debug</span>(<span style="color:#a5d6ff">"Config dir: {s}"</span>,<span style="color:#6e7681"> </span>.{paths.cfg_dir});<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span>std.log.<span style="color:#d2a8ff;font-weight:bold">debug</span>(<span style="color:#a5d6ff">"Data dir: {s}"</span>,<span style="color:#6e7681"> </span>.{paths.data_dir});<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span>}<span style="color:#6e7681">
</span></span></span></code></pre></div><p>And here we are again, with a stack allocated buffer as in the first example, but with the extra
convenience of not having to allocate individual buffers for each field, calling the same init
function that we used when we were using the heap, without having to change its signature.</p>
<p>That is what using Allocator means: not needing to worry again about finding out the best place to
request memory from; the caller will provide it.</p>
<p><em>Noice</em>.</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
