<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://bitshifter-1.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://bitshifter-1.github.io/" rel="alternate" type="text/html" /><updated>2025-05-23T09:30:05+00:00</updated><id>https://bitshifter-1.github.io/feed.xml</id><title type="html">Bitshifters</title><subtitle>Opinions on programming languages and software</subtitle><author><name>Bitshifter</name></author><entry><title type="html">Is C3 the Underdog That Will Overtake Zig and Odin?</title><link href="https://bitshifter-1.github.io/2025/05/22/c3-c-tradition.html" rel="alternate" type="text/html" title="Is C3 the Underdog That Will Overtake Zig and Odin?" /><published>2025-05-22T00:00:00+00:00</published><updated>2025-05-22T00:00:00+00:00</updated><id>https://bitshifter-1.github.io/2025/05/22/c3-c-tradition</id><content type="html" xml:base="https://bitshifter-1.github.io/2025/05/22/c3-c-tradition.html"><![CDATA[<p>(The previous post, on V, can be read <a href="https://bitshifters.cc/2025/05/17/vlang.html">here</a>)</p>

<p>In the ever-evolving landscape of systems programming languages, C3 emerges as a language that seeks to modernize C without abandoning its foundational principles. Designed to be an evolution rather than a revolution, C3 is intended to leverage familiar syntax while introducing enhancements aimed at improving safety, ergonomics, and performance.</p>

<p>The language began development in mid 2019, after C2 contributor Christoffer Lernö decided to branch out on his own, and the compiler is still primarily a single-developer effort, even though it experienced a breakthrough in popularity from mid 2024.</p>

<h2 id="philosophy">Philosophy</h2>

<p>Among other things, C3 <a href="https://c3-lang.org/getting-started/design-goals/">lists</a> the following design goals:</p>

<ul>
  <li>Procedural language, with a pragmatic ethos to get work done.</li>
  <li>Seamless C integration.</li>
  <li>Ergonomic common patterns.</li>
  <li>Avoid “big ideas”.</li>
</ul>

<p>Lernö’s approach to language design, judging from his blog on c3.handmade.network, is deeply pragmatic, iterative, and user-focused. He prioritizes practicality over purity, ergonomics over novelty, and clarity over cleverness. While not using the “Joy of Programming” motto, this language clearly shares priorities with Odin, something Lernö himself has pointed out:</p>

<blockquote>
  <p><em>“C3 has a slightly different feature set than Odin […] but the goals aligns[sic] strongly with Odin’s.”</em> <a href="https://news.ycombinator.com/item?id=43569724">link</a></p>
</blockquote>

<h2 id="first-impressions">First impressions</h2>

<p>That said, what about actually using C3? The first “Hello World” is somewhat surprising. Even though C3 claims to be an evolution of C, the example looks unfamiliar:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">module</span> <span class="n">hello_world</span><span class="p">;</span>
<span class="n">import</span> <span class="n">std</span><span class="o">::</span><span class="n">io</span><span class="p">;</span>

<span class="n">fn</span> <span class="kt">void</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
    <span class="n">io</span><span class="o">::</span><span class="n">printn</span><span class="p">(</span><span class="s">"Hello, world!"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>We still have the familiar <code class="language-plaintext highlighter-rouge">void main()</code>, C3 unexpectedly introduces an <code class="language-plaintext highlighter-rouge">fn</code> keyword. Also somewhat controversial, C3 seems to embrace <code class="language-plaintext highlighter-rouge">::</code> as the module separator, which is reminiscent of C++ rather than C, leading to comments such as:</p>

<blockquote>
  <p>*“One thing I just can’t understand is proactively using the :: syntax. It’s sooo ugly with so much unnecessary line noise” <a href="https://news.ycombinator.com/item?id=43569724">link</a></p>
</blockquote>

<p>C3 somewhat surprisingly doesn’t provide any automatic header imports unlike Zig, but using C from the language is surprisingly straightforward. I managed to write a straight-to-LibC hello world like this:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">extern</span> <span class="n">fn</span> <span class="kt">int</span> <span class="nf">puts</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">s</span><span class="p">);</span>
<span class="n">fn</span> <span class="kt">void</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
    <span class="n">puts</span><span class="p">(</span><span class="s">"Hello world!"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Moving over to Raylib, it’s straightforward to grab Raylib using the “vendor-fetch” command of the compiler. Out of the box it gives us a <code class="language-plaintext highlighter-rouge">.c3l</code> file, which is C3’s library format. This seems mainly to be easy to use with the C3 projects, but I was able to use it directly with a single file.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">import</span> <span class="n">raylib5</span><span class="p">;</span>

<span class="n">fn</span> <span class="kt">void</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
    <span class="n">rl</span><span class="o">::</span><span class="n">initWindow</span><span class="p">(</span><span class="mi">1280</span><span class="p">,</span> <span class="mi">720</span><span class="p">,</span> <span class="s">"Testing"</span><span class="p">);</span>
    <span class="n">Vector2</span> <span class="n">pos</span> <span class="o">=</span> <span class="p">{</span> <span class="mi">640</span><span class="p">,</span> <span class="mi">320</span> <span class="p">};</span>

    <span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">rl</span><span class="o">::</span><span class="n">windowShouldClose</span><span class="p">())</span> <span class="p">{</span>
        <span class="n">rl</span><span class="o">::</span><span class="n">beginDrawing</span><span class="p">();</span>
        <span class="n">rl</span><span class="o">::</span><span class="n">clearBackground</span><span class="p">(</span><span class="n">rl</span><span class="o">::</span><span class="n">BLUE</span><span class="p">);</span>
        <span class="n">rl</span><span class="o">::</span><span class="n">drawRectangleV</span><span class="p">(</span><span class="n">pos</span><span class="p">,</span> <span class="p">{</span><span class="mi">32</span><span class="p">,</span> <span class="mi">32</span><span class="p">},</span> <span class="n">rl</span><span class="o">::</span><span class="n">GREEN</span><span class="p">);</span>
        
        <span class="k">if</span> <span class="p">(</span><span class="n">rl</span><span class="o">::</span><span class="n">isKeyDown</span><span class="p">(</span><span class="n">rl</span><span class="o">::</span><span class="n">KEY_LEFT</span><span class="p">))</span> <span class="p">{</span>
            <span class="n">pos</span><span class="p">.</span><span class="n">x</span> <span class="o">-=</span> <span class="mi">400</span> <span class="o">*</span> <span class="n">rl</span><span class="o">::</span><span class="n">getFrameTime</span><span class="p">();</span>
        <span class="p">}</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">rl</span><span class="o">::</span><span class="n">isKeyDown</span><span class="p">(</span><span class="n">rl</span><span class="o">::</span><span class="n">KEY_RIGHT</span><span class="p">))</span> <span class="p">{</span>
            <span class="n">pos</span><span class="p">.</span><span class="n">x</span> <span class="o">+=</span> <span class="mi">400</span> <span class="o">*</span> <span class="n">rl</span><span class="o">::</span><span class="n">getFrameTime</span><span class="p">();</span>
        <span class="p">}</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">rl</span><span class="o">::</span><span class="n">isKeyDown</span><span class="p">(</span><span class="n">rl</span><span class="o">::</span><span class="n">KEY_UP</span><span class="p">))</span> <span class="p">{</span>
            <span class="n">pos</span><span class="p">.</span><span class="n">y</span> <span class="o">-=</span> <span class="mi">400</span> <span class="o">*</span> <span class="n">rl</span><span class="o">::</span><span class="n">getFrameTime</span><span class="p">();</span>
        <span class="p">}</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">rl</span><span class="o">::</span><span class="n">isKeyDown</span><span class="p">(</span><span class="n">rl</span><span class="o">::</span><span class="n">KEY_DOWN</span><span class="p">))</span> <span class="p">{</span>
            <span class="n">pos</span><span class="p">.</span><span class="n">y</span> <span class="o">+=</span> <span class="mi">400</span> <span class="o">*</span> <span class="n">rl</span><span class="o">::</span><span class="n">getFrameTime</span><span class="p">();</span>
        <span class="p">}</span>
        <span class="n">rl</span><span class="o">::</span><span class="n">endDrawing</span><span class="p">();</span>
    <span class="p">}</span>
    <span class="n">rl</span><span class="o">::</span><span class="n">closeWindow</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Aside from the aforementioned <code class="language-plaintext highlighter-rouge">:</code> this is just C. For sticking to the basics and not changing what doesn’t need to be changed, C3 really gets a big thumbs up. For a C programmer it can’t get much simpler than this.</p>

<h2 id="other-language-features">Other language features</h2>

<p>On its homepage C3 boasts of “compile time and semantic macros”, “gradual contracts”, “zero overhead errors” and “runtime and compile time reflection”. While the macros and compile time seems to be a simpler version of Zig’s comptime, but this appears to be a deliberate choice. The design is explained in his blog post <a href="https://c3.handmade.network/blog/p/8590-the_downsides_of_compile_time_evaluation">The downsides of compile time evaluation</a></p>

<p>He writes:</p>
<blockquote>
  <p><em>“Macros and compile time form a set of meta programming tools, and in general meta programming has very strong downsides in terms of maintaining and refactoring code”</em></p>
</blockquote>

<p>It’s clear that compile-time functionality is available but not intended for heavy use, unlike Zig or Jai.</p>

<h2 id="error-handling-in-c3">Error handling in C3</h2>

<p>C3’s error handling is probably the biggest hurdle in learning the language. The docs spend two different pages trying to explain how the errors work, but still leaves you somewhat confused:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">File</span><span class="o">?</span> <span class="n">file</span> <span class="o">=</span> <span class="n">file</span><span class="o">::</span><span class="n">open</span><span class="p">(</span><span class="s">"test.txt"</span><span class="p">,</span> <span class="s">"rb"</span><span class="p">);</span>
</code></pre></div></div>

<p>This would look familiar to users of Swift and Kotlin, and largely you can unwrap or force unwrap in the same way but there’s a twist: this optional value also carries an error code as well and it’s not possible to use it in structs or as parameters.</p>

<p>In Swift we can do optional chaining like <code class="language-plaintext highlighter-rouge">file?.close()</code>, but in C3 it’s implicit: <code class="language-plaintext highlighter-rouge">file.close()</code>. This novel take on errors is either brilliant or just weird.</p>

<p>On the Zig discord a friendly user pointed out that it’s is making it easy to use C3 from C:</p>

<blockquote>
  <p><em>“it’s a better version of C’s convention for returning int where -1 means error, positive or 0 means success, and maybe other negative values mean other specific errors. Instead of returning -1 or other negative error codes, you return foo?, and the user will still probably still check it as a binary condition, as in, in C, result = foo(); if (result &lt; 0), and in C3, if (catch excuse = foo())”.</em></p>
</blockquote>

<p>On the C3 discord there was some dissent on whether it was good. One user said:</p>

<blockquote>
  <p><em>“I love everything in C3 except the error system.  I am using the error system only in inevitable situations.”</em></p>
</blockquote>

<p>However, most other C3 users seemed to disagree with that opinion:</p>

<blockquote>
  <p><em>“For me error handling was one of the reasons to switch to C3 from Odin. IMHO in C3 it’s (1) much easier to compose code and (2) much harder to ignore error by accident.”</em></p>
</blockquote>

<blockquote>
  <p><em>“Error handling is also one of the reasons I prefer C3 over Odin.”</em></p>
</blockquote>

<p>For a language designer who is advocating only <a href="https://c3.handmade.network/blog/p/8682-some_language_design_lessons_learned">innovating if there is no other option</a>, it’s clear that
Lernö must have felt that adding error handling was a must-have improvement.</p>

<h2 id="compile-time-and-operator-overloading">Compile time and operator overloading</h2>

<p>Aside from error handling, the major difference compared to C is the macro system. Gone is the trusty – but problematic - C preprocessor, replaced by compile time macros similar to Zig. Although whereas Zig’s comptime is sometimes somewhat implicit, C3’s macros are clearly inspired by the C preprocessor in that the compile time statements are orthogonal in style to runtime code, which has been both applauded and criticized.</p>

<p>Operator overloading is noteworthy in that neither Odin nor Zig – the other two most C-like alternatives – have been interested in implementing it. Andrew Kelley expressing it to be in violation to Zig’s philosophy. It’s interesting that C3 chooses to be more similar to Jai and V, both of which support operator overloading.</p>

<h2 id="still-small-and-evolving">Still small and evolving</h2>

<p>While Odin and Zig are fairly stable feature wise (and V at least intending to be so), C3 is still refining  itself. While the language is stable enough to only have breaking releases for dot-1 increments, it’s still evolving with operator overloading for arithmetics only added in a recent release.</p>

<p>While the Raylib bindings were fine, it is clear that all the “vendor-fetch” libraries might not be polished. Lernö admits as much on the C3 discord:</p>

<blockquote>
  <p><em>“As I’ve said before, I need to take a more closer[sic] look at vendor this year to try to make them more consistent. People have been contributing but I haven’t put in the time to guide it.”</em></p>
</blockquote>

<p>C3’s community remains comparatively small next to Zig, Odin, and V. On the positive side, Lernö has a stellar reputation for responding to bug reports. Release schedules also follow a regular timetable, with new 0.0.1 releases every month. Odin similarly has a monthly release schedule, but Zig still (at the time of this writing) struggles with their massive dot-1 yearly releases which puts a lot of stress on the community.</p>

<p>Still it’s clear that it doesn’t yet have the same momentum.</p>

<h2 id="summary">Summary</h2>

<p>C3 is a compelling option, particularly for C and C++ developers: it seeks to modernize the C experience without abandoning the language’s core principles.  Its enhancements in safety, performance, and ergonomics make it a strong contender in the low level programming domain and beyond. While it may not yet have the extensive ecosystems of some of its peers, its design philosophy and compatibility with C position it as a practical choice for projects requiring both modern features and legacy integration.</p>

<p>Compared to Jai, Odin, Zig, and V, C3 clearly carves out its own principles ideas, distinct from the others. While its features are more complex than Odin and Zig’s, in part due to its commitment to preserving C’s syntax and semantics, whereas both Odin and Zig has had an easier time trimming away features.</p>

<p>That is not to say that C3 feels particularly complex. It’s familiar and approachable with excellent ergonomics and a feature set that feels very complete without being overwhelming.</p>

<hr />

<p>Discuss this article <a href="https://www.reddit.com/r/programming/comments/1kszaxr/c3_iterative_innovation_in_the_c_tradition/">on Reddit</a> or <a href="https://news.ycombinator.com/item?id=44065756">Hacker News</a>.</p>

<p>The next post in the series will compare the languages covered so far.</p>]]></content><author><name>Bitshifter</name></author><summary type="html"><![CDATA[(The previous post, on V, can be read here)]]></summary></entry><entry><title type="html">You Can’t Say That About Our Language!</title><link href="https://bitshifter-1.github.io/2025/05/20/shut.html" rel="alternate" type="text/html" title="You Can’t Say That About Our Language!" /><published>2025-05-20T00:00:00+00:00</published><updated>2025-05-20T00:00:00+00:00</updated><id>https://bitshifter-1.github.io/2025/05/20/shut</id><content type="html" xml:base="https://bitshifter-1.github.io/2025/05/20/shut.html"><![CDATA[<p>An interlude:</p>

<p>Apparently, my experience with the Zig community was more par for the course than I had imagined.</p>

<p>I asked the V community for feedback on the V article, and they told me I should spend more time discussing V’s memory management — which I only mentioned in passing, choosing to highlight the flagship “autofree” feature and not explicitly noting that it’s also possible to manually perform heap allocations.</p>

<p>I deemed this out of scope for a very rough overview. But that — together with my impression that the current state was somewhat fragile — apparently upset the entire community rather badly:</p>

<blockquote>
  <p><em>“The blog post had numerous factual errors and was full of misinformation. To the extent that when it was posted on Hacker News, it was flagged and the title removed, which is very unusual for them. It appears the post was such egregious clickbait, in addition to trying to stir up language flamewars and drama, that their moderators aren’t tolerating it. To the OP, of course particular competitors and individuals associated with them, will encourage or instigate others to carry out attacks for them against the V language or whichever competitors are on their target list. They will attempt to convert you or anyone susceptible into being their tool. Sincerely hope you can find the moral courage to resist being recruited for such unethical behavior, for the love of programming and allowing people to freely choose.”</em> <a href="https://www.reddit.com/r/programming/comments/1kp6w36/comment/mt3t77t/">link</a></p>
</blockquote>

<p>For the record: getting something “flagged” on HN is something <em>anyone</em> can do — not just moderators. I encouraged the V community to <em>comment</em> and correct any mistakes in the text. Instead, they went for the “take it down, it’s misinformation!” approach.</p>

<p>What they actually seem to disagree with aren’t really the facts, but the interpretations:</p>

<blockquote>
  <p><em>“This piece cherry-picks misfires, ignores the last couple years of solid momentum, and pads its case with biased jabs while skipping repos that actually ship code in V. It lands as a “see, I told you so” hit, not a balanced review… Its takes on memory, stdlib, and maturity are half-true at best.”</em> <a href="https://news.ycombinator.com/item?id=44017739">link</a></p>
</blockquote>

<p>(I’m sure that’s what it looks like from within the bubble)</p>

<p>As a counterpoint to this a previous V contributor showed weighted in:</p>

<blockquote>
  <p><em>“I disagree. Having been a previous V developer contributing to the language (though arguably very little), the issues of missed deadlines, fake it till you make it and more seems completely valid.</em></p>

  <p><em>If you join the Discord and search for “this week”, “this month”, “next week” and “next month” with Alex Medvednikov as the author, you’ll see 80+ (yes, that many) missed deadlines, some that go all the way back to 2019.
Version 0.3 promised autofree and a syntax freeze, it got none of those. I remember discussing this with Alex, saying that it would be ideal, to publicly mention that autofree wouldn’t be included in the release, and it is as if he just couldn’t see why that type of communication was important.</em></p>

  <p><em>There were also instances of moderators publicly being mean to other chatters, _especially_ when asking about valid concerns in the language, like the criticism mentioned in other blog posts (specifically the “V is for Vaporware” posts). When talking to one of the other moderators, this was defended as “cultural differences”.
I left the team (and was blocked by Alex) the same day that another developer left.”</em></p>
</blockquote>

<h3 id="wake-up-and-smell-the-coffee">Wake up and smell the coffee</h3>

<p>Given the drama and outrage from Zig and V, I feel like the communities need a serious wake-up call: in the real world, no one cares about your small niche languages. V might take pride in being number 43 on the TIOBE index (no idea how that happened, given all the other negative trends), which places it behind such household names as <em>ABAP</em> and <em>GAMS</em>. Nor should Zig (42 on Languish) be uncorking the champagne — that’s a share of 0.18%.</p>

<p>Get this: no one is using your languages. You might think that because a few repos exist, your language is on the verge of a breakthrough — but face it: you don’t matter and your language doesn’t matter yet.</p>

<p>Maybe in the future it will. But for now, the average programmer couldn’t care less.</p>

<p>I care enough to actually sit down with your language and others to do a comparison. I’m writing <em>overviews</em>, not deep dives. Apparently this nuance is lost on language cultists — the idea that someone could simply want to write a summary for <em>normal programmers</em> who just want a quick look at the superficial differences.</p>

<p>So boo-hoo — but I won’t give preferential treatment to your cult. I don’t care, and it’s not “unfair” to treat you the same as everyone else.</p>

<p>So just kindly shut up.</p>]]></content><author><name>Bitshifter</name></author><summary type="html"><![CDATA[An interlude:]]></summary></entry><entry><title type="html">Can V Deliver on Its Promises?</title><link href="https://bitshifter-1.github.io/2025/05/17/vlang.html" rel="alternate" type="text/html" title="Can V Deliver on Its Promises?" /><published>2025-05-17T00:00:00+00:00</published><updated>2025-05-17T00:00:00+00:00</updated><id>https://bitshifter-1.github.io/2025/05/17/vlang</id><content type="html" xml:base="https://bitshifter-1.github.io/2025/05/17/vlang.html"><![CDATA[<p>(The previous post, on Zig, can be read <a href="https://bitshifters.cc/2025/05/04/zig.html">here</a>)</p>

<p>When Alexander Medvednikov announced the V language it was to both cheers of “This is the best language I’ve seen!” and skepticism “These claims can’t possibly be true”. Controversies have dogged the V language to the point that actual contributors and users have very little tolerance to critics. As one Hacker News user put it:</p>

<blockquote>
  <p><em>“Vlang is infamous for crowdfunding on the back of many extremely grandiose, but not actually implemented claims.”</em> <a href="https://news.ycombinator.com/item?id=38837769">link</a></p>
</blockquote>

<p>But lost in the noise is a simpler question: is it actually good as a language?</p>

<p>So rather than diving into the history of the language, let’s start with first impressions.</p>

<h2 id="first-impressions">First impressions</h2>

<p>Out of the box, running just <code class="language-plaintext highlighter-rouge">v</code> launches a REPL. This enforces the feel of a high level language even if the compiler warns that it’s “highly experimental”.</p>

<p>As expected, the “Hello, world” is simple and script-like:</p>

<pre><code class="language-vlang">fn main() {
    println("Hello, world!")
}
</code></pre>

<p>Again, similar to scripting languages, V has its own package repository somewhat unimaginatively called <em>VPM</em>. While not beating Odin, it’s close: just type <code class="language-plaintext highlighter-rouge">v install raylib</code> and it’s downloaded.</p>

<p>Using the Raylib bindings clearly shows that they are generated: <code class="language-plaintext highlighter-rouge">raylib.is_key_down(int(raylib.KeyboardKey.key_down))</code> is not particularly reasonable compared to Zig’s <code class="language-plaintext highlighter-rouge">rl.IsKeyDown(rl.KEY_LEFT)</code> or Odin’s <code class="language-plaintext highlighter-rouge">rl.IsKeyDown(.LEFT)</code>, but inevitable with the C-to-V conversion the bindings rely on.</p>

<pre><code class="language-v">import raylib

fn main() 
{
    raylib.init_window(1280, 768, 'Testing')
    mut pos := raylib.Vector2{640, 320}
    for !raylib.window_should_close() {
        raylib.begin_drawing()
        raylib.clear_background(raylib.blue)
        raylib.draw_rectangle_v(pos, raylib.Vector2{32, 32}, raylib.green)
        if raylib.is_key_down(int(raylib.KeyboardKey.key_left)) {
            pos.x -= 400 * raylib.get_frame_time()
        }
        if raylib.is_key_down(int(raylib.KeyboardKey.key_right)) {
           pos.x += 400 * raylib.get_frame_time()
        }
        if raylib.is_key_down(int(raylib.KeyboardKey.key_up)) {
           pos.y -= 400 * raylib.get_frame_time()
        }
        if raylib.is_key_down(int(raylib.KeyboardKey.key_down)) {
           pos.y += 400 * raylib.get_frame_time()
        }
        raylib.end_drawing()
    }
    raylib.close_window()
}
</code></pre>

<p><em><strong>EDIT 2025-05-20:</strong> A user wanted to point out that it’s possible to alias “raylib” to “rl”. I assumed aliasing was there somewhere but wanted to follow the examples in the Raylib bindings.</em></p>

<h2 id="the-language-a-mixed-set-of-features">The language: a mixed set of features</h2>

<p>On paper this language looks very appealing: you get “immutable by default” variables, a “minimalist design” that promises tight code, and a suite of safety features—bounds checking, option (?) and result (!) types, and the outright elimination of null pointers. For concurrency, V sports Go-like channels and it memory management promises “autofree” without a GC.</p>

<pre><code class="language-vlang">s := '[{"name":"Frodo", "age":25}, {"name":"Bobby", "age":10}]'
// 'users' is implicitly freed 
mut users := json.decode([]User, s) or {
    eprintln('Failed to parse json')
    return
}
for user in users {
    println('${user.name}: ${user.age}')
}`
</code></pre>

<p>Similar to Odin, it also gives you built-in dynamic arrays and maps.</p>

<p>However, all of these features pull in different directions. It’s illustrative that there is an escape hatch to enable globals that seems to be used quite a lot:</p>

<pre><code class="language-vlang">@[has_globals]
// Game state
__global ball = raylib.Vector2{screen_width / 2, screen_height / 2}
__global ball_direction = raylib.Vector2{1, 1}
</code></pre>

<p><em><strong>EDIT 2025-05-20:</strong> The same user wanted to add this about global variables: “Vlang encourages using struct fields instead of global variables. Global variables have to be specifically activated from the command line and are very discouraged. They are reserved for primarily compatibility with other languages or very specific use cases”.</em></p>

<p><em>It is odd then that it appears in several code examples I looked at, but maybe those have to do with C interop primarily? If so then you should at least be prepared to use it if you are working outside of pure V code.</em></p>

<p>The “autofree” is in theory similar to that of the <a href="https://strlen.com/lobster/">Lobster</a> language, but in practice V is using a regular GC and it’s unclear whether it will ever come close to its initial claims.</p>

<p>What does V try to be? Is it a simpler Rust, a more low level Go language or a C alternative with a cleaner syntax?</p>

<p>The fairly loose memory management is very far from Odin or Zig. For V optimizing memory management appears to be something you do at the end for optimization (even disregarding the fact that “autofree” doesn’t work yet). Of course, while this might be expected for projects written in Rust or modern C++, it might not sit well with programmers that want C’s low level control.</p>

<p>This means that V isn’t ideal for projects requiring more precise resource management as it is much less straightforward to achieve.</p>

<p>Visually, V code is clean and arguably outshines Odin in clarity. It’s no wonder that it struck a chord when it was first announced. Zig code in comparison feels coarse and inelegant, without any clear payoff in readability.</p>

<p>Despite visual clarity, the language semantics feel unfinished. Despite ensurances that the language “will not change much” it’s really unclear how a 1.0 of the language will work. One user summed it up this way:</p>

<blockquote>
  <p><em>“After reading a lot of threads/articles/comments about this language, I’m convinced that V adopted the ‘Fake it till you make it’ philosophy.”</em> <a href="https://www.reddit.com/r/ProgrammingLanguages/comments/j4sjdb/the_v_programming_language/">link</a></p>
</blockquote>

<h2 id="using-v">Using V</h2>

<p>On a more positive note, V comes with a lot of different examples, from a self learning Flappy Bird to working with ORMs. The standard library also boasts everything from SSL connections to DB clients. However, if we look closely, those are actually bindings to existing C libraries, and not anything written in native V.</p>

<p>This means that the actual standard library code is largely wrappers around well-known C libraries. It is one thing to have the standard library depend on one or two C libraries, but the amount of C libraries that goes into the V standard library does not seem to be small.</p>

<p>While Odin has something similar in its <code class="language-plaintext highlighter-rouge">vendor</code>, those libraries are at least well controlled and understood to be external dependencies. In the V case this is much more opaque, which potentially could put a developer in a very tight spot when trying to ship something believed only to be dependent on the V standard library, but in reality depends on many different C libraries.</p>

<p>I think “brittle” is the best way to describe the situation. This underlying fragility, which by all accounts have bee present since V’s early days, makes it hard to recommend the language for serious use.</p>

<h2 id="the-future-of-v">The future of V</h2>

<p>In 2020 Medvednikov announced that after 0.4 the language would quickly reach 1.0. Five years later, it’s still at 0.4, and the text was updated to say “after 0.6”. Even disregarding controversies, it’s clear that V is not really able to hit its targets.</p>

<p>In software this is nothing new, Zig has a similar problem, with 1.0 being ready in two years for several years now. However, V is rather exceptional in how many self imposed deadlines it has failed to meet. The language really feels like an 0.4 language in terms of stability, even if it is much more mature in terms of toolchain completeness.</p>

<p>Past controversies also pose a larger problem for the language: not only has it lost momentum, but there are a lot of people which simply will not use it on account of being a scam. And this in turn hurts the long term prospects of V. As one Hacker News commenter bluntly put it:</p>

<blockquote>
  <p><em>“The root cause is that V initially made some promises that seemed completely unrealistic… and some that are arguably impossible… When V was eventually released, the implementation fell very short of these goals.”</em> <a href="https://www.reddit.com/r/ProgrammingLanguages/comments/vq4ul6/why_does_v_language_get_so_much_hate/">link</a></p>
</blockquote>

<p>The language has a lot to prove, and a long way to go before it matures. On the positive side, its syntax is almost universally well loved.</p>

<p><em><strong>EDIT 2025-05-20:</strong> The V community wants to point out that V shows up on the TIOBE index. One V user writes: “As of May 2025, in the TIOBE Index, Vlang is ranked # 43. That is above many of their competitors, despite all the things they have done or do to inhibit the language or damage its reputation.”</em> <a href="https://www.reddit.com/r/programming/comments/1kp6w36/comment/mt3t77t]">link</a></p>

<p><em>It is certainly an accomplishment, but on other indexes V fall behind. On <a href="https://tjpalmer.github.io/languish/">Languish</a> V is at 193 with Zig at 42, and V fails to show up in the <a href="https://survey.stackoverflow.co/2024/technology">Stack overflow survey for 2024</a>.</em></p>

<h2 id="summary">Summary</h2>

<p>Despite the controversies surrounding V, the language has not died but clearly survived and still thrives to some extent. While V is syntactically elegant and pleasant to use, its semantics are somewhat haphazard, some of its key features incomplete and its tooling clearly not yet battle-tested.</p>

<p>With its high-level memory model, V feels less like a C replacement and more like a lightweight Rust or C++ alternative.</p>

<p>It’s definitely worth a try for those curious about new languages, but its current immaturity means that, for now at least, Jai, Odin, and Zig remain safer and more pragmatic choices for serious projects.</p>

<p>Only time will tell if V can mature enough to truly challenge the other players.</p>

<hr />

<p>Discuss this on <a href="https://news.ycombinator.com/item?id=44017739">Hacker News</a> and <a href="https://www.reddit.com/r/programming/comments/1kp6w36/can_v_deliver_on_its_promises/?utm_source=share&amp;utm_medium=web3x&amp;utm_name=web3xcss&amp;utm_term=1&amp;utm_content=share_button">r/Programming</a></p>

<p><em><strong>EDIT 2025-05-20:</strong> I’ve incuded feedback from the Reddit user <strong>waozen</strong>. The full comment can be read <a href="https://www.reddit.com/r/programming/comments/1kp6w36/comment/mt3t77t/?context=3">on Reddit</a></em></p>

<p>The next post covers C3 and can be read <a href="https://bitshifters.cc/2025/05/22/c3-c-tradition.html">here</a>.</p>]]></content><author><name>Bitshifter</name></author><summary type="html"><![CDATA[(The previous post, on Zig, can be read here)]]></summary></entry><entry><title type="html">Next up: C3, Hare and V</title><link href="https://bitshifter-1.github.io/2025/05/12/the-rest.html" rel="alternate" type="text/html" title="Next up: C3, Hare and V" /><published>2025-05-12T00:00:00+00:00</published><updated>2025-05-12T00:00:00+00:00</updated><id>https://bitshifter-1.github.io/2025/05/12/the-rest</id><content type="html" xml:base="https://bitshifter-1.github.io/2025/05/12/the-rest.html"><![CDATA[<p>Zig and slowly but surely Odin is gaining some visibility, but what about the other contenders?</p>

<h3 id="c3">C3</h3>

<p>Unlike all other contenders, C3 sets out to be an “evolution” of C. The site highlights “Full C ABI compatibility”, “Module system” and “Operator Overloading” as its major changes, but announces other improvements to the C experience as well.</p>

<h3 id="v">V</h3>

<p>A controversial language that initially skyrocketed in interest but has been source of much controversy. Despite this it has built a solid community of contributors. Perhaps the language with the most high-level looking syntax.</p>

<h3 id="hare">Hare</h3>

<p>Designed by well known Open Source profile Drew DeVault it aims to be a simple language that will last the next 100 years. Strongly inspired by C’s stability and longevity. DeVault publicly stated that non-free platforms (presumably MacOS, Windows and similar) would never be supported – which perhaps puts limits to its usefulness.</p>

<p>Could any of these be a better alternative than Odin and Zig? I’ll try to answer that in the upcoming blog posts.</p>]]></content><author><name>Bitshifter</name></author><summary type="html"><![CDATA[Zig and slowly but surely Odin is gaining some visibility, but what about the other contenders?]]></summary></entry><entry><title type="html">No one likes a critic</title><link href="https://bitshifter-1.github.io/2025/05/11/no-one-likes.html" rel="alternate" type="text/html" title="No one likes a critic" /><published>2025-05-11T00:00:00+00:00</published><updated>2025-05-11T00:00:00+00:00</updated><id>https://bitshifter-1.github.io/2025/05/11/no-one-likes</id><content type="html" xml:base="https://bitshifter-1.github.io/2025/05/11/no-one-likes.html"><![CDATA[<p>The article on Odin was well liked (aside from an weird accusation that it was AI-written). The Zig article was “well written but we hated it”.
It’s a weird dynamic. It failed to get any traction because it was negative, but the negative part was genuine feedback on the beginner experience.</p>

<p>Improving these pain points would make it more easy to get into, but since criticism isn’t interesting for people to hear, the feedback has problems penetrating.
(On the other hand, people love criticism if it’s a the current thing in vogue to dislike)</p>

<p>One commenter said:</p>

<blockquote>
  <p><em>I also think you didn’t show enough data for some of your conclusions and didn’t highlight enough of the pros of the language while focusing on the cons.</em></p>
</blockquote>

<p>I will say it’s is hard to figure out what the pros of Zig are when the feedback on the article consisted of downvotes without comments. But that’s Reddit for you.</p>

<p>The same commenter later quipped:</p>

<blockquote>
  <p><em>I think you should try out the language before writing an article, tbh</em></p>
</blockquote>

<p>I think this really brings home the problem in the attitude of Zig towards beginners: you’re supposed to dive into the language and weather the pain of learning the language in depth, not until then will you understand the excellence of the language.</p>

<p>The pain I described of having to learn how build.zig works while still a beginner at Zig is therefore no problem, because the opinions of beginners doesn’t matter.</p>

<p>I am constantly reminded of how different this is from most other languages that actually try to be beginner friendly as well.</p>]]></content><author><name>Bitshifter</name></author><summary type="html"><![CDATA[The article on Odin was well liked (aside from an weird accusation that it was AI-written). The Zig article was “well written but we hated it”. It’s a weird dynamic. It failed to get any traction because it was negative, but the negative part was genuine feedback on the beginner experience.]]></summary></entry><entry><title type="html">Odin, A Pragmatic C Alternative with a Go Flavour</title><link href="https://bitshifter-1.github.io/2025/05/04/odin.html" rel="alternate" type="text/html" title="Odin, A Pragmatic C Alternative with a Go Flavour" /><published>2025-05-04T00:00:00+00:00</published><updated>2025-05-04T00:00:00+00:00</updated><id>https://bitshifter-1.github.io/2025/05/04/odin</id><content type="html" xml:base="https://bitshifter-1.github.io/2025/05/04/odin.html"><![CDATA[<p>(Previous post in the series was on Jai and can be found <a href="/2025/04/28/jai.html">here</a>)</p>

<p>Odin is a general-purpose systems programming language authored by Bill “gingerBill” Hall.
Designed as a modern alternative to C, Odin emphasizes simplicity, performance, and readability
without sacrificing control over low-level details.</p>

<p>The website says it’s “data-oriented”, and features such as SOA (structs-of-arrays) and implicit zero initialization tie into that. Despite this focus, the language surprisingly has dynamic
maps and arrays built into the language itself. While the memory is still manually managed,
it’s uncommon to see such built-ins.</p>

<p>This perhaps sets the tone of Odin: it tries to be ergonomic and easy to write by offering a lot out of the box. Odin also comes with “vendor”, containing bindings to a wide variety of popular libraries. This makes the language very easy to get into.</p>

<h2 id="design-philosophy">Design Philosophy</h2>

<p>Odin focuses on practical solutions to real-world programming challenges—in other words, it favours pragmatism over idealism (I’ll return to this when I later discuss Zig). Rather than introducing complex features, Odin focuses on code that is simple and clean to read and reason about. This is the polar opposite of Zig’s embracing of metaprogramming for as much as possible.</p>

<p>Odin also has a fairly old-fashioned view of types. The current trend is to make programming languages increasingly more complex so that they can describe more and more types in the language itself. Odin instead harkens back to older languages where built-in types flourished. Consequently, Odin does not just offer the aforementioned hashmaps and dynamic arrays, but also numerical types
such as complex numbers, vectors, matrices, and even quaternions. This makes up for its rejection of operator overloading by a wide margin. It’s not a coincidence that the flagship app to demonstrate Odin’s capabilities, EmberGen, is a math- and graphics-heavy tool.</p>

<h2 id="a-quick-look-at-the-syntax">A quick look at the syntax</h2>

<p>Odin has a fairly straightforward syntax for a beginner. The fact that there is no excessive nagging about mutability or constness makes things just work as expected.</p>

<p>The declaration is otherwise very inspired by Jai and fairly minimal. Odin’s concession to modern fashion shows up in its removal of the traditional <code class="language-plaintext highlighter-rouge">;</code>.</p>

<p>Odin produces code anyone used to C or other low level languages can read at a glance. Here is a short “move the dot around the screen” using Raylib:</p>

<pre><code class="language-odin">package test

import rl "vendor:raylib"

main :: proc() 
{
    rl.InitWindow(1280, 720, "Testing")
    pos : rl.Vector2 = { 640, 320 }
		
    for !rl.WindowShouldClose() {
        rl.BeginDrawing()
        rl.ClearBackground(rl.BLUE)
        rl.DrawRectangleV(pos, {32, 32}, rl.GREEN)
        
        if rl.IsKeyDown(.LEFT) {
            pos.x -= 400 * rl.GetFrameTime()
        }
        if rl.IsKeyDown(.RIGHT) {
            pos.x += 400 * rl.GetFrameTime()
        }
        if rl.IsKeyDown(.UP) {
            pos.y -= 400 * rl.GetFrameTime()
        }
        if rl.IsKeyDown(.DOWN) {
            pos.y += 400 * rl.GetFrameTime()
        }
        rl.EndDrawing()
    }
    rl.CloseWindow()
}
</code></pre>

<p>All in all, Odin’s syntax mostly feel familiar, even with changes to things like function declaration syntax. There are no deep quirks, the changes are superficial.</p>

<p>This conservative streak in Odin is echoed by others. Dale Weiler, at JangaFX, was an early adopter. He wrote on his blog:</p>

<blockquote>
  <p><em>“Odin is a systems programming language that is more conservative in its design than other newer programming languages such as Rust, Zig, and Carbon. The design ideology around Odin is to provide some greatly needed quality of life improvements over the lingua-franca of systems languages: C, while still staying as simple as C.”</em> <a href="https://graphitemaster.github.io/odin_review/">link</a></p>
</blockquote>

<p>It’s clear that Odin has pulled off the trick of feeling familiar even if the syntax is much different from C.</p>

<h2 id="error-handling">Error handling</h2>

<p>Odin’s most controversial choice is probably the error handling, which uses multiple returns in the fashion of Go. And while Odin offers better ergonomics with <code class="language-plaintext highlighter-rouge">or_else</code> and <code class="language-plaintext highlighter-rouge">or_return</code>, it can feel clunky compared to other solutions.</p>

<p>As far as first impressions go, this choice is probably not ideal. It seems like a common thing to criticize. On the other hand, this conceptual simplicity goes hand in hand with the straightforwardness that is the trademark of Odin’s design.</p>

<h2 id="the-joy-of-programming">The Joy of Programming</h2>

<p>Odin shares the “joy of programming” slogan with Jai, but the appreciation seems to be real. To quote one user:</p>

<blockquote>
  <p><em>“Odin has renewed my joy of programming. Built-in bounds checking, slices, distinct typing, no undefined behavior, consistent semantics between optimization modes, minimal implicit type conversions, context system, and the standard library tracking allocator combine together to eliminate the majority of memory bugs I found use for sanitizers in C/C++.”</em> <a href="https://news.ycombinator.com/item?id=32799499">link</a></p>
</blockquote>

<p>Another user writes:</p>

<blockquote>
  <p>*“[…] moving from C to Odin was quite a pleasant and rather easy experience. The languages are rather similar but Odin takes the painful bits away, letting you to focus on the problem instead of wondering why something is going weirdly wrong again.” <a href="https://akselmo.dev/posts/moving-from-c-to-odin/">link</a></p>
</blockquote>

<p>Just taking the language for a spin, it’s friendly and does what you think. It’s essentially an approachable language that just feels nice to use.</p>

<h2 id="comparisons">Comparisons</h2>

<h3 id="jai">Jai</h3>

<p>Odin and Jai share syntactic similarities, but their approaches differ. Where Jai emphasizes compile-time execution and metaprogramming, offering powerful abstractions, Odin instead focuses
on simplicity and lots of features out of the box.</p>

<p>While on a syntactic level Odin clearly took inspiration from Jai, the two languages have evolved very differently. Jonathan Blow stated in a video that Jai has grown more complex than he planned.</p>

<p>Odin, on the other hand, is clearly a language that feels simple to learn and use.</p>

<h3 id="zig">Zig</h3>

<p>Both Odin and Zig aim to modernize systems programming, but they clearly diverge in philosophy. Zig offers extensive compile-time metaprogramming (although not to the level of Jai), whereas Odin only retains the necessary functionality needed for conditional compilation.</p>

<p>Odin, like Jai, has runtime reflection. Zig’s reflection, on the other hand, is generally limited to compile time. In practice, this means Zig relies on a lot of metaprogramming to do things like serialization through type inspection, whereas in Odin it’s available at runtime, so the code is magnitudes easier to understand.</p>

<p>The biggest difference is its attitude toward the programmer’s experience. Odin tries to be simple and make it fun and pleasant to program in; it has a straightforward, no-fuss syntax.</p>

<p>Zig, on the other hand, cares little about the user experience and prefers verbose explicitness over convenient abstractions.</p>

<p>A developer compared the two:</p>

<blockquote>
  <p><em>“Zig is very verbose… Odin in comparison is very minimal in terms of typing while communicating basically the same info.”</em> <a href="https://forum.odin-lang.org/t/comparing-zig-with-odin/740/2">link</a></p>
</blockquote>

<h2 id="criticisms">Criticisms</h2>

<p>While Odin has been in use by projects for quite a while, the official documentation is still lacking in depth and examples. On top of this, it seems that the primary community platform is Discord, which may not be accessible to all.</p>

<p>There have been people criticizing the lack of more extensive metaprogramming. But with access to type information at runtime and a fairly wide array of built-ins, it is not clear whether this has any merit.</p>

<p>For example, both Zig and Jai offer support for struct-of-arrays through metaprogramming. In Odin, however, this feature is built into the language.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Odin presents a compelling option for developers seeking a modern, efficient, and readable systems programming language. That it is successful in graphics-intensive applications like EmberGen demonstrates its robustness and performance. While it has areas for growth—particularly in documentation and community accessibility—Odin’s pragmatic design and focus on developer experience make it a worthy alternative to C for contemporary systems programming needs.</p>

<hr />

<p>Next post in the series is on Zig and can be read <a href="/2025/05/04/zig.html">here</a>)</p>]]></content><author><name>Bitshifter</name></author><summary type="html"><![CDATA[(Previous post in the series was on Jai and can be found here)]]></summary></entry><entry><title type="html">Zig: A New Direction for Low-Level Programming?</title><link href="https://bitshifter-1.github.io/2025/05/04/zig.html" rel="alternate" type="text/html" title="Zig: A New Direction for Low-Level Programming?" /><published>2025-05-04T00:00:00+00:00</published><updated>2025-05-04T00:00:00+00:00</updated><id>https://bitshifter-1.github.io/2025/05/04/zig</id><content type="html" xml:base="https://bitshifter-1.github.io/2025/05/04/zig.html"><![CDATA[<p>(Previous post was on Odin <a href="https://bitshifters.cc/2025/05/04/odin.html">here</a></p>

<p><a href="https://ziglang.org/">Zig</a> is a general-purpose systems programming language designed by Andrew Kelley. The language bills itself as “a general-purpose programming language and toolchain for maintaining robust, optimal, and reusable software”.</p>

<p>Proponents of Zig often argue that the language is a revolutionary low-level language that represents a massive shift in how you think about programming. They claim it’s the best C replacement language. This is high praise indeed, so let’s see if it can actually live up to the hyperbole.</p>

<h2 id="philosophy">Philosophy</h2>

<p>Its core philosophy emphasises explicit control: the language motto is “No hidden control flow”. In practice, this means all memory allocation, error handling, and control-flow constructs must be spelled out by the programmer.</p>

<p>Kelley has stressed that the language is for creating “optimal” solutions at the cost of some programmer inconvenience. Correctness and robustness are equally held as important goals for Zig code.</p>

<p>Critics argue that Zig adding undefined behaviour compared to C for additional possible compiler optimisation benefits runs counter to these last two goals, although the Zig documentation claims they are not in opposition:</p>

<blockquote>
  <p><em>“Zig uses undefined behavior as a razor sharp tool for both bug prevention and performance enhancement.”</em> <a href="https://ziglang.org/learn/overview/">link</a></p>
</blockquote>

<p>At the same time, even Zig’s docs acknowledge that <code class="language-plaintext highlighter-rouge">ReleaseFast</code> and <code class="language-plaintext highlighter-rouge">ReleaseSmall</code> have no protection against UB that isn’t detected when testing with <code class="language-plaintext highlighter-rouge">Debug</code> or <code class="language-plaintext highlighter-rouge">ReleaseSafe</code>.*</p>

<p>From this, we can conclude that the Zig approach is to assume that all undefined behaviour will be caught while testing in safe mode, and the application can then safely be run without checks and with full performance in production.</p>

<p>This is different from most other languages that either retain many checks, or at least try to retain well-defined behaviour as much as possible.</p>

<p><strong>* EDIT: On the Zig discord there was some resistance to this saying <code class="language-plaintext highlighter-rouge">ReleaseSafe</code> was a good alternative to be used in production to ensure safety. However, since this has a non-neglible overhead, trading safety for a potential large cost in performance I think my point stands. It is not comparable to languages like Rust in this respect.</strong></p>

<h2 id="doubtful-claims-of-excellence">Doubtful claims of excellence</h2>

<p>Zig made an early splash with the claim that “Zig is faster than C” in early talks by Kelley. This turned out not to be quite true. The benchmarks turned out to be meaningless, as the Zig code had been compiled to the native architecture, while the C code was compiled for a generic CPU. When this discrepancy was removed, they compiled to the same code.*</p>

<p>The docs still talk about Zig being faster than C, and in the community, many assume it’s true, even though the docs do mention “native arch” by default:</p>

<blockquote>
  <p><em>“For native targets, advanced CPU features are enabled (-march=native), thanks to the fact that Cross-compiling is a first-class use case.”</em> <a href="https://ziglang.org/learn/overview/">link</a></p>
</blockquote>

<p>Here I am not sure why “cross-compiling” being a first-class use case has to do with compiling for the native CPU. In practice, accidentally compiling for native architecture and then being unable to share the executable happens to many beginners.</p>

<p>The other big claim, which caused conflict with the Rust community, is the claim of safety without trade-offs, or as the Zig docs say: <em>“Performance and Safety: Choose Two”</em>.</p>

<p>As we already saw, Zig doesn’t really acknowledge that there is a problem running with UB in production as long as it has been tested with debug mode on. This should rightly be criticised. Anyone with experience of projects that have gone through lengthy testing can testify: bugs still happen in production. Even in full debug mode, Zig only checks UB and not overall correctness – for that, something like contracts are needed, and Zig does not have them.</p>

<p>Another claim was Zig’s “colourblind” async, which “solved the async colouring problem”. However, it was later removed from the language, and it’s currently waiting for problems with the implementation and semantics to be worked out.</p>

<p>Async is interesting in other ways, as it seems to grossly violate the “explicitness” principle of Zig, but was nonetheless added.</p>

<p><strong>* EDIT: According to Zig users this should more be taken to mean that it’s easier to write code that is fast when using Zig than when using C or C++. This is a quite different from what the documentation and what the early talks by Andrew Kelley is claiming. I think I have to stand by my criticism here.</strong></p>

<h2 id="first-impressions">First impressions</h2>

<p>The first Hello World looks like this:</p>

<div class="language-zig highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">const</span> <span class="n">std</span> <span class="o">=</span> <span class="nb">@import</span><span class="p">(</span><span class="s">"std"</span><span class="p">);</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="n">main</span><span class="p">()</span> <span class="k">void</span> <span class="p">{</span>
    <span class="n">std</span><span class="p">.</span><span class="py">debug</span><span class="p">.</span><span class="nf">print</span><span class="p">(</span><span class="s">"Hello, world!</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="o">.</span><span class="p">{});</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We start by defining the constant <code class="language-plaintext highlighter-rouge">std</code>, which gives us an alias to the struct/module (Zig doesn’t differentiate between the two) containing the standard library.</p>

<p>Second, we print using the standard “debug” print, and Zig’s explicitness is already on full display: it uses full paths as recommended – although we could possibly have aliased it somewhat further – and there is that little .{} at the end.</p>

<p>This <code class="language-plaintext highlighter-rouge">.{}</code> is because Zig lacks vaargs or default arguments, so <code class="language-plaintext highlighter-rouge">.{}</code>, which is an empty anonymous struct in Zig, has to be passed even though we don’t really have any argument to format.</p>

<p>Already here we see a vast rift between Zig and Odin, which we previously looked at. In fact, at this point, it is similarly verbose to the infamous Java hello world of <code class="language-plaintext highlighter-rouge">System.out.println("Hello, World")</code>.</p>

<p>If we want to do anything more complicated, we need to use a build.zig script*. There is a way to use Zig’s package manager, but to do that we must dig deep into how Zig build and “zon” files work. Once this is done (in my particular case, I copied a project that already had a build.zig with related files for raylib development).</p>

<p>After a lot of fiddling and consulting the docs (as the errors are not helpful), you might discover that this is the</p>
<div class="language-zig highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="n">pos</span><span class="p">:</span> <span class="n">ray</span><span class="p">.</span><span class="py">Vector2</span> <span class="o">=</span> <span class="o">.</span><span class="p">{</span> <span class="p">.</span><span class="py">x</span> <span class="o">=</span> <span class="mi">640</span><span class="p">,</span> <span class="p">.</span><span class="py">y</span> <span class="o">=</span> <span class="mi">320</span> <span class="p">};</span>
</code></pre></div></div>
<p>In case someone wonders: no, <code class="language-plaintext highlighter-rouge">var pos: ray.Vector2 = .{ 640, 320 };</code> doesn’t work.</p>

<p>Of course, if you don’t use <code class="language-plaintext highlighter-rouge">pos</code>, the compiler will say that this is an error and refuse to compile as well. And if you discard that error (typically using <code class="language-plaintext highlighter-rouge">_ = pos;</code>), the compiler will complain that it should be a const.</p>

<p>Not warnings here, compilation errors.</p>

<p>Converting the example from the Odin article, we get this:</p>

<div class="language-zig highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">const</span> <span class="n">std</span> <span class="o">=</span> <span class="nb">@import</span><span class="p">(</span><span class="s">"std"</span><span class="p">);</span>
<span class="k">const</span> <span class="n">rl</span> <span class="o">=</span> <span class="nb">@import</span><span class="p">(</span><span class="s">"raylib.zig"</span><span class="p">).</span><span class="py">raylib</span><span class="p">;</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="n">main</span><span class="p">()</span> <span class="k">void</span> <span class="p">{</span>
    <span class="n">rl</span><span class="p">.</span><span class="nf">InitWindow</span><span class="p">(</span><span class="mi">1280</span><span class="p">,</span> <span class="mi">768</span><span class="p">,</span> <span class="s">"Testing"</span><span class="p">);</span>
    <span class="k">var</span> <span class="n">pos</span><span class="p">:</span> <span class="n">rl</span><span class="p">.</span><span class="py">Vector2</span> <span class="o">=</span> <span class="o">.</span><span class="p">{</span> <span class="p">.</span><span class="py">x</span> <span class="o">=</span> <span class="mi">640</span><span class="p">,</span> <span class="p">.</span><span class="py">y</span> <span class="o">=</span> <span class="mi">320</span> <span class="p">};</span>
    <span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">rl</span><span class="p">.</span><span class="nf">WindowShouldClose</span><span class="p">())</span> <span class="p">{</span>
        <span class="n">rl</span><span class="p">.</span><span class="nf">BeginDrawing</span><span class="p">();</span>
        <span class="n">rl</span><span class="p">.</span><span class="nf">ClearBackground</span><span class="p">(</span><span class="n">rl</span><span class="p">.</span><span class="py">BLUE</span><span class="p">);</span>
        <span class="n">rl</span><span class="p">.</span><span class="nf">DrawRectangleV</span><span class="p">(</span><span class="n">pos</span><span class="p">,</span> <span class="o">.</span><span class="p">{.</span><span class="py">x</span> <span class="o">=</span> <span class="mi">32</span><span class="p">,</span> <span class="p">.</span><span class="py">y</span> <span class="o">=</span> <span class="mi">32</span><span class="p">},</span> <span class="n">rl</span><span class="p">.</span><span class="py">GREEN</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">rl</span><span class="p">.</span><span class="nf">IsKeyDown</span><span class="p">(</span><span class="n">rl</span><span class="p">.</span><span class="py">KEY_LEFT</span><span class="p">))</span> <span class="p">{</span>
           <span class="n">pos</span><span class="p">.</span><span class="py">x</span> <span class="o">-=</span> <span class="mi">400</span> <span class="o">*</span> <span class="n">rl</span><span class="p">.</span><span class="nf">GetFrameTime</span><span class="p">();</span>
        <span class="p">}</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">rl</span><span class="p">.</span><span class="nf">IsKeyDown</span><span class="p">(</span><span class="n">rl</span><span class="p">.</span><span class="py">KEY_RIGHT</span><span class="p">))</span> <span class="p">{</span>
           <span class="n">pos</span><span class="p">.</span><span class="py">x</span> <span class="o">+=</span> <span class="mi">400</span> <span class="o">*</span> <span class="n">rl</span><span class="p">.</span><span class="nf">GetFrameTime</span><span class="p">();</span>
        <span class="p">}</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">rl</span><span class="p">.</span><span class="nf">IsKeyDown</span><span class="p">(</span><span class="n">rl</span><span class="p">.</span><span class="py">KEY_UP</span><span class="p">))</span> <span class="p">{</span>
           <span class="n">pos</span><span class="p">.</span><span class="py">y</span> <span class="o">-=</span> <span class="mi">400</span> <span class="o">*</span> <span class="n">rl</span><span class="p">.</span><span class="nf">GetFrameTime</span><span class="p">();</span>
        <span class="p">}</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">rl</span><span class="p">.</span><span class="nf">IsKeyDown</span><span class="p">(</span><span class="n">rl</span><span class="p">.</span><span class="py">KEY_DOWN</span><span class="p">))</span> <span class="p">{</span>
           <span class="n">pos</span><span class="p">.</span><span class="py">y</span> <span class="o">+=</span> <span class="mi">400</span> <span class="o">*</span> <span class="n">rl</span><span class="p">.</span><span class="nf">GetFrameTime</span><span class="p">();</span>
        <span class="p">}</span>
        <span class="n">rl</span><span class="p">.</span><span class="nf">EndDrawing</span><span class="p">();</span>
    <span class="p">}</span>
    <span class="n">rl</span><span class="p">.</span><span class="nf">CloseWindow</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Zig can’t compete with Odin’s bundled “vendor”, of course, but the whole process of using build.zig is so much work up front trying to figure out how the Zig build system works – a daunting task if you’re still learning the language, whereas for Odin it “just works”.</p>

<p>The mandatory names in the initialisers (<code class="language-plaintext highlighter-rouge">.x</code> etc.) feel just superfluous and unergonomic for things like vectors. Despite looking more like C than Odin, Zig keeps having its own way of doing things, violating expectations.</p>

<p><strong>* EDIT: I asked about this on the Zig discord, and I was told the following: <em>“99.5% of zig code can be compiled by zig build-exe. In any case, zig init will generate a basic one that rarely needs to be changed. Build.zig is easier to learn if you know zig than cmake if you know zig, because it’s already zig code.”</em> However, my point is that as a beginner one might not know enough Zig to edit the <code class="language-plaintext highlighter-rouge">build.zig</code> in the first place, so this to me is placing the cart before the horse.</strong></p>

<h2 id="error-handling">Error handling</h2>

<p>Zig’s error handling is fairly novel: it returns a kind of “Result” type that needs to be immediately handled. Zig errors can also optionally be implicitly returned or invoke a panic:</p>

<div class="language-zig highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">const</span> <span class="n">std</span> <span class="o">=</span> <span class="nb">@import</span><span class="p">(</span><span class="s">"std"</span><span class="p">);</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="n">main</span><span class="p">()</span> <span class="o">!</span><span class="k">void</span> <span class="p">{</span>
    <span class="k">const</span> <span class="n">file</span> <span class="o">=</span> <span class="n">std</span><span class="p">.</span><span class="py">fs</span><span class="p">.</span><span class="nf">cwd</span><span class="p">().</span><span class="nf">openFile</span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span> <span class="o">.</span><span class="p">{})</span> <span class="k">catch</span> <span class="p">|</span><span class="n">err</span><span class="p">|</span> <span class="n">label</span><span class="p">:</span> <span class="p">{</span>
        <span class="n">std</span><span class="p">.</span><span class="py">debug</span><span class="p">.</span><span class="nf">print</span><span class="p">(</span><span class="s">"unable to open file: {}</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="o">.</span><span class="p">{</span><span class="n">err</span><span class="p">});</span>
        <span class="k">return</span><span class="p">;</span>
    <span class="p">};</span>
    <span class="c">// "try" rethrows the error, similar to Odin's "or_return"</span>
    <span class="c">// const file = try std.fs.cwd().openFile("foo.txt", .{});</span>
    <span class="c">// Alternatively panic:</span>
    <span class="c">// const file = std.fs.cwd().openFile("foo.txt", .{}) catch unreachable;</span>
    <span class="k">defer</span> <span class="n">file</span><span class="p">.</span><span class="nf">close</span><span class="p">();</span>
    <span class="k">try</span> <span class="n">file</span><span class="p">.</span><span class="nf">writeAll</span><span class="p">(</span><span class="s">"test"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Unlike exceptions or even Odin’s approach, there is no simple way to handle all errors in a single sweep, which means that usually Zig code will use try and handle all results at a higher level. We could continue diving into error handling, but for a supposedly simple language, Zig has a lot of new features that need covering.</p>

<h2 id="zig-comptime">Zig comptime</h2>

<p>According to many Zig proponents, the poster child for Zig’s versatility and simplicity is its compile-time execution. As we’ll see, Zig’s compile time is more of a cross between a toned-down Jai meta-programming and C++ templates.</p>

<p>Zig’s compile-time execution serves three main goals:</p>

<ol>
  <li>To enable polymorphic functions.</li>
  <li>To generate generic types.</li>
  <li>To conditionally compile code.</li>
</ol>

<p>The primary mechanism comes from Zig taking untyped or compile-time-only arguments. Compile-time-only arguments are things like types, which only have representation at compile-time. Untyped arguments allow compile-time “duck typing”.</p>

<p>Unfortunately, the “duck typing” suffers the same problem as C++ templates – that instantiating them creates an error that possibly is shown rather deep into the generic library code.</p>

<p>The generic types that Zig can create are more flexible than C++ templates, though, being able to implement things as SoA (struct of array) types at compile-time. The cost of this is that an IDE that wishes to provide checking on generic Zig types would need to execute the entire generic function in order to understand its actual layout. It is clear from this that, like Jai, Zig does not prioritise IDE friendliness.</p>

<p>This complexity is also something a human reader will have to deal with, and the comptime here is quite implicit in what it does. This implicitness is even worse when conditionally compiling code.</p>

<p>The following code will compile:</p>

<div class="language-zig highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">const</span> <span class="n">foo</span> <span class="p">:</span> <span class="k">bool</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">foo</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">const</span> <span class="n">x</span> <span class="p">:</span> <span class="kt">i32</span> <span class="o">=</span> <span class="mf">1.2</span><span class="p">;</span>
    <span class="mi">_</span> <span class="o">=</span> <span class="n">x</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>But setting <code class="language-plaintext highlighter-rouge">foo</code> to <code class="language-plaintext highlighter-rouge">true</code> instead will reveal a compile-time error as the implicit conversion from 1.2 to i32 isn’t allowed. Similarly, a function that isn’t called will not even be type-checked. This allows the somewhat humorous situation where you can write a function and marvel that it compiles in Zig, just to notice that you forgot to call it or declare it public and therefore it’s not actually checked.</p>

<p>While not checking conditionally evaluated code is quite reasonable, the highly implicit way Zig implements it is rather questionable, to say the least.</p>

<p>There is no reason why an ordinary if should lazily evaluate its branches. The language could have used a construct like inline if (which it uses for comptime versions of for loops) to make it explicit, but has chosen to make it more implicit.</p>

<p>While Zig’s comptime undoubtedly makes the language more powerful and unifies three different concepts – generics, compile-time evaluation, and polymorphism – it comes at a clear lack of clarity and explicitness.</p>

<p>It is unclear how this matches the language motto of “no hidden control flow”.</p>

<h2 id="verbosity">Verbosity</h2>

<p>Critics say that Zig is verbose. Is there some merit to this? We already mentioned the long paths (std.debug.print) in passing. Another commonly raised problem is the casts. Zig has tens of different cast operations as Zig “builtins” (special functions that provide functionality that otherwise would not be available) to specify exactly from what category we’re converting to and from (e.g. <code class="language-plaintext highlighter-rouge">@intFromFloat</code>), which causes math-heavy code to balloon.</p>

<p>One user submitted the following on Reddit:</p>

<div class="language-zig highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="n">fib</span><span class="p">(</span><span class="n">n</span><span class="p">:</span> <span class="kt">u8</span><span class="p">)</span> <span class="kt">u32</span> <span class="p">{</span>
    <span class="k">const</span> <span class="n">sqrt_5</span><span class="p">:</span> <span class="kt">f32</span> <span class="o">=</span> <span class="k">comptime</span> <span class="n">std</span><span class="p">.</span><span class="py">math</span><span class="p">.</span><span class="nf">sqrt</span><span class="p">(</span><span class="mf">5.0</span><span class="p">);</span>
    <span class="k">const</span> <span class="n">golden_ratio</span><span class="p">:</span> <span class="kt">f32</span> <span class="o">=</span> <span class="p">(</span><span class="mf">1.0</span> <span class="o">+</span> <span class="n">sqrt_5</span><span class="p">)</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">;</span>

    <span class="k">const</span> <span class="n">n_f32</span><span class="p">:</span> <span class="kt">f32</span> <span class="o">=</span> <span class="n">@floatFromInt</span><span class="p">(</span><span class="n">n</span><span class="p">);</span>
    <span class="k">const</span> <span class="n">result_f32</span> <span class="o">=</span> <span class="nb">@round</span><span class="p">(</span><span class="n">std</span><span class="p">.</span><span class="py">math</span><span class="p">.</span><span class="nf">pow</span><span class="p">(</span><span class="kt">f32</span><span class="p">,</span> <span class="n">golden_ratio</span><span class="p">,</span> <span class="n">n_f32</span><span class="p">)</span> <span class="o">/</span> <span class="n">sqrt_5</span><span class="p">);</span>

    <span class="k">return</span> <span class="n">@intFromFloat</span><span class="p">(</span><span class="n">result_f32</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Proponents argue that this makes code “honest” and predictable.</p>

<p>Another complaint has been the added noise due to the syntax changes from C, such as replacing <code class="language-plaintext highlighter-rouge">for</code> loops with Zig’s <code class="language-plaintext highlighter-rouge">while</code>:*</p>

<div class="language-zig highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="n">block_h</span><span class="p">:</span> <span class="kt">u32</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">while</span> <span class="p">(</span><span class="n">block_h</span> <span class="o">&lt;</span> <span class="n">block_cnt_h</span><span class="p">)</span> <span class="p">:</span> <span class="p">(</span><span class="n">block_h</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">var</span> <span class="n">block_w</span><span class="p">:</span> <span class="kt">u32</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">block_w</span> <span class="o">&lt;</span> <span class="n">block_cnt_w</span><span class="p">)</span> <span class="p">:</span> <span class="p">(</span><span class="n">block_w</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="c">// Loop body</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This translates to the following in C:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="n">block_h</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">block_h</span> <span class="o">&lt;</span> <span class="n">block_cnt_h</span><span class="p">;</span> <span class="n">block_h</span><span class="o">++</span> <span class="p">)</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="n">block_w</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">block_w</span> <span class="o">&lt;</span> <span class="n">block_cnt_w</span><span class="p">;</span> <span class="n">block_w</span><span class="o">++</span> <span class="p">)</span> <span class="p">{</span>
        <span class="c1">// Loop body</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Although the Zig community, on the whole, seems happy with Zig’s syntax despite this, it’s hard to know if this is confirmation bias (i.e. people who don’t like the syntax do not use Zig) or if it’s just something that grows on you.</p>

<p>A common refrain on forums is that Zig’s learning curve is steep: it has “strong opinions” about syntax and requires learning many new idioms. As one commenter on Hacker News warns, if a developer does not <em>“appreciate simplicity and control over tiny details,”</em> Zig <em>“will not bring anything particularly interesting”</em> <a href="https://news.ycombinator.com/item?id=35325556">link</a></p>

<p><strong>* EDIT: On the Zig discord I was told that Zig has a <code class="language-plaintext highlighter-rouge">for</code> loop. However, this is actually a foreach style <code class="language-plaintext highlighter-rouge">for</code> loop where you cannot modify the counter to make jumps (important when filtering lists for example), so I do not consider them equivalent</strong></p>

<h2 id="the-road-to-zig-10">The road to Zig 1.0?</h2>

<p>Another concern for Zig to tackle is the apparently ever-increasing scope of the project. Zig has soon been in development for ten years, with no clear end in sight. Because Zig is funded by donations, this is not the death knell it would be for a commercial project. However, it remains a long-term concern if the language cannot reach version 1.0. It should be noted that ten years is a fairly common timeframe for a language to reach 1.0, and Odin – about a year “younger” than Zig – is clearly much closer to the elusive milestone.</p>

<p>It is clearly not a lack of funding or contributors: Zig has enough of both. The problem appears more like classic feature creep. The Zig compiler has now been rewritten more than once*; it is also aiming to replace LLVM with its own backends, its own linker, and so on. The list seems endless.</p>

<p>Even if these components are not strictly required for 1.0, they all have to be coordinated, leaving less time for the language itself.</p>

<p><strong>* EDIT: This turned out to be a misconception on my part: the version of the compiler that mixed C++ and Zig was never considered a real rewrite, but only a stepping stone to the pure Zig version of the compiler. The new bootstrap process (the way the the Zig compiler in Zig compiles itself) is discussed <a href="https://ziglang.org/news/goodbye-cpp">here</a>, so Zig has only been fully rewritten once. I stand corrected.</strong></p>

<h2 id="zig-the-good-parts">Zig: the good parts</h2>

<p>After all this doom and gloom, let’s focus on the area where Zig is already king: cross-platform compilation.</p>

<p>Ironically, cross-platform compilation is already available out of the box with LLVM, but Clang – which builds on LLVM – makes it non-trivial to cross-compile from start to finish.</p>

<p>While solving this problem for Zig, the team realised that with their bundled Clang library, they could offer a better frontend to Clang, providing cross-compilation out of the box.</p>

<p>This has been enormously successful, with major companies adopting “zig cc” (Zig’s Clang frontend) as their preferred compiler for cross-compilation. This cements the Zig compiler as a worthwhile product – even though, ironically, it has little to do with the language itself.</p>

<p>With Zig now present in toolchains, there have been pushes to get companies using build.zig to script their builds, as a way of selling the language itself. While this has seen some success, it hasn’t led to official adoption by any major company.</p>

<p>While Zig users swear by build.zig’s elegance, it’s not universally loved.</p>

<h2 id="summary">Summary</h2>

<p>Zig aims to be a modern C replacement with a focus on explicit control, safety, and zero hidden behaviour – but it often trades usability for perceived ideological purity. Features like comptime offer powerful metaprogramming capabilities, but they make standalone tooling and IDE support significantly harder. While it is touted as being safer and faster than C without compromises, it’s doubtful that the faster than C claim holds up under close scrutiny. It’s also worth noting that Zig usually disables safety checks in release builds.</p>

<p>Despite nearly a decade of development, Zig 1.0 still feels far off. Ironically, its biggest success isn’t the language itself but its excellent cross-compilation tooling – so much so that many use zig cc as a drop-in Clang replacement without writing a single line of Zig.</p>

<p>But is this really the best alternative to C? Odin offers more high-level conveniences, while Zig is far more bare-bones. At times, it seems as though within the Zig community, “low-level” and “high performance” are conflated with “no abstractions”, as if something as thin and anæmic as the C standard library were a necessary condition for speed. In several ways, Zig is actually more painful to work with than the C it aims to replace. The argument that Zig is a great C alternative seems to have little to support it.</p>

<p>Aside from first mover advantage, there seems to be little to recommend Zig over Odin or other C alternatives, but as we all know, it’s not always the best alternative that wins.</p>

<hr />

<p>Discuss this on <a href="https://news.ycombinator.com/item?id=43942094">Hacker News</a> or on <a href="https://www.reddit.com/r/programming/comments/1kjigtz/zig_the_ideal_c_replacement_or/">r/Programming</a>.</p>

<p><strong>EDITED 2025-05-10 with feedback from the Zig Discord.</strong></p>

<p>The next post is on V <a href="https://bitshifters.cc/2025/05/17/vlang.html">here</a>.</p>]]></content><author><name>Bitshifter</name></author><summary type="html"><![CDATA[(Previous post was on Odin here]]></summary></entry><entry><title type="html">Jai, the game programming contender</title><link href="https://bitshifter-1.github.io/2025/04/28/jai.html" rel="alternate" type="text/html" title="Jai, the game programming contender" /><published>2025-04-28T00:00:00+00:00</published><updated>2025-04-28T00:00:00+00:00</updated><id>https://bitshifter-1.github.io/2025/04/28/jai</id><content type="html" xml:base="https://bitshifter-1.github.io/2025/04/28/jai.html"><![CDATA[<p>(Introduction <a href="/2025/04/20/after-cplusplus">here</a>)</p>

<p>In systems programming, C++ has ruled the roost since the late 90s. But for game developers who want tighter control, and modern workflow – and above all: <em>faster compile times</em>, it’s <em>far</em> from perfect.</p>

<p>This is where <strong>Jai</strong> steps in, a language created by <em>Jonathan Blow</em> — best known for his games <em>Braid</em> and <em>The Witness</em> (actually, Blow has never officially called the language ‘Jai’ - the name comes from the apparent name of the compiler executable in YT demos).</p>

<p>Blow set out to design Jai as a purpose-built alternative to C++, tailored specifically for the kind of high-performance code that games demand. His pitch? “We can do better” - modern features, more powerful meta programming and extensibility with lightning-fast compile times. Jai is meant to give total control of the machine with most of the latest contemporary language features.</p>

<p>Blow started work on Jai around 2014, and although the language isn’t publicly available yet, it’s already made waves — spawning what some call the <em>“low-level programming renaissance”</em>. Languages like <strong>Odin</strong> and <strong>Zig</strong> can trace parts of their DNA directly back to Jai’s early ideas.</p>

<h2 id="jais-influence-zig-odin-and-the-new-wave">Jai’s Influence: Zig, Odin, and the new wave</h2>

<p>It’s hard to overstate how much Jai has quietly shaped the new generation of low-level languages. <strong>Ginger Bill</strong>, the creator of Odin, openly credits Jai, saying he borrowed <em>“quite a lot of ideas”</em>. Features like <strong>compile-time code execution</strong>, <strong>type introspection</strong>, and a deep focus on <strong>data-oriented design</strong> are hallmarks of all three languages — but Jai pioneered the model.</p>

<p>Similarly, <strong>Zig</strong>’s focus on manual memory management, tight control over the binary layout, and the now-famous <code class="language-plaintext highlighter-rouge">comptime</code> execution system feel very much like a sibling to Jai’s concepts. But it’s worth noting: Zig limits what can happen at compile time for safety reasons, while Jai — true to Blow’s style — hands you the big red button and lets you press it. One developer quipped that Zig’s compile-time features feel like <em>“child’s play”</em> compared to Jai’s open-ended system.</p>

<p>In Jai, not only can you run arbitrary code during compilation (including, famously from his first demo, <strong>running a game</strong> at compile time), but you can script and tweak your entire build process inside your codebase. No Makefiles or external build scripts needed. Compared to that, Zig’s comptime and build.zig feels cautious and overly nerfed.</p>

<h2 id="what-makes-jai-stand-out">What makes Jai stand out?</h2>

<p>Jai isn’t trying to reinvent programming so much as <em>streamline</em> it. Many of its features feel like common sense — if you’re writing big, performance-sensitive codebases:</p>

<ul>
  <li><strong>Blazing-Fast Compilation</strong>: Blow’s target is compiling a million lines of code in under a second.</li>
  <li><strong>First-Class Metaprogramming</strong>: Any function can be tagged to run at compile time. There is no special language and no separate macro files.</li>
  <li><strong>Manual Memory Control</strong>: This is perhaps obvious. However, Jai also has a “context” allowing programs to do scoped overrides of the default heap allocator. (Odin also has a “context” – clearly inspired by Jai, but it is not as heavily emphasized as in Jai)</li>
  <li><strong>Built-in Reflection</strong>: Jai can inspect its own types natively at runtime and compile-time.</li>
  <li><strong>Data-Oriented Defaults</strong>: Structures-of-Arrays layouts, polymorphic procedures, and easy slicing — all geared for cache-friendly, high-performance code.</li>
  <li><strong>Rich Syntax for Control</strong>: Operator overloading, <code class="language-plaintext highlighter-rouge">defer</code>, <code class="language-plaintext highlighter-rouge">#asm</code> blocks, and more — with an emphasis on “control first, magic later.”</li>
</ul>

<p>On top of all that, Blow’s design philosophy shows through: he wanted a tool that feels like C, but sheds C’s worst baggage. No silent errors, no complex build chains, no mysterious behaviors you can’t debug.</p>

<h2 id="what-user-like-about-jai">What user like about Jai</h2>

<p>Despite not being publicly available yet, Jai has built a dedicated fanbase and a solid set of beta testers. Here’s what people generally seem to love about it:</p>

<ul>
  <li><strong>Speed</strong>: Both in compilation and execution. No more coffee breaks between builds.</li>
  <li><strong>Productivity Gains</strong>: Less boilerplate means fewer bugs and faster iteration. Blow estimates about a 15% productivity increase — and he expects that number to grow over time.</li>
  <li><strong>Powerful Compile-Time Tools</strong>: Jai’s ability to run arbitrary code at compile time blows traditional macros (and even Zig’s comptime) out of the water.</li>
  <li><strong>Clarity and Simplicity</strong>: Even complex systems programming feels straightforward compared to C++’s jungle of templates, inheritance, and fragile build systems.</li>
  <li><strong>Better Debugging</strong>: Debug symbols, no name mangling, and runtime checks make debugging far more pleasant.</li>
</ul>

<p>As one early user put it, Jai feels <em>“tremendously simpler than C++, and even simpler than C”</em> — but without giving up the power that serious systems work demands.</p>

<h2 id="the-bad-parts">The bad parts</h2>

<p>Not everyone loves Jai. Common criticisms come up again and again:</p>

<ul>
  <li><strong>Closed Development</strong>: Right now, Jai is not open-source. It’s invite-only, with no firm public release date. Many developers simply won’t invest time into a language they can’t use freely.</li>
  <li><strong>Growing Complexity</strong>: Even Blow has admitted that Jai is “more complex than he intended”. New features keep creeping in, and some fear it is getting harder to master.</li>
  <li><strong>Changing Features</strong>: Some ideas have been scrapped (like automatic structure layout flipping), and others are still experimental.</li>
  <li><strong>Thin Ecosystem</strong>: With no public release, there are no big libraries, no third-party tooling, and not many community-built resources yet.</li>
  <li><strong>Potential Security Risks</strong>: Jai’s wide-open compile-time execution could open the door to more side-effects or vulnerabilities if not used carefully.</li>
</ul>

<p>Blow has acknowledged some of these trade-offs openly. On the matter of complexity, he notes it’s a constant balancing act: <em>“You want the language to stay simple, but sometimes you need the complexity to get real gains.”</em></p>

<p>Jonathan Blow has a fairly divisive public persona, that seem to also polarize the community’s response to his language. Even so, one might note that very little criticism has been directed at the language’s semantics and syntax, which seems generally well received (as well as frequently copied).</p>

<h2 id="the-closed-source-debate">The “Closed Source” Debate</h2>

<p>One of the hottest topics around Jai is its closed status. Blow has been clear: Jai won’t be open-sourced until it’s truly ready. This approach allows his team to stay nimble and uncompromising, but it also alienates many developers who expect open-source access by default nowadays.</p>

<p>Some in the community understand Blow’s logic — one developer noted that keeping Jai closed lets it stay <em>“as opinionated as it needs to be”</em>. Others are less forgiving, calling it <em>“disappointing”</em> that such an exciting language isn’t available for contribution or even serious testing.</p>

<p>Either way, the closed nature of Jai is a real roadblock for wider adoption, so we can’t really see the true potential of the language yet.</p>

<h2 id="what-happens-when-jai-finally-drops">What Happens When Jai Finally Drops?</h2>

<p>It’s interesting to speculate: when Jai eventually releases publicly, what happens to languages like Zig and Odin?</p>

<p>Zig, which has spent years slowly gaining traction, might suddenly have serious competition. One user warned that Zig would need to “solidify” its user base fast, because Jai could <em>“steal the momentum”</em>. Jai’s more ambitious metaprogramming, faster compile speeds, and deeper focus on game development might appeal to the very developers Zig is currently winning over.</p>

<p>On the other hand, Zig and Odin aren’t standing still either. And it’s possible that Jai’s release — whenever it happens — will benefit the whole low-level ecosystem by bringing more attention (and pressure) to raise the bar for all C++ successors.</p>

<h2 id="summary">Summary</h2>

<p>Jai is a very interesting competitor to C++ and has good chances to be embraced by at least the game programming community.</p>

<p>Even if it isn’t widely adopted after its release, it has already had a huge impact — igniting a trend of building low-level programming languages, and spawning the rapidly growing Zig and the well-regarded Odin.</p>

<hr />]]></content><author><name>Bitshifter</name></author><summary type="html"><![CDATA[(Introduction here)]]></summary></entry><entry><title type="html">After C/C++</title><link href="https://bitshifter-1.github.io/2025/04/20/after-cplusplus.html" rel="alternate" type="text/html" title="After C/C++" /><published>2025-04-20T00:00:00+00:00</published><updated>2025-04-20T00:00:00+00:00</updated><id>https://bitshifter-1.github.io/2025/04/20/after-cplusplus</id><content type="html" xml:base="https://bitshifter-1.github.io/2025/04/20/after-cplusplus.html"><![CDATA[<p>Since the 1980s, there have been innumerable attempts to supplant or augment C.</p>

<p>The greatest success, of course, has been C++ (originally “C with Classes”).</p>

<p>Did you know that there was a fair bit of research to improve or replace the <a href="https://en.wikipedia.org/wiki/C_preprocessor">C preprocessor</a>? Even <a href="https://en.wikipedia.org/wiki/James_Gosling">James Gosling</a>, the designer of Java, wrote <a href="https://swtch.com/gosling89ace.pdf">one</a>.</p>

<p><a href="https://en.wikipedia.org/wiki/Objective-C">Objective-C</a> was less successful than C++, but it nonetheless powered MacOS X and later iOS. Now largely supplanted by Swift, it still enjoyed its fair share of glory days.</p>

<p>As C++ grew, so did the discontent. Alternatives with garbage collection 
replaced the less low-level uses of C++, but in domains where C once 
held sway – such as game development — C++ had few rivals.</p>

<p>However, as the need for low-level safety mounted, Rust emerged to answer the call. Yet such safety is often overkill in non-critical areas like gaming, 
and in 2014 Jonathan Blow made quite the splash with his announcement of an <a href="https://www.youtube.com/watch?v=TH9VCN6UkyQ">untitled programming language for game development</a> – later known as Jai.</p>

<p>This stirred up renewed interest in low-level languages, which led to the rise of indie languages like Zig and Odin.</p>

<p>So we end up with a timeline so far looking like C -&gt; C++ -&gt; Rust.</p>

<p>But what will happen now? Rust is ascendant, being fairly well known
– but what of the later alternatives such as Jai? Do they really
offer anything beyond what C/C++/Rust offers?</p>

<p>This is what I want to explore in later blog posts.</p>

<p>The story so far:</p>

<h3 id="language-overviews">Language overviews</h3>

<ol>
  <li><a href="/2025/04/28/jai.html">Jai, the game programming contender</a></li>
  <li><a href="/2025/05/04/odin.html">Odin, A Pragmatic C Alternative with a Go Flavour</a></li>
  <li><a href="/2025/05/04/zig.html">Zig: A New Direction for Low-Level Programming?</a></li>
  <li><a href="/2025/05/12/the-rest.html">Next up: C3, Hare and V</a></li>
  <li><a href="/2025/05/17/vlang.html">Can V Deliver on Its Promises?</a></li>
  <li><a href="https://bitshifters.cc/2025/05/22/c3-c-tradition.html">Is C3 the Underdog That Will Overtake Zig and Odin?</a></li>
</ol>

<h3 id="language-comparisons">Language comparisons</h3>

<p>TBD</p>]]></content><author><name>Bitshifter</name></author><summary type="html"><![CDATA[Since the 1980s, there have been innumerable attempts to supplant or augment C.]]></summary></entry><entry><title type="html">Welcome to the Blog</title><link href="https://bitshifter-1.github.io/2025/04/19/welcome-to-my-blag.html" rel="alternate" type="text/html" title="Welcome to the Blog" /><published>2025-04-19T00:00:00+00:00</published><updated>2025-04-19T00:00:00+00:00</updated><id>https://bitshifter-1.github.io/2025/04/19/welcome-to-my-blag</id><content type="html" xml:base="https://bitshifter-1.github.io/2025/04/19/welcome-to-my-blag.html"><![CDATA[<p>Alrighty.</p>

<p>Welcome to my dumping ground of thoughts, where I’ll be sharing my musings on programming, software design, and all things tech.</p>

<p>Privacy matters, and my opinions may occasionally tick off a prospective (or current) client. I’d rather not give my bosses any headaches over something I wrote here. 
Especially since I work in a business related to programming language tooling. Trust me, people can get real sensitive when you start criticizing their “darlings.”</p>

<p>Feel free to browse, and I hope you find something useful here!</p>]]></content><author><name>Bitshifter</name></author><summary type="html"><![CDATA[Alrighty.]]></summary></entry></feed>