<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Systems Programming on Tachera W Sasi, Backend &amp; Infra Engineer (Go, Self-Hosted, Observability)</title><link>https://tacherasasi.github.io/categories/systems-programming/</link><description>Recent content in Systems Programming on Tachera W Sasi, Backend &amp; Infra Engineer (Go, Self-Hosted, Observability)</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Fri, 02 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tacherasasi.github.io/categories/systems-programming/index.xml" rel="self" type="application/rss+xml"/><item><title>I Want to Understand Computers, Not Just Use Them</title><link>https://tacherasasi.github.io/posts/i-want-to-understand-computers-not-just-use-them-b94385de2ffb/</link><pubDate>Fri, 02 Oct 2026 00:00:00 +0000</pubDate><guid>https://tacherasasi.github.io/posts/i-want-to-understand-computers-not-just-use-them-b94385de2ffb/</guid><description>&lt;h2 id="tags-tachera-software-engineering-systems-programming"&gt;&amp;mdash;2&amp;quot;
tags: [&amp;ldquo;tachera&amp;rdquo;, &amp;ldquo;software-engineering&amp;rdquo;, &amp;ldquo;systems-programming&amp;rdquo;]&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1024/1*Z-4jE3q_hWwAJq4NPpkbrw.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;I’ve spent a lot of time writing software.&lt;/p&gt;
&lt;p&gt;Web applications. Mobile apps. APIs. Backends. Databases. Deployment systems. Developer tools. And lately, more and more infrastructure and very, very low-level stuff, mostly in Zig.&lt;/p&gt;
&lt;p&gt;And honestly, I’m starting to suspect that I have a problem.&lt;/p&gt;
&lt;p&gt;At some point, I realized something had changed.&lt;/p&gt;
&lt;p&gt;I don’t just want to build software anymore.&lt;/p&gt;
&lt;p&gt;I want to understand the computer underneath it.&lt;/p&gt;
&lt;p&gt;I want to know what happens when a process starts, how memory actually gets used, why a network connection fails, what the operating system is doing, how filesystems work, what happens when a server runs out of resources, and why a distributed system behaves differently from the beautiful little diagram I drew on a whiteboard.&lt;/p&gt;
&lt;p&gt;The diagram, of course, assumes everything works.&lt;/p&gt;
&lt;p&gt;The real system apparently did not receive the memo.&lt;/p&gt;
&lt;p&gt;I want to understand the machine, not just the framework sitting on top of it.&lt;/p&gt;
&lt;h3 id="the-abstraction-is-useful-until-itisnt"&gt;The abstraction is useful until it isn’t&lt;/h3&gt;
&lt;p&gt;Modern software development is incredibly good at hiding complexity.&lt;/p&gt;
&lt;p&gt;That’s one of its greatest strengths.&lt;/p&gt;
&lt;p&gt;I can create an HTTP server without thinking about TCP. I can use a database without thinking about disk pages. I can deploy an application without manually configuring a server. I can create a mobile interface without thinking about how pixels eventually reach a display.&lt;/p&gt;
&lt;p&gt;That’s great.&lt;/p&gt;
&lt;p&gt;Abstraction exists for a reason.&lt;/p&gt;
&lt;p&gt;I don’t want to manually implement TCP every time I build a todo app. That sounds like a terrible Tuesday.&lt;/p&gt;
&lt;p&gt;But there is a point where abstraction becomes a ceiling.&lt;/p&gt;
&lt;p&gt;You can spend years knowing React, Laravel, Next.js, Docker, Kubernetes, or whatever the industry is excited about this month without understanding much about what actually happens underneath them.&lt;/p&gt;
&lt;p&gt;And then something breaks.&lt;/p&gt;
&lt;p&gt;A connection starts timing out.&lt;/p&gt;
&lt;p&gt;A process consumes all the memory.&lt;/p&gt;
&lt;p&gt;A deployment works on one machine but not another.&lt;/p&gt;
&lt;p&gt;A filesystem fills up.&lt;/p&gt;
&lt;p&gt;A request occasionally takes five seconds instead of fifty milliseconds.&lt;/p&gt;
&lt;p&gt;A queue gets backed up.&lt;/p&gt;
&lt;p&gt;Your logs say absolutely nothing useful.&lt;/p&gt;
&lt;p&gt;You stare at the terminal.&lt;/p&gt;
&lt;p&gt;The terminal stares back.&lt;/p&gt;
&lt;p&gt;Suddenly the abstraction isn’t enough anymore.&lt;/p&gt;
&lt;p&gt;You have to go one layer deeper.&lt;/p&gt;
&lt;p&gt;And I want to be comfortable there.&lt;/p&gt;
&lt;h3 id="i-started-caring-about-the-layers-underneath"&gt;I started caring about the layers underneath&lt;/h3&gt;
&lt;p&gt;This is probably why I’ve become increasingly interested in Go, Zig, Rust, Linux, networking, distributed systems, infrastructure, and developer tooling.&lt;/p&gt;
&lt;p&gt;They force me to think differently.&lt;/p&gt;
&lt;p&gt;When I’m building something in Go, I find myself thinking about processes, concurrency, memory, networking, files, system calls, binaries, and failure modes.&lt;/p&gt;
&lt;p&gt;When I’m writing Zig, I have considerably fewer layers between me and the machine.&lt;/p&gt;
&lt;p&gt;Sometimes that’s beautiful.&lt;/p&gt;
&lt;p&gt;Sometimes it feels like the computer has finally decided to stop protecting me from my own decisions.&lt;/p&gt;
&lt;p&gt;Either way, I’m learning.&lt;/p&gt;
&lt;p&gt;When I’m working with Linux, I can’t pretend the operating system doesn’t exist.&lt;/p&gt;
&lt;p&gt;When I’m building infrastructure, I have to think about what happens when something goes wrong.&lt;/p&gt;
&lt;p&gt;And that’s exactly what I want.&lt;/p&gt;
&lt;p&gt;I don’t want software to feel like magic.&lt;/p&gt;
&lt;p&gt;I want to understand the magic trick.&lt;/p&gt;
&lt;p&gt;Preferably before production catches fire.&lt;/p&gt;
&lt;h3 id="this-is-also-why-i-build-things-fromscratch"&gt;This is also why I build things from scratch&lt;/h3&gt;
&lt;p&gt;I’ve always had a tendency to build things I probably could have just downloaded.&lt;/p&gt;
&lt;p&gt;Sometimes that’s objectively inefficient.&lt;/p&gt;
&lt;p&gt;If I need file storage, there are dozens of existing products.&lt;/p&gt;
&lt;p&gt;If I need deployment infrastructure, there are already massive platforms.&lt;/p&gt;
&lt;p&gt;If I need a programming language, there are hundreds of them.&lt;/p&gt;
&lt;p&gt;And yet I keep building.&lt;/p&gt;
&lt;p&gt;VintLang started because I wanted to understand what it actually means to build a programming language.&lt;/p&gt;
&lt;p&gt;BeamDrop exists because I wanted to build a self-hosted storage system.&lt;/p&gt;
&lt;p&gt;Ekilie exists because I wanted to understand the infrastructure behind deploying and managing software.&lt;/p&gt;
&lt;p&gt;At this point, I should probably learn to leave perfectly good software alone.&lt;/p&gt;
&lt;p&gt;But there is something different about building a thing yourself.&lt;/p&gt;
&lt;p&gt;You don’t just read about the problem.&lt;/p&gt;
&lt;p&gt;You run directly into it.&lt;/p&gt;
&lt;p&gt;You discover why the boring decisions matter.&lt;/p&gt;
&lt;p&gt;You discover where systems become complicated.&lt;/p&gt;
&lt;p&gt;You discover what breaks.&lt;/p&gt;
&lt;p&gt;You discover that your beautiful architecture diagram did not account for the disk being full.&lt;/p&gt;
&lt;p&gt;And, most importantly, you develop an intuition that you can’t get from reading API documentation alone.&lt;/p&gt;
&lt;p&gt;I don’t necessarily expect every project to become a massive company.&lt;/p&gt;
&lt;p&gt;Sometimes the project itself is the education.&lt;/p&gt;
&lt;p&gt;Sometimes you spend three days building something that already exists because you wanted to know how it works.&lt;/p&gt;
&lt;p&gt;That’s not always good engineering.&lt;/p&gt;
&lt;p&gt;But it’s excellent curiosity.&lt;/p&gt;
&lt;h3 id="i-dont-want-to-memorize-moreapis"&gt;I don’t want to memorize more APIs&lt;/h3&gt;
&lt;p&gt;There is a strange trap in software engineering where becoming more experienced can sometimes mean becoming better at remembering abstractions.&lt;/p&gt;
&lt;p&gt;You learn another framework.&lt;/p&gt;
&lt;p&gt;Another library.&lt;/p&gt;
&lt;p&gt;Another cloud service.&lt;/p&gt;
&lt;p&gt;Another ORM.&lt;/p&gt;
&lt;p&gt;Another deployment platform.&lt;/p&gt;
&lt;p&gt;Another programming language.&lt;/p&gt;
&lt;p&gt;Another JavaScript build tool whose name you will forget in approximately six months.&lt;/p&gt;
&lt;p&gt;And suddenly your definition of progress becomes:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;How many technologies do I know?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I’m becoming less interested in that question.&lt;/p&gt;
&lt;p&gt;I’d rather ask:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;How much of the system do I actually understand?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If I know five frameworks but don’t understand networking, that’s a problem.&lt;/p&gt;
&lt;p&gt;If I know three cloud platforms but can’t explain what happens when a Linux process runs out of memory, that’s a problem.&lt;/p&gt;
&lt;p&gt;If I can deploy a Kubernetes cluster but don’t understand why I needed one in the first place, that’s a particularly interesting problem.&lt;/p&gt;
&lt;p&gt;Tools are useful.&lt;/p&gt;
&lt;p&gt;Understanding is more valuable.&lt;/p&gt;
&lt;h3 id="the-deeper-i-go-the-more-interesting-softwarebecomes"&gt;The deeper I go, the more interesting software becomes&lt;/h3&gt;
&lt;p&gt;The funny thing is that learning the lower layers hasn’t made higher-level development less interesting.&lt;/p&gt;
&lt;p&gt;It has made it more interesting.&lt;/p&gt;
&lt;p&gt;When I write an API now, I think differently about it.&lt;/p&gt;
&lt;p&gt;When I deploy an application, I think about the machine running it.&lt;/p&gt;
&lt;p&gt;When I write concurrent code, I think about what the runtime and operating system are actually doing.&lt;/p&gt;
&lt;p&gt;When I design a system, I think more about failure instead of just the happy path.&lt;/p&gt;
&lt;p&gt;The layers aren’t separate.&lt;/p&gt;
&lt;p&gt;They’re connected.&lt;/p&gt;
&lt;p&gt;A web request eventually becomes bytes.&lt;/p&gt;
&lt;p&gt;Those bytes travel through networks.&lt;/p&gt;
&lt;p&gt;Processes consume resources.&lt;/p&gt;
&lt;p&gt;Data ends up somewhere on storage.&lt;/p&gt;
&lt;p&gt;The operating system manages all of it.&lt;/p&gt;
&lt;p&gt;And underneath all of that is hardware executing instructions.&lt;/p&gt;
&lt;p&gt;Which is slightly insane when you think about it.&lt;/p&gt;
&lt;p&gt;You can write:&lt;/p&gt;
&lt;p&gt;fetch(&amp;quot;/users&amp;quot;)&lt;/p&gt;
&lt;p&gt;and somehow that eventually involves electricity moving around inside a machine, packets travelling through networks, operating systems scheduling work, and hardware executing instructions.&lt;/p&gt;
&lt;p&gt;And I used to think the interesting part was the React component.&lt;/p&gt;
&lt;p&gt;The React component is still pretty cool, though.&lt;/p&gt;
&lt;h3 id="im-not-trying-to-become-a-10x-engineer"&gt;I’m not trying to become a “10x engineer”&lt;/h3&gt;
&lt;p&gt;I’ve never been particularly interested in that idea.&lt;/p&gt;
&lt;p&gt;I don’t want to optimize myself into a machine that produces pull requests.&lt;/p&gt;
&lt;p&gt;I want to become the kind of engineer who can sit in front of an unfamiliar system and eventually figure it out.&lt;/p&gt;
&lt;p&gt;Give me a strange codebase.&lt;/p&gt;
&lt;p&gt;Give me a broken server.&lt;/p&gt;
&lt;p&gt;Give me an unfamiliar protocol.&lt;/p&gt;
&lt;p&gt;Give me a performance problem.&lt;/p&gt;
&lt;p&gt;Give me a system with terrible documentation.&lt;/p&gt;
&lt;p&gt;I want my first reaction to be curiosity instead of panic.&lt;/p&gt;
&lt;p&gt;Although some panic is probably acceptable.&lt;/p&gt;
&lt;p&gt;That’s the skill I’m actually chasing.&lt;/p&gt;
&lt;p&gt;Not knowing everything.&lt;/p&gt;
&lt;p&gt;Knowing how to find out.&lt;/p&gt;
&lt;h3 id="maybe-thats-what-engineering-reallyis"&gt;Maybe that’s what engineering really is&lt;/h3&gt;
&lt;p&gt;The older I get as a developer, the less impressive syntax feels.&lt;/p&gt;
&lt;p&gt;Writing code is important, but code is only one part of the job.&lt;/p&gt;
&lt;p&gt;The interesting part is understanding why the system behaves the way it does.&lt;/p&gt;
&lt;p&gt;Understanding constraints.&lt;/p&gt;
&lt;p&gt;Understanding trade-offs.&lt;/p&gt;
&lt;p&gt;Understanding failure.&lt;/p&gt;
&lt;p&gt;Understanding the environment your software lives in.&lt;/p&gt;
&lt;p&gt;And understanding enough of the underlying machine that you aren’t completely helpless when an abstraction breaks.&lt;/p&gt;
&lt;p&gt;That’s where I want to go.&lt;/p&gt;
&lt;p&gt;I still love building web applications.&lt;/p&gt;
&lt;p&gt;I still enjoy React.&lt;/p&gt;
&lt;p&gt;I still write APIs.&lt;/p&gt;
&lt;p&gt;I still use frameworks.&lt;/p&gt;
&lt;p&gt;I’m not abandoning abstraction.&lt;/p&gt;
&lt;p&gt;I’m just becoming more interested in what exists underneath it.&lt;/p&gt;
&lt;p&gt;Because eventually, I don’t want to just be someone who knows how to use computers.&lt;/p&gt;
&lt;p&gt;I want to be someone who understands them.&lt;/p&gt;
&lt;p&gt;Even if that means occasionally spending six hours debugging something that turns out to be a typo.&lt;/p&gt;
&lt;p&gt;And I think that’s a much more interesting journey.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://medium.com/_/stat?event=post.clientViewed&amp;amp;referrerSource=full_rss&amp;amp;postId=b94385de2ffb" alt=""&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://medium.com/@tacherasasi/i-want-to-understand-computers-not-just-use-them-b94385de2ffb?source=rss-9a41d7ec29fb------2"&gt;Medium&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>What Building malloc Taught Me About Memory</title><link>https://tacherasasi.github.io/posts/what-building-malloc-taught-me-about-memory-57eae5b35166/</link><pubDate>Fri, 13 Mar 2026 00:00:00 +0000</pubDate><guid>https://tacherasasi.github.io/posts/what-building-malloc-taught-me-about-memory-57eae5b35166/</guid><description>&lt;h2 id="tags-clang"&gt;&amp;mdash;2&amp;quot;
tags: [&amp;ldquo;clang&amp;rdquo;]&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1024/1*2dOqSEchtIkDv2Fma09rOA.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Modern developers rarely think about memory.&lt;/p&gt;
&lt;p&gt;We call malloc, new, or let a garbage collector handle everything. Most of the time that’s perfectly fine. But recently I decided to dig deeper and implement a very small version of malloc myself.&lt;/p&gt;
&lt;p&gt;Not to replace real allocators. Just to understand what actually happens under the hood.&lt;/p&gt;
&lt;p&gt;The result was surprisingly simple and incredibly educational.&lt;/p&gt;
&lt;p&gt;This post walks through the core ideas.&lt;/p&gt;
&lt;h3 id="why-understanding-memory-still-matters"&gt;Why Understanding Memory Still Matters&lt;/h3&gt;
&lt;p&gt;Most software today runs on layers of abstraction.&lt;/p&gt;
&lt;p&gt;Frameworks → languages → runtimes → operating systems.&lt;/p&gt;
&lt;p&gt;That’s powerful, but it also means many developers never see the &lt;strong&gt;foundations&lt;/strong&gt; those layers are built on.&lt;/p&gt;
&lt;p&gt;Understanding memory allocation helps with:&lt;/p&gt;
&lt;p&gt;• writing faster software&lt;br&gt;
• debugging memory issues&lt;br&gt;
• understanding how runtimes work&lt;br&gt;
• building systems tools&lt;br&gt;
• designing better APIs&lt;/p&gt;
&lt;p&gt;Even if you work in higher level languages like &lt;strong&gt;Go&lt;/strong&gt; , these concepts still power everything underneath.&lt;/p&gt;
&lt;h3 id="step-1-where-malloc-gets-memory"&gt;Step 1: Where malloc Gets Memory&lt;/h3&gt;
&lt;p&gt;When a program runs, memory roughly looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Code
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Globals
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Heap ← grows upward
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Stack ← grows downward
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;malloc allocates from the &lt;strong&gt;heap&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Under the hood, the allocator asks the OS for more heap space using a system call like sbrk().&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;void *ptr = sbrk(1024);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This moves the &lt;strong&gt;program break&lt;/strong&gt; forward by 1024 bytes and returns a pointer to the new memory.&lt;/p&gt;
&lt;p&gt;Think of it like extending the end of your program’s heap.&lt;/p&gt;
&lt;h3 id="step-2-the-naive-allocator"&gt;Step 2: The Naive Allocator&lt;/h3&gt;
&lt;p&gt;The simplest possible allocator would just call sbrk.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;void *my_malloc(size_t size) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return sbrk(size);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But this immediately creates a problem.&lt;/p&gt;
&lt;p&gt;If you later call free, the allocator has &lt;strong&gt;no idea&lt;/strong&gt; :&lt;/p&gt;
&lt;p&gt;• how big the allocation was&lt;br&gt;
• where the next block starts&lt;br&gt;
• whether a block can be reused&lt;/p&gt;
&lt;p&gt;So real allocators store &lt;strong&gt;metadata&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="step-3-the-hidden-header-trick"&gt;Step 3: The Hidden Header Trick&lt;/h3&gt;
&lt;p&gt;Every allocation secretly stores a small header before the memory returned to the user.&lt;/p&gt;
&lt;p&gt;Memory layout:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[ HEADER ][ USER MEMORY ]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The header might contain:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;size
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;is_free
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;next_block
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Example structure:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;typedef&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;block_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;is_free&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;block_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;block_header_t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When the allocator returns memory, it actually returns a pointer &lt;strong&gt;after the header&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;return (void *)(block + 1);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Which means the user sees:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ptr → usable memory
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;While the allocator still has its metadata.&lt;/p&gt;
&lt;h3 id="step-4-tracking-allocations"&gt;Step 4: Tracking Allocations&lt;/h3&gt;
&lt;p&gt;Blocks are usually tracked using a linked list.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;heap_start
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[block A] → [block B] → [block C]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When malloc runs:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;search for a free block big enough&lt;/li&gt;
&lt;li&gt;reuse it if possible&lt;/li&gt;
&lt;li&gt;otherwise request more memory from the OS&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This keeps allocations fast and avoids unnecessary system calls.&lt;/p&gt;
&lt;h3 id="step-5-what-free-actually-does"&gt;Step 5: What free Actually Does&lt;/h3&gt;
&lt;p&gt;One surprising thing: free usually does &lt;strong&gt;not return memory to the OS&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Instead it just marks the block as reusable.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[HEADER free=1][memory]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Future allocations can reuse that block instead of requesting more memory.&lt;/p&gt;
&lt;h3 id="what-real-allocators-do-differently"&gt;What Real Allocators Do Differently&lt;/h3&gt;
&lt;p&gt;Real allocators (like the ones used by &lt;strong&gt;glibc&lt;/strong&gt;) add several optimizations:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Block splitting&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Free block: 100 bytes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Need: 20
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Split into:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;20 used
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;80 still free
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Block coalescing&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Adjacent free blocks merge together to reduce fragmentation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Large allocation handling&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Large allocations often use mmap instead of the heap.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thread safety&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Modern allocators maintain thread local arenas for performance.&lt;/p&gt;
&lt;h3 id="what-i-learned-from-this"&gt;What I Learned From This&lt;/h3&gt;
&lt;p&gt;Building even a tiny allocator reinforced a few important lessons.&lt;/p&gt;
&lt;h3 id="abstractions-hide-complexity-but-the-fundamentals-still-matter"&gt;Abstractions hide complexity, but the fundamentals still matter&lt;/h3&gt;
&lt;p&gt;Languages like &lt;strong&gt;Go&lt;/strong&gt; make memory management easier, but the underlying principles are still there.&lt;/p&gt;
&lt;h3 id="performance-often-comes-from-understanding-the-layers-below-you"&gt;Performance often comes from understanding the layers below you&lt;/h3&gt;
&lt;p&gt;Understanding memory layout and allocation patterns helps explain why some code performs better than others.&lt;/p&gt;
&lt;h3 id="systems-knowledge-compounds"&gt;Systems knowledge compounds&lt;/h3&gt;
&lt;p&gt;Once you understand memory allocators, many other topics suddenly make more sense:&lt;/p&gt;
&lt;p&gt;• garbage collectors&lt;br&gt;
• runtime design&lt;br&gt;
• kernel memory management&lt;br&gt;
• high performance servers&lt;/p&gt;
&lt;h3 id="final-thoughts"&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;You don’t need to write your own allocator for production software.&lt;/p&gt;
&lt;p&gt;But building one, even a small version, forces you to think like the runtime.&lt;/p&gt;
&lt;p&gt;And that perspective is valuable.&lt;/p&gt;
&lt;p&gt;The more you understand the layers beneath your tools, the better engineer you become.&lt;/p&gt;
&lt;p&gt;GITHUB =&amp;gt; &lt;a href="https://github.com/tacheraSasi/malloc.git"&gt;https://github.com/tacheraSasi/malloc.git&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://medium.com/_/stat?event=post.clientViewed&amp;amp;referrerSource=full_rss&amp;amp;postId=57eae5b35166" alt=""&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://medium.com/@tacherasasi/what-building-malloc-taught-me-about-memory-57eae5b35166?source=rss-9a41d7ec29fb------2"&gt;Medium&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Comptime vs. Macros: Metaprogramming in Zig and Rust</title><link>https://tacherasasi.github.io/posts/comptime-vs-macros-metaprogramming-in-zig-and-rust-511fedcdcffe/</link><pubDate>Mon, 10 Nov 2025 00:00:00 +0000</pubDate><guid>https://tacherasasi.github.io/posts/comptime-vs-macros-metaprogramming-in-zig-and-rust-511fedcdcffe/</guid><description>&lt;h2 id="tags-rust-macros-zig-rust-comptime"&gt;&amp;mdash;2&amp;quot;
tags: [&amp;ldquo;rust-macros&amp;rdquo;, &amp;ldquo;zig&amp;rdquo;, &amp;ldquo;rust&amp;rdquo;, &amp;ldquo;comptime&amp;rdquo;]&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1024/1*5v5iBN3T7ascRigMp8HF4Q.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Metaprogramming allows code to generate or manipulate other code, often at compile time, boosting flexibility and performance. Two modern languages tackle this differently: Zig with its &lt;em&gt;&lt;strong&gt;comptime&lt;/strong&gt;&lt;/em&gt; feature and Rust with macros. Let’s explore both with examples, drawing from recent discussions and developments as of 2025.&lt;/p&gt;
&lt;h4 id="comptime-in-zig-seamless-compile-time-execution"&gt;Comptime in Zig: Seamless Compile-Time Execution&lt;/h4&gt;
&lt;p&gt;Zig’s &lt;strong&gt;comptime&lt;/strong&gt; integrates compile-time computation directly into the language, making it feel like regular code. Functions or expressions marked with &lt;code&gt;**comptime**&lt;/code&gt; run during compilation, enabling generics, conditional compilation, and more without separate syntax. It’s restrictive by design to avoid complexity, yet powerful for tasks like serialization or ORM.&lt;/p&gt;
&lt;p&gt;A simple example: generating a factorial at compile time in Zig.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comptime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;: &lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;comptime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Computed at compile time: 120
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;compileLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Logs during compilation
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This runs entirely at compile time, embedding the result in the binary. Recent projects use comptime for auto-generating DLL loading code or erasure coding optimizations. In 2025, discussions highlight how comptime eliminates the need for macros by treating code uniformly.&lt;/p&gt;
&lt;h4 id="macros-in-rust-declarative-and-procedural-power"&gt;Macros in Rust: Declarative and Procedural Power&lt;/h4&gt;
&lt;p&gt;Rust’s macros come in two flavors: declarative (via &lt;code&gt;macro_rules!&lt;/code&gt;) for pattern-matching code generation, and procedural for custom syntax extensions. They’re explicit, ensuring type safety and hygiene (no accidental variable captures). Macros like &lt;code&gt;println!&lt;/code&gt; or &lt;code&gt;vec!&lt;/code&gt; are everyday examples.&lt;/p&gt;
&lt;p&gt;Here’s a declarative macro for a simple vector creator:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;macro_rules! my_vec {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ( $( $x:expr ),* ) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; let mut temp_vec = Vec::new();
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; $(
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; temp_vec.push($x);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; )*
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; temp_vec
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fn main() {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; let v = my_vec![1, 2, 3]; // Expands to Vec with pushes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; println!(&amp;#34;{:?}&amp;#34;, v);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This expands at compile time. Advanced uses include deriving traits or composing derives with crates like &lt;code&gt;macro_rules_attribute&lt;/code&gt;. Recent 2025 experiments, like “&lt;strong&gt;Crabtime&lt;/strong&gt; ,” even attempt to emulate Zig’s comptime in Rust using macros, showing cross-inspiration.&lt;/p&gt;
&lt;h4 id="key-comparisons"&gt;Key Comparisons&lt;/h4&gt;
&lt;p&gt;- &lt;strong&gt;Simplicity vs. Explicitness&lt;/strong&gt; : Zig’s comptime is implicit and blends with runtime code, but changes can break callers unexpectedly. Rust macros are explicit, with strong constraints via traits, reducing errors but adding boilerplate.&lt;/p&gt;
&lt;p&gt;- &lt;strong&gt;Type Access&lt;/strong&gt; : Zig provides direct type info at comptime, easing generics. Rust macros handle this through patterns or proc macros, but it’s more involved.&lt;/p&gt;
&lt;p&gt;-&lt;strong&gt;Use Cases&lt;/strong&gt; : Both enable generics and optimizations, but Zig avoids a separate macro system, while Rust’s is versatile for DSLs.&lt;/p&gt;
&lt;p&gt;In 2025, Zig’s approach is praised for reducing complexity, though Rust’s ecosystem offers more mature tools. If you’re building low-level systems, experiment with both. Zig for elegance, Rust for safety.&lt;/p&gt;
&lt;p&gt;Thanks for reading! What’s your take on metaprogramming?&lt;/p&gt;
&lt;p&gt;&lt;img src="https://medium.com/_/stat?event=post.clientViewed&amp;amp;referrerSource=full_rss&amp;amp;postId=511fedcdcffe" alt=""&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://medium.com/@tacherasasi/comptime-vs-macros-metaprogramming-in-zig-and-rust-511fedcdcffe?source=rss-9a41d7ec29fb------2"&gt;Medium&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description></item></channel></rss>