<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dayvster 🌊</title>
    <description>The latest articles on DEV Community by Dayvster 🌊 (@dayvster).</description>
    <link>https://dev.to/dayvster</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F274998%2F09f57f7f-daec-4303-8766-138cfd1396d9.png</url>
      <title>DEV Community: Dayvster 🌊</title>
      <link>https://dev.to/dayvster</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dayvster"/>
    <language>en</language>
    <item>
      <title>Why Bun leaving Zig is Great for Zig</title>
      <dc:creator>Dayvster 🌊</dc:creator>
      <pubDate>Mon, 25 May 2026 21:12:47 +0000</pubDate>
      <link>https://dev.to/dayvster/why-bun-leaving-zig-is-great-for-zig-hl2</link>
      <guid>https://dev.to/dayvster/why-bun-leaving-zig-is-great-for-zig-hl2</guid>
      <description>&lt;p&gt;The tech internet is reacting exactly how you would expect to the news that Bun has merged a near-total rewrite from Zig into Rust. Most of the Rust crowd is out in full force celebrating it as another big win for their side. They are already writing off Zig as some niche language that is not built for the future.&lt;/p&gt;

&lt;p&gt;They are missing the point.&lt;/p&gt;

&lt;p&gt;This is not cope. This is not some Zig fanboy rant. It is a realistic look at a very risky engineering decision with real consequences for the people who actually have to ship and maintain this thing.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Gamble
&lt;/h3&gt;

&lt;p&gt;Anthropic bought Bun in December 2025. The exact amount they paid is still unknown. Speculation ranges from a lowball one million dollars all the way up to around one hundred million. We do know Bun had raised roughly twenty six million before the acquisition. That is a serious amount of money for a JavaScript runtime, but it is basically nothing to a company valued at three hundred and eighty billion dollars.&lt;/p&gt;

&lt;p&gt;Even if they paid the full one hundred million, it would be like you or me walking around with one thousand dollars in our bank account and buying a pack of gum or a cup of coffee. Not exactly a dangerous bet for Anthropic.&lt;/p&gt;

&lt;p&gt;The online crowd is acting like this rewrite is already a guaranteed success just because they switched to Rust. In reality this is a high-stakes gamble, mostly for the Bun team. They are the ones whose reputation is on the line if things go wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  Four Possible Outcomes
&lt;/h3&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Scenario A: It Actually Works&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The translated Rust code ends up fast enough and mostly stable. Anthropic gets to show it off as proof that their AI can rewrite large systems code. Bun ends up with the same product they already had, just written in a different language.&lt;/p&gt;

&lt;p&gt;Even in this best case there is a catch. Early checks on the Rust port showed over thirteen thousand unsafe blocks because the AI just carried over the old Zig pointer style. The Rust community might end up doing a bunch of unpaid cleanup work on the messy generated code. So even if it ships, it could still be a weird Frankenstein project under the hood.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Scenario B: Flaky and Unreliable&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The runtime mostly runs but has weird regressions, random panics, and edge cases that tests did not catch. Anthropic will probably downplay it and call it progress. For the Bun team this would be painful. A lot of people picked Bun specifically because it promised incredible performance. If that trust breaks, many will quietly switch back to Node or Deno and never look back.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Scenario C: Total Disaster&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The new codebase has serious bugs that break production apps for users. Upgrades become a nightmare. Anthropic takes a small hit to their image but keeps running their main business without much trouble. How ever bun as a product might no longer really exists after such an event. Once trust is gone in the JavaScript runtime space it is very hard to get it back. The project could slowly fade into irrelevance.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Scenario D: It Works But Becomes Maintenance Hell&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This is the one a lot of people are not talking about. The code compiles, passes the tests, and mostly works in production. But the generated Rust is ugly, hard to read, and full of weird patterns that no human would write. Over time it becomes much more painful to maintain and extend than the old Zig version ever was.&lt;/p&gt;

&lt;p&gt;New features take longer, bugs are harder to track down, and the original Bun team ends up spending most of their time fighting the codebase instead of improving the runtime. Users get slower progress and the project loses momentum even if nothing catastrophic happens. This is probably the most likely middle-ground outcome.&lt;/p&gt;

&lt;h3&gt;
  
  
  Outcome Breakdown
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Anthropic Result&lt;/th&gt;
&lt;th&gt;Bun Team Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Clean Success&lt;/td&gt;
&lt;td&gt;Big Win&lt;/td&gt;
&lt;td&gt;Break-even&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flaky and Unreliable&lt;/td&gt;
&lt;td&gt;Small Win&lt;/td&gt;
&lt;td&gt;Loss&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total Disaster&lt;/td&gt;
&lt;td&gt;No Real Damage&lt;/td&gt;
&lt;td&gt;Horrible Loss&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Works But Maintenance Hell&lt;/td&gt;
&lt;td&gt;Minor Win&lt;/td&gt;
&lt;td&gt;Slow Pain / Stagnation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  What Did Zig Actually Lose?
&lt;/h3&gt;

&lt;p&gt;Pretty much nothing important.&lt;/p&gt;

&lt;p&gt;They lost a corporate sponsor check and a team that never really fit the Zig way of doing things.&lt;/p&gt;

&lt;p&gt;Zig is about taking it easy, moving carefully, and doing the work right. It is built for patient craftsmanship. The Bun team was running on a completely different mindset. They operated in full hype mode, rushing everything out to feed the JavaScript ecosystem’s demand for speed.&lt;/p&gt;

&lt;p&gt;That constant rush led to a lot of breakage. Instead of owning it, they blamed Zig. They pointed at the pre-1.0 compiler and breaking changes as the reason their runtime kept feeling unstable. They even forked the language and tried forcing lifetimes and heavy AI patches into it, trying to turn Zig into something it was never meant to be.&lt;/p&gt;

&lt;p&gt;Ditching that mismatch is probably a relief for the Zig project.&lt;/p&gt;

&lt;p&gt;But the real gift is what comes next. Anthropic just paid for the cleanest before-and-after comparison the Zig community could ever ask for. We had Bun running on Zig for years. That was the before. Now we have the exact same codebase, same architecture, and same team running on a machine-translated Rust version. That is the after.&lt;/p&gt;

&lt;p&gt;No more excuses. No more arguing about pre-1.0 volatility or breaking changes. All the variables are removed. The Zig community now gets to watch what happens in plain sight.&lt;/p&gt;

&lt;p&gt;If Bun suddenly becomes rock solid and reliable, then maybe Zig really was holding it back. But if the flakiness, regressions, and instability follow it into Rust anyway, that will be very telling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zig didn’t lose its flagship project. It let a multi-billion-dollar company pay for the crash test.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>zig</category>
    </item>
    <item>
      <title>In Defense of Manual Memory Management</title>
      <dc:creator>Dayvster 🌊</dc:creator>
      <pubDate>Mon, 25 May 2026 21:11:01 +0000</pubDate>
      <link>https://dev.to/dayvster/in-defense-of-manual-memory-management-3jpo</link>
      <guid>https://dev.to/dayvster/in-defense-of-manual-memory-management-3jpo</guid>
      <description>&lt;h2&gt;
  
  
  Why Manual Memory Management Still Matters ?
&lt;/h2&gt;

&lt;p&gt;It is now easier than ever to write software without ever needing to learn about the heap or the stack. Languages like JavaScript, Python and even Go have built-in ways to manage your memory usage for you at runtime with acceptable performance hits for most modern consumer grade hardware. However consumer hardware is not the end all be all of computing. There are still many use cases where the performance benefits of manual memory management are worth the effort, such as in embedded systems, game development, and high-performance computing. &lt;/p&gt;

&lt;p&gt;I'd even argue that it's a valuable skill to have and learn even if you don't need it for your day to day work. It can give you a deeper understanding of how computers work and how to optimize your code for performance.&lt;/p&gt;

&lt;p&gt;When you understand how memory management works how your system works under the hood, you will also as a third order effect understand your own code and software better. Plus you can then use super cool and trendy languages like Zig, Odin and C3 which have manual memory management as a core feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Usual Pitfalls
&lt;/h2&gt;

&lt;p&gt;Here are some things that you should be aware of when doing manual memory management:&lt;/p&gt;

&lt;h3&gt;
  
  
  Dangling Pointers
&lt;/h3&gt;

&lt;p&gt;A dangling pointer is a pointer that points to memory that has already been deallocated. Imagine lending your friend a book and making a note of it and when you go to ask for it back they tell you that they've already thrown it away, rude yes, but now you have a note that points to a book that no longer exists. Bad analogy aside a dangling pointer is a pointer that points to memory that has already been freed, and if you try to access it you will get undefined behavior, which can lead to crashes, data corruption, and security vulnerabilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Double Free
&lt;/h3&gt;

&lt;p&gt;Continuing my bad analogy say that you've lent your friend a book and now you wish to throw it away, so first you have to get it back from your friend, who has rudely already thrown it away, even though you wanted to throw it away yourself. So you attempt to throw away a book that has already been thrown away. Wow I'm really bad at this whole analogy thing. Regardless, a double free is when you try to free memory that has already been freed, and this can lead to undefined behavior, which can lead to crashes, data corruption, and security vulnerabilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use After Free
&lt;/h3&gt;

&lt;p&gt;You know what I'm sticking to my bad analogy, so say that you've lent your friend a book and then you attempt to read it after your friend has already thrown it away, you will get a book that is no longer there and you will be confused and upset. This is what happens when you try to access memory that has already been freed, you will get undefined behavior, which can lead to... yes you've guessed it, crashes, data corruption, and security vulnerabilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Leaking Memory
&lt;/h3&gt;

&lt;p&gt;Your friend lives in a small apartment and you lend them a book, then you lend them 10 more books and after that you just keep lending them books like the crazed book lender that you are without ever asking for them back. Eventually your friend will have a huge pile of books that they can't manage(that'll show em) and they will have no space left in their apartment. This is what happens when you allocate memory but never free it, you will eventually run out of memory and your application will crash. This is called a memory leak, and it can be a serious problem if not managed properly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Buffer Overflows
&lt;/h3&gt;

&lt;p&gt;Your friend is a bit OCD they reserved a singular bookshelf for the books you lend them, it only has space for about 10 books because your very OCD friend likes the number 10, so you lend them 10 books one week and the next week you lend them 1 more book. Your friend has a meltdown because they have 11 books and can't fit them on their shelf that can only ever hold 10 books. Yes I am sticking to my bad analogy. A buffer overflow is essentially when you try to write more data to a buffer than it can hold. Guess what this can lead to? Yes you guessed it, crashes, data corruption, and security vulnerabilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory Fragmentation
&lt;/h3&gt;

&lt;p&gt;Your very OCD friend built a bigger shelf and has a new strategy of how to place books on that shelf, they wanna sort them alphabetically or by color or something I don't know it's an OCD thing. So you keep lending them book chaotically which leads your friend to have a bunch of empty space between the books on the shelf, the shelf itself is bigger yes, but the empty space between the books is wasted space that can't be used for anything else. This is what happens when you have a lot of small allocations and deallocations, it can lead to memory fragmentation, which can lead to inefficient use of memory and eventually running out of memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Incorrect Ownership
&lt;/h3&gt;

&lt;p&gt;Ok I got no idea how to work my bad analogy into this one, you and your ... roommates ? all try to modify the same book on the shelf at the same time, this opens a rift in the space time continuum because somehow you've now produced a quantum book that exists in multiple states at the same time, and you have no idea which state is the correct one. Wow that went off the rails real quick. Incorrect ownership is basically when you have multiple pointers to the same memory and each of them tries modifying it. This can lead to race conditions and data corruption, and it can be very difficult to debug.&lt;/p&gt;




&lt;p&gt;As you can see doing manual memory management is a lot more involved than just using a garbage collected language, but it can also be a lot more rewarding and fun. You get to have more control over your application's memory usage and you can really optimize your software for performance and for known hardware. However, you also have to be very careful and pay attention to the details, because if you make a mistake it can lead to some very bad consequences.&lt;/p&gt;

&lt;h2&gt;
  
  
  How To Avoid These Pitfalls
&lt;/h2&gt;

&lt;p&gt;Just use a garbage collected language, problem solved. End of article. Just kidding, if you want to do manual memory management there are some things you can do to avoid these pitfalls, you've already solved the biggest problem by now which is understanding the pitfalls (I hope). But just understanding the problem is only half the journey the solution is the other half. So let's think about some solutions to these problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Mental Model
&lt;/h3&gt;

&lt;p&gt;The first thing you need to do is to have a mental model of how memory management works. Let's start with 2 terms which immediately describe what they do, the &lt;strong&gt;stack&lt;/strong&gt; and the &lt;strong&gt;heap&lt;/strong&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;p&gt;For a stack we always default to a stack of plates when describing it because it's the easiest way to visualize it, you can only add or remove plates from the top of the stack because we really do not wanna break any plates. So the stack is a region of memory that is used for storing local variables and function call information. It is organized in a last-in-first-out (LIFO) manner, which means that the most recently added item is the first one to be removed. The stack is typically much faster than the heap because it has a fixed size and does not require dynamic memory allocation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Heap
&lt;/h2&gt;

&lt;p&gt;For the heap imagine a big disorganized pile of clothes, you can very quickly throw clothes onto the pile and you can also quickly grab clothes from the pile, but you have no idea where anything is and it can get really messy really fast. The heap is a region of memory that is used for dynamic memory allocation. It is organized in a more flexible manner than the stack, which means that you can allocate and deallocate memory in any order. The heap is typically slower than the stack because it requires dynamic memory allocation and deallocation.&lt;/p&gt;

&lt;p&gt;I could easily write full articles on just the stack and just the heap alone but for the sake of keeping your attention span I think this is a good enough mental model to get you started. &lt;/p&gt;

&lt;h3&gt;
  
  
  Tools and Tricks
&lt;/h3&gt;

&lt;p&gt;Use the tools available to you. We have debuggers and sanitizers, logging and profiling tools, and even static analysis tools that can help you catch these issues before they become a problem. Use them, they are there for a reason and they can save you a lot of time and headaches.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Debuggers&lt;/strong&gt;: A debugger is a tool that allows you to step through your code line by line and inspect the state of your program at any point in time. This can be incredibly helpful for finding and fixing memory management issues, as you can see exactly where things are going wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sanitizers&lt;/strong&gt;: Sanitizers are tools that can help you detect memory management issues at runtime. For example, AddressSanitizer can help you detect buffer overflows and use-after-free errors, while LeakSanitizer can help you detect memory leaks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logging and Profiling Tools&lt;/strong&gt;: Logging can help you track the flow of your program and see where memory is being allocated and deallocated. Profiling tools can help you identify performance bottlenecks in your code, which can be caused by inefficient memory management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Static Analysis Tools&lt;/strong&gt;: Static analysis tools can analyze your code without running it and can help you find potential memory management issues before they become a problem. For example, tools like Clang's static analyzer can help you find potential memory leaks and other issues in your code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code Reviews&lt;/strong&gt;: Code reviews can be a great way to catch memory management issues before they make it into production. Having another set of eyes on your code can help you spot potential issues that you might have missed, ask a friend to review your code if it's a hobby or solo project if you have to, just maybe not the same friend that you lend books to in the previous analogies, they might be a bit overwhelmed at this point.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Best Practices and Habits
&lt;/h3&gt;

&lt;p&gt;Tools are great and all but at the core of manual memory management is you, the developer. You need to have a good set of habits and practices that can help you avoid these pitfalls in the first place. By delving into the world of manual memory management you take responsibility for your own code and you have to be disciplined about how you manage your memory. Here are some best practices and habits that can help you avoid these pitfalls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Always Free What You Allocate&lt;/strong&gt;: This is the most basic rule of manual memory management, if you allocate memory you need to make sure that you free it when you're done with it. This can help you avoid memory leaks and ensure that your application does not run out of memory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Smart Pointers&lt;/strong&gt;: If you're using a language that supports smart pointers, such as C++, use the tools they give you, smart pointers are easy to learn and they can help you avoid a ton of pitfalls listed above like dangling pointers, double free, use after free, and even incorrect ownership. They are a great way to manage memory safely and efficiently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Favor scope-based cleanup&lt;/strong&gt;: If your language supports it, use scope-based cleanup mechanisms. In C++, this is RAII (Resource Acquisition Is Initialization), where resources are automatically released when they go out of scope. In Zig, you can use &lt;code&gt;defer&lt;/code&gt; to schedule cleanup code that runs when a block ends. These tools help prevent memory leaks and make sure your application doesn’t run out of memory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test small and often&lt;/strong&gt;: When you're doing manual memory management, it's important to test your code frequently and in small increments. This can help you catch issues early on before they become bigger problems. Write unit tests that specifically test the memory management aspects of your code, and run them regularly to ensure that everything is working as expected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Respect your future self, document your damn code&lt;/strong&gt;: You will forget about the details of your code in the future, especially when it comes to manual memory management. Make sure to document your code well, especially the parts that deal with memory management. This can help you understand your code better in the future and can help you avoid making mistakes when you come back to it later on.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How Different Languages Tackle Manual Memory Management
&lt;/h2&gt;

&lt;p&gt;Ok we're done with bad analogies for now, so let's discuss how different languages that do not come with a garbage collector handle manual memory management, and how they help you avoid the pitfalls mentioned above.&lt;/p&gt;

&lt;h3&gt;
  
  
  C
&lt;/h3&gt;

&lt;p&gt;C is a fine classic, it's timeless and beautiful in its simplicity. I appreciate just how minimal it is and how much you can do with just raw C and the standard library. Famously C has &lt;code&gt;malloc&lt;/code&gt; and &lt;code&gt;free&lt;/code&gt; for manual memory management, and it is up to the developer to make sure that they are used correctly. C does not have any built-in tools to help you avoid the pitfalls of manual memory management, so you need to be very careful when using it. However, there are many third-party libraries and tools that can help you manage memory in C, such as Valgrind for detecting memory leaks and AddressSanitizer for detecting buffer overflows and use-after-free errors.&lt;/p&gt;

&lt;p&gt;But recently we've gotten some new tools and helpers in the C ecosystem such as Fil-C.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fil-C
&lt;/h3&gt;

&lt;p&gt;Fil-C is a modified toolchain that adds a ton of memory safety to C and C++ at a very minimal cost and no need for expensive rewrites to other languages or paradigms which may lead you to throw away decades of battle tested code and bugfixes. &lt;/p&gt;

&lt;p&gt;What Fil-C delivers is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Modified Compiler&lt;/strong&gt;: Fil-C uses a modified compiler based on LLVM/Clang that instruments your code during compilation. It rewrites pointer operations and inserts safety checks so that invalid memory accesses such as out of bounds reads or writes are detected at runtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom Runtime&lt;/strong&gt;: Fil-C links programs against its own runtime which is responsible for enforcing these safety guarantees. It tracks memory objects, validates pointer accesses, and aborts execution if the program attempts an unsafe operation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modified Standard Library&lt;/strong&gt;: The C standard library used with Fil-C is adapted to operate within the same safety model. This ensures that safety guarantees apply consistently across both your code and the libraries it depends on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Memory Safe ABI&lt;/strong&gt;: Fil-C programs run with a modified application binary interface designed to support its safety checks and metadata tracking. This ABI allows the runtime to reliably enforce memory safety across the entire program.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, these components allow Fil-C to keep the simplicity and ecosystem of C, while adding a layer of protection against many of the most common and dangerous memory errors.&lt;/p&gt;

&lt;p&gt;But the magic happens when you compile your existing C or C++ code with the Fil-C compile, it will automatically rewrite your code to add safety checks, insert runtime checks and link against the Fil-C runtime, all without you having to change a single line of your code. This means that you can take your existing C or C++ codebase and compile it with Fil-C to get memory safety guarantees without having to rewrite your code or change your programming paradigm.&lt;/p&gt;

&lt;p&gt;Essentially something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Will compile to something sorta like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;check_pointer_bounds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;load_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  C++
&lt;/h2&gt;

&lt;p&gt;Ah yes the one everyone loves to hate but also the one that has had one of the biggest impact on the software industry. I know C++ gets a bad rep, in fact I even wrote a whole article &lt;a href="https://dev.to/blog/in-defense-of-cpp/"&gt;in defense of C++&lt;/a&gt; give it a read if you have some time. But back to the main point, C++ has evolved a lot since its inception and it has added a lot of features to help with manual memory management in a safe and controlled way, such as &lt;code&gt;smart pointers&lt;/code&gt; which are basically wrappers around raw pointers that automatically manage the memory they point to. &lt;/p&gt;

&lt;p&gt;Also modern C++ strongly encourages the use of containers instead of manual memory allocation, usually if you use the following: &lt;code&gt;std::vector&lt;/code&gt;, &lt;code&gt;std::string&lt;/code&gt;, &lt;code&gt;std::map&lt;/code&gt;, &lt;code&gt;std::unordered_map&lt;/code&gt; and so on, you will be using manual memory management under the hood but you won't have to worry about it because the containers will take care of it for you.&lt;/p&gt;

&lt;p&gt;Trouble arises if you use something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a raw pointer and you will have to manually manage the memory for it. The same can be achieved with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will give you a vector of 10 integers and you won't have to worry about memory management&lt;/p&gt;

&lt;h2&gt;
  
  
  Zig
&lt;/h2&gt;

&lt;p&gt;My favorite of the bunch, Zig is a modern programming language that has manual memory management as a core feature. It has a unique approach to memory management that is designed to be safe and efficient, while still giving developers full control over their memory usage. You can accomplish this by &lt;a href="https://dev.to/blog/zig-allocators-explained/"&gt;understanding zig allocators&lt;/a&gt; and using them correctly in your code. Zig also has the &lt;code&gt;defer&lt;/code&gt; keyword which allows you to schedule cleanup code that runs when a block ends, this can help you avoid memory leaks and ensure that your application does not run out of memory.&lt;/p&gt;

&lt;p&gt;Basically Zig gives you the tools you need to manage your memory safely and efficiently with minimal effort on your end and all this with basically no overhead or significant performance hits, it's a win-win situation.&lt;/p&gt;

&lt;p&gt;Plus zig can interop with C and even be used as a C or C++ compiler. &lt;/p&gt;

&lt;h2&gt;
  
  
  Odin
&lt;/h2&gt;

&lt;p&gt;I've covered how Odin manages memory in &lt;a href="https://dev.to/blog/is-odin-just-a-more-boring-c/"&gt;is odin just a more boring C?&lt;/a&gt; the title is a bit clickbaity, I'm sorry about that, I mean it in a good way, I like a language that is simple and straightforward and gets out of my way and lets me be productive. Odin is precisely that and I love it. I really would recommend reading through my article on Odin because it has a very cool and unique approach to manual memory management that is worth learning about.&lt;/p&gt;

&lt;p&gt;What makes Odin interesting when it comes to memory management is that it does not try to hide memory from you. Instead it makes it explicit and flexible through allocators. In Odin you can pass allocators around just like normal values and decide exactly how memory should be handled in a given part of your program. Many APIs also default to using context.allocator, which gives you a convenient default while still letting you override it when needed.&lt;/p&gt;

&lt;p&gt;This means you can easily switch strategies depending on the problem. Maybe you want an arena allocator for short lived data, or a custom allocator for a performance sensitive subsystem. Odin makes that kind of thing straightforward.&lt;/p&gt;

&lt;p&gt;In practice this leads to code that is very predictable. You know where allocations happen, you know who owns them, and you can change the allocation strategy without rewriting half your program. It fits nicely with Odin’s general philosophy of simplicity and control.&lt;/p&gt;

&lt;h2&gt;
  
  
  C3
&lt;/h2&gt;

&lt;p&gt;I have not had the time to properly dive into C3 yet. I tinkered with it a bit and I like the direction they are taking.&lt;/p&gt;

&lt;p&gt;From what I have seen so far, C3 leans heavily on scope based allocation and deterministic cleanup. The idea is that a lot of memory you allocate only needs to live for the duration of a function or a block. Instead of manually tracking every allocation and freeing it later, the language and its libraries make it easy to tie allocations to a scope and clean them up when that scope ends.&lt;/p&gt;

&lt;p&gt;C3 also provides helpers like &lt;code&gt;mem::new_array&lt;/code&gt; and &lt;code&gt;mem::alloc_array&lt;/code&gt; in the standard library so you are not constantly dealing with raw allocation primitives. The project also promotes the use of temporary or arena style allocators for short lived data.&lt;/p&gt;

&lt;p&gt;What I like about this approach is that it tackles one of the biggest sources of memory bugs which is lifetime confusion. If allocations clearly belong to a scope, a lot of mistakes simply disappear.&lt;/p&gt;

&lt;p&gt;I still need to spend more time with C3 before forming a strong opinion, but from what I have seen so far it looks like another interesting attempt at making manual memory management practical instead of painful.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Rust Detour
&lt;/h2&gt;

&lt;p&gt;I'll be honest with you, I don't consider Rust to be a manual memory management language. It's a language that has a unique approach to memory management yes but at no point does it require you to manually allocate and deallocate memory, it has a built in ownership system that takes care of all of that for you. Rust is basically a set of compiler rules which basically just shifts the responsibility of memory management away from the developer, if that sounds familiar it's because that's precisely what garbage collected languages do. They assume you will mess up and they take care of it for you, Rust just does that at compile time instead of runtime. &lt;/p&gt;

&lt;p&gt;Is that a good approach? Honestly no. I think it's a horrible approach that adds more problems than it solves and in the end it does not even guarantee memory full safety, that's how you get the famous quotes like &lt;code&gt;memory leaks are considered memory safe in Rust&lt;/code&gt;. So far Rust has failed to impress in production as well such as when Cloudflare went down because of a memory issue in their Rust code, a problem Rust was specifically designed to prevent. &lt;/p&gt;

&lt;p&gt;As you can tell I'm not a fan of Rust and I don't think it's a good language at all. It came in hot with wild promises and failed to deliver on any of them so far, but that's a topic for another article.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Should you Reach for Manual Memory Management?
&lt;/h2&gt;

&lt;p&gt;Manual memory management is really powerful and if you understand it well you can write incredibly efficient and performant code, but it also comes with a lot of responsibility and potential for mistakes. So when should you reach for manual memory management?&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Critical Code
&lt;/h3&gt;

&lt;p&gt;In high performance systems, even small inefficiencies can add up. Games, trading systems, real time audio processing, and high frequency networking software often benefit from predictable memory usage and deterministic cleanup.&lt;/p&gt;

&lt;p&gt;Garbage collectors can introduce pauses or unpredictable allocation patterns. Manual memory management allows developers to control exactly when allocations happen and when memory is released.&lt;/p&gt;

&lt;h3&gt;
  
  
  Embedded Systems
&lt;/h3&gt;

&lt;p&gt;Embedded devices often run with extremely limited memory and CPU resources. A garbage collector may simply be too expensive or too unpredictable in these environments.&lt;/p&gt;

&lt;p&gt;Manual memory management lets developers carefully control how memory is used and ensures that the system never exceeds its constraints.&lt;/p&gt;

&lt;h3&gt;
  
  
  Systems Programming
&lt;/h3&gt;

&lt;p&gt;Operating systems, drivers, compilers, and networking stacks all tend to require direct control over memory. In these areas you often need to manage memory layouts explicitly, work with hardware, or operate in environments where a runtime simply does not exist.&lt;/p&gt;

&lt;h3&gt;
  
  
  Large Existing Codebases
&lt;/h3&gt;

&lt;p&gt;Sometimes the reason is simple. There are decades of battle tested C and C++ code in the world. Rewriting those systems into a different paradigm or language is often unrealistic or would lead to throwing away decades worth of battle tested code and bug fixes. &lt;/p&gt;

&lt;p&gt;In those cases it makes more sense to improve tooling, add safety layers, or adopt better practices rather than start over from scratch.&lt;/p&gt;

&lt;p&gt;And that is really the key point. Manual memory management is not something you reach for just because you can. It is something you use when you need control, predictability, and performance that higher level runtimes cannot always provide.&lt;/p&gt;

&lt;p&gt;Understanding it also changes how you think about software in general. Once you know what is actually happening under the hood when memory is allocated, freed, or reused, many parts of programming start to make a lot more sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Manual memory management has a reputation for being dangerous and outdated, but that reputation mostly comes from misunderstanding and misuse.&lt;/p&gt;

&lt;p&gt;Yes, the pitfalls are real. But so are the performance benefits and the deeper understanding of how software works that comes with mastering it.&lt;/p&gt;

&lt;p&gt;The reality is that manual memory management is still everywhere. Operating systems, game engines, databases, embedded systems, networking stacks and high performance software all rely on it every day. Entire industries run on systems that manage memory manually and have done so successfully for decades.&lt;/p&gt;

&lt;p&gt;What has changed is the ecosystem around it. We now have better debugging tools, sanitizers, static analyzers and improved language features that make writing safe manual memory code far more approachable than it used to be. Modern C++, Zig, Odin and C3 are all exploring different ways to give developers control without unnecessary pain.&lt;/p&gt;

&lt;p&gt;At the end of the day, manual memory management is just another tool in the toolbox. Not every project needs it, and for many applications a garbage collected language is perfectly fine. But when performance, determinism or low level control matter, understanding manual memory management becomes incredibly valuable.&lt;/p&gt;

&lt;p&gt;And honestly, once you understand it, it stops being scary. It just becomes another part of writing good software.&lt;/p&gt;

&lt;p&gt;Thank you for your time, this was a long one and I sincerely hope you enjoyed it.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>zig</category>
      <category>systems</category>
    </item>
    <item>
      <title>AI’s Trap: Settling for Boilerplate Over Elegant Code</title>
      <dc:creator>Dayvster 🌊</dc:creator>
      <pubDate>Mon, 27 Oct 2025 11:04:09 +0000</pubDate>
      <link>https://dev.to/dayvster/ais-trap-settling-for-boilerplate-over-elegant-code-n32</link>
      <guid>https://dev.to/dayvster/ais-trap-settling-for-boilerplate-over-elegant-code-n32</guid>
      <description>&lt;p&gt;We are all familiar with Picasso's "The Bull" series, in which he progressively simplifies the image of a bull down to its most basic, yet still recognizable form. Steve Jobs was famously inspired by this concept, leading him to advocate for simplicity and elegance in design and technology above countless features and excessive complexity. Distill a concept even as complex as software or UX down to its essence, and what you are left with is something beautiful and elegant that fulfills its purpose with minimal fuss.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdayvster.com%2Fimages%2Fblog%2Fais-trap-settling-for-boilerplate-over-elegant-code%2Fbull.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdayvster.com%2Fimages%2Fblog%2Fais-trap-settling-for-boilerplate-over-elegant-code%2Fbull.png" width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  So Why Do We Accept Ugly Code Then?
&lt;/h2&gt;

&lt;p&gt;I've noticed a worrying trend in programming that, as tools around a programming language improve and automate more of our work, we increase our tolerance for boilerplate, repetitive, and frankly, ugly code. We accept it and we tell ourselves it's ok, the linter will fix it, the formatter will fix it, the compiler will optimize it, in the end it's all ones and zeroes anyway, right, why would any of this matter?&lt;/p&gt;

&lt;p&gt;Since AI has entered the equation, tolerance for boilerplate and excuses for ugly code has only increased. We tell ourselves that as long as the AI can generate the code for us, it doesn't matter if it's elegant or not. After all, we didn't write it, the AI did. So why should we care about the quality of the code? After all, we relinquished ownership of the code the moment we asked the AI to generate it for us. &lt;/p&gt;

&lt;p&gt;Now, you may not be someone who uses AI to generate code, and kudos to you, welcome to the club, however, even as someone who relatively early noticed that AI does not produce something that I could sign off on as my own work proudly and with confidence. I have engaged in the practice of using AI to generate some more tedious tasks for myself, such as just taking a JSON response from an API and asking Copilot, ChatGPT, or Grok to generate a type definition in the language I am currently working with. &lt;/p&gt;

&lt;p&gt;I work on many personal and professional projects, and I encounter different types of people and teams, some embrace AI, others shun it. However I have noticed that in teams or projects where AI is embraced or even encouraged or mandated as part of the development process, tend to produce a lot of boilerplate and a lot of very ugly inelegant code that few wish to take real ownership of, because it is not their creation they handed ownership of it to the AI and therefore abandoned the developer - code relationship that is so essential to producing quality software.&lt;/p&gt;

&lt;h2&gt;
  
  
  Developers Should Love Their Code
&lt;/h2&gt;

&lt;p&gt;I harp on this a lot, development is a unique one of a kind blend of engineering and creative expression, those two aspects of our craft should not be at odds with each other, but should rather complement each other.&lt;/p&gt;

&lt;p&gt;When you write code, you should love it, you should be proud of it, you should want to show it off to others who can understand it and appreciate it, in essence, your way of expressing your way of thinking and how you tackle problems.&lt;/p&gt;

&lt;p&gt;I've briefly touched upon the fact that handing off ownership of your code to AI means abandoning that relationship between you, the developer, and your code. I want to expand on that a bit more. If you don't care about the code you write, if you don't love what you are doing or the creative process to solve a specific problem, you will forever lack understanding of that specific problem that you are solving. It will get solved for you by the AI, and you will rob yourself of the opportunity to learn and understand that problem on a deeper level.&lt;/p&gt;

&lt;p&gt;This is something that has kicked me in the ass a fair few times in the past, I'd get a problem to solve, think oh this is easy, draft up an initial solution solve the problem at a very superficial level and then in the coming weeks get 10 QA tickets filed against that problem because there is more to the problem than meets the eye and there are often things that you miss or do not even consider during your first implementation. &lt;/p&gt;

&lt;p&gt;AI will do exactly the same thing every single time. The difference, though, is that every time I was given a problem to solve, it resulted in fewer and fewer QA tickets because I understood and learnt from my past experiences and mistakes and knew how to approach problem-solving more effectively.&lt;/p&gt;

&lt;p&gt;AI will not do any of that, it will always solve the problem given to it at face value without any deeper understanding or context. It will not learn from past mistakes, grow, or adapt its mindset to shift its approach based on new information or insights. It will always solve the problem, and worst of all, it will never learn to understand the problem so well that it can simplify the solution down to its essence and produce something elegant.&lt;/p&gt;

&lt;p&gt;There is a certain beauty to solving a problem and all of its potential side effects in a very minimal and easily readable way. That is an ideal that I strive for in my own work, and I encourage you to do the same. Love your code, be proud of it, and strive for elegance over boilerplate.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cost of Boilerplate
&lt;/h2&gt;

&lt;p&gt;Boilerplate code is not just an eyesore, it comes with real costs. It increases the cognitive load on developers, makes the project less enjoyable to work on, and increases the time to onboard new team members. And increases the time to fix or resolve issues, which directly impacts the experience of the end user of your product.&lt;/p&gt;

&lt;p&gt;As a third-order effect, it also increases the soft requirement for more tooling and more AI assistance to manage and maintain your codebase, which is a dangerous spiral best avoided altogether. &lt;br&gt;
A question I've been asking myself is how much of this is by happenstance and how much of this is by design. I don't want to get all conspiracy theorist on you, but it does make me wonder if there are vested interests in making us accept boilerplate and ugly code as the norm, because it increases the demand for AI tools and services that can help us manage and maintain our codebases. I'm not confident enough to attach any weight to this thought, but it's something worth pondering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aesthetic Responsibility
&lt;/h2&gt;

&lt;p&gt;It's very easy for us as developers to simply dismiss aesthetics as something superficial and unimportant in the grand scheme of things. After all, we are not building art, we are building software that solves problems and delivers value to users. However, I would argue that aesthetics play a crucial role in the quality and maintainability of our code. &lt;/p&gt;

&lt;p&gt;When we write code that is elegant and beautiful, we are not just making it easier for ourselves to read and understand, we are also making it easier for others to read and understand. We are creating a shared language and a shared understanding of the problem we are solving and the solution we are implementing. When we write ugly and boilerplate code, we are creating barriers and obstacles for ourselves and others. We are making it harder to read and understand, we are making it harder to maintain and evolve, and we are making it harder to collaborate and share knowledge. We're increasing the friction instead of reducing it.&lt;/p&gt;

&lt;p&gt;It always feels nice as a human to look at something beautiful, whether it's a piece of art, a well-designed product, a sports car, a building with stunning architecture, and so on and so forth. But when it comes to code, we often dismiss aesthetics as unimportant, after all, it's a means to an end, right? I would argue that as developers, we have an aesthetic responsibility to ourselves and to others to write code that is elegant and beautiful, not just for the sake of aesthetics, but for the sake of quality and maintainability.&lt;/p&gt;

&lt;p&gt;But it is very easy lately to just let the tools we have been given and AI do the heavy lifting for us, and in doing so, we risk losing sight of our aesthetic responsibility as developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Does AI Increase the Tolerance for Boilerplate?
&lt;/h2&gt;

&lt;p&gt;You know the drill: you are working away on a piece of a project, and you're on a tight deadline with a backlog full of features to implement and bugs to fix. You think to yourself, "I just need to get this done quickly, I can refactor it later(a mindset I generally encourage)." So you ask the AI to generate the code for you, and it spits out a solution that does not work, so you refine your prompt and try again, and again, and again until you get something that works. Now you have a working solution, great! One quick glance at your &lt;code&gt;git status&lt;/code&gt;, &lt;strong&gt;HOLY SH.. WHY ARE THERE 32 FILES CHANGED? AND WHY ARE MOST OF THEM NEW FILES?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ok..OK, you have a working solution. It's best to open up a PR and let the Copilot PR bot handle the code review, and maybe a couple of co-workers will spot some things and suggest improvements. Once you have more time on your hands, you will absolutely go back and refactor this mess down to something elegant and beautiful. You just need to get this next task done first, oh, and it's on a tight deadline as well... &lt;/p&gt;

&lt;p&gt;Before you know it, you have a codebase full of boilerplate and code that could easily be done in half as many lines or less, you will never go back and refactor it because there is always something more urgent to do, and the cycle continues. &lt;/p&gt;

&lt;p&gt;Maintenance? Not a problem, we can just use AI to generate documentation for us or explain code blocks for us. Testing? AI can generate tests for us, no need to think about edge cases or test coverage. Performance? AI can optimize our code for us, no need to understand the underlying algorithms or data structures.&lt;/p&gt;

&lt;p&gt;It creeps up on you slowly, but surely. AI stands as an excuse to not care about the quality of your code, but only the quality or functionality of your outcome. There's a lot to be said and discussed about this topic.&lt;/p&gt;

&lt;p&gt;You might be rightfully asking yourself, does it even matter if the code is ugly and I feel no pride in it, as long as the end user gets the functionality they need?&lt;/p&gt;

&lt;h2&gt;
  
  
  "Good Enough" Is Not Good Enough
&lt;/h2&gt;

&lt;p&gt;I always liked the saying, "How you do anything is how you do everything." It has multiple different explanations and interpretations, but to me, it boils down to this: if you accept mediocrity in one aspect of your life, you will accept mediocrity in all aspects of your life. Or, how you do the little things is how you do the big things. &lt;/p&gt;

&lt;p&gt;If you accept "good enough" code, you will eventually have a good enough product on your hands that might have to compete with products that were built with care and pride. Do you want to deliver good enough products, or do you want to deliver great products that you can be proud of?&lt;/p&gt;

&lt;p&gt;Completely ignoring the market and financial viability of this approach, will you be happy with the work you do if mediocrity is the standard you hold yourself to? Or worse, if mediocrity is the modus operandi of your team or company? &lt;/p&gt;

&lt;p&gt;If you truly believe that "good enough" is good enough, then by all means continue down that path. But I urge you to test that belief, challenge it, start a project that you may never finish or get paid for, but attempt to take a complex concept and distill it down to its essence in the most elegant way possible, do what Picasso did with his bull series, and see how far you can push yourself to create something beautiful out of something complex while still maintaining its core functionality and purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;AI is a powerful tool that can help us be more productive and efficient, but it should not be used as an excuse to accept boilerplate and ugly code. As developers, we should strive for elegance and simplicity in our code, and we should take pride in the work we do. We should love our code and our craft, and we should never settle for "good enough" when it comes to the quality of our work. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write code you would sign your name under.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you've enjoyed this article and made it this far, thank you sincerely for your time. I hope it was worth it and that it sparked some thoughts and reflections on your own approach to coding and craftsmanship, and I sincerely hope it did not feel wasted.&lt;/p&gt;

&lt;p&gt;If you have any thoughts or feedback on this article, please feel free to reach out to me on &lt;a href="https://twitter.com/dayvsterdev" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. I'm always open to discussions and feedback, or just general chit chat about just about anything I find interesting.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>programming</category>
      <category>ai</category>
    </item>
    <item>
      <title>Why Zig + Qt Feels Like Doing the Impossible Right</title>
      <dc:creator>Dayvster 🌊</dc:creator>
      <pubDate>Wed, 22 Oct 2025 19:34:37 +0000</pubDate>
      <link>https://dev.to/dayvster/why-zig-qt-feels-like-doing-the-impossible-right-2ibn</link>
      <guid>https://dev.to/dayvster/why-zig-qt-feels-like-doing-the-impossible-right-2ibn</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;It's no secret that I love Zig, and it's also no secret that my favorite GUI framework by far is Qt. So naturally, when I discovered that there were Zig bindings for Qt I had to give it a try and build something with it. What I decided to build was a fairly rudimentary shopping list application, nothing fancy just one of my standard things I like to build when exploring a new GUI framework of any kind. Because a shopping list app is simple enough to not get in the way of learning the framework but also complex enough to cover a lot of the basic concepts that you'll need to know when building a GUI application such as handling user input, displaying data, responding to different events, sorting and filtering the data and so on. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Library
&lt;/h2&gt;

&lt;p&gt;For this article and experiment I went with &lt;a href="https://github.com/rcalixte/libqt6zig" rel="noopener noreferrer"&gt;libqt6zig&lt;/a&gt;, there are other bindings libraries out there for Zig but they are QML based whereas libqt6zig is a direct binding to the Qt C++ API which I tend to prefer and it interested me a lot more because direct bindings are generally more performant and a lot harder to do correctly, so I wanted to see how well it was implemented and how well it worked in practice. The results were frankly surprisingly good and I was able to build a fully functional shopping list application with it in a relatively short amount of time, with minimal friction or trouble, I did manage to segfault a couple of times but that's to be expected and honestly half the fun, made me kinda nostalgic in fact. I've also spoken about the library with its author and maintainer &lt;a href="https://github.com/rcalixte" rel="noopener noreferrer"&gt;rcalixte&lt;/a&gt; who is a super nice and helpful guy with an enthusiasm for tech and programming that is infectious and I can really relate to. So honestly if you've ever done any GUI programming before and you're interested in trying out Zig for GUI development I highly recommend you give libqt6zig a try, it's a solid library with a ton of potential.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Experience
&lt;/h2&gt;

&lt;p&gt;The very first touch point is the &lt;strong&gt;installation&lt;/strong&gt; of the bindings to your Zig project, this is fairly easy and well documented &lt;a href="https://github.com/rcalixte/libqt6zig?tab=readme-ov-file#building" rel="noopener noreferrer"&gt;here&lt;/a&gt;, after you install all your dependencies libraries, you can initialize your new zig project with &lt;code&gt;zig init&lt;/code&gt; as you always would, then you simply need to add the libqt6zig bindings to your zig project, you can do this a number of ways but my personal favorite is to simply use &lt;code&gt;zig fetch --save git+https://github.com/rcalixte/libqt6zig&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does Zig fetch do?
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;zig fetch&lt;/code&gt; command will add the specified dependency to your &lt;code&gt;build.zig.zon&lt;/code&gt; with two fields, &lt;code&gt;url&lt;/code&gt; and &lt;code&gt;hash&lt;/code&gt;, the &lt;code&gt;url&lt;/code&gt; field is the URL of the git repository and the &lt;code&gt;hash&lt;/code&gt; field is a hash of the commit at the time you added the dependency, this ensures that your project will always use the same version of the dependency even if the repository is updated later on. You can also specify the hash in your &lt;code&gt;zig fetch&lt;/code&gt; command by adding &lt;code&gt;#&amp;lt;commit-hash&amp;gt;&lt;/code&gt; like so: &lt;code&gt;zig fetch --save https://github.com/username/repository/archive/&amp;lt;commit&amp;gt;&lt;/code&gt; which will essentially lock your dependency to that specific commit. Note even if you omit the hash it will lock your dependency in &lt;code&gt;build.zig.zon&lt;/code&gt; to the the latest commit at the time you added the dependency. &lt;/p&gt;

&lt;p&gt;What it then does is fetch the contents of the repository and stores it in your zig cache directory &lt;code&gt;.zig-cache&lt;/code&gt;, so that it does not need to refetch it every time you build your project nor does it need internet connectivity every time you build your project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using the Library
&lt;/h3&gt;

&lt;p&gt;We're not quite done yet, after you've added the dependency to your project you need to actually link it in your &lt;code&gt;build.zig&lt;/code&gt; file, this is fairly straightforward as well, you simply need to add the following lines to your &lt;code&gt;build.zig&lt;/code&gt; file, so that zig build knows to link the libqt6zig library when building your project. Luckily rxcalixte has documented this process well in his readme in the &lt;a href="https://github.com/rcalixte/libqt6zig?tab=readme-ov-file#usage" rel="noopener noreferrer"&gt;Usage&lt;/a&gt; section. I recommend you check it out for the full details as it is subject to change and a link to the readme will always be best for the most up to date information. &lt;/p&gt;

&lt;h2&gt;
  
  
  OK Now let's Build Something
&lt;/h2&gt;

&lt;p&gt;This is what we are building:&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdayvster.com%2Fimages%2Fblog%2Fzigqt%2Fshopping-list-screenshot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdayvster.com%2Fimages%2Fblog%2Fzigqt%2Fshopping-list-screenshot.png" alt="Shopping List App Screenshot" width="711" height="922"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;So as I've mentioned before I decided to build a simple shopping list application, nothing fancy just a basic CRUD application that allows you to add items to your shopping list, mark them as bought, upon which I want to apply some styles to it such as a strikethrough effect and a different color to indicate that the item has been bought and sort the item to the bottom of the list so that the unbought items are always at the top of the list. We also wanna be able to easily clean up the entire list with a single button click on "Clear All" or just clear all bought items with a click on "Clear Bought" which will only remove all the items that have been marked as bought.&lt;/p&gt;

&lt;h2&gt;
  
  
  Opening the Application Window
&lt;/h2&gt;

&lt;p&gt;The first step to any GUI application is to first open the window, I won't make the obvious bad pun here, you're welcome for that by the way. To open a window with &lt;code&gt;libqt6zig&lt;/code&gt; is fairly straightforward, you simply need to create a new widget and tell Qt to show it. That widget will serve as the main application window, and everything else.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qwidget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;New2&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;qwidget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;QDelete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;qwidget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetWindowTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Shopping List"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;qwidget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetMinimumSize2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;480&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might be confused by the use of &lt;code&gt;New2&lt;/code&gt; here instead of just &lt;code&gt;New&lt;/code&gt;, this is because Qt has a lot of overloaded functions and Zig does not support function overloading, so the bindings library has to create multiple versions of the same function with different names to accommodate for that. In this case &lt;code&gt;New2&lt;/code&gt; is the version of &lt;code&gt;New&lt;/code&gt; that takes no arguments, whereas &lt;code&gt;New&lt;/code&gt; takes a parent widget as an argument.&lt;br&gt;
After creating the window widget we set its title and minimum size, then we can show it by calling &lt;code&gt;qwidget.Show(window);&lt;/code&gt;. Which I will do later after setting up the rest of the application, but if you are just playing around feel free to call it right after setting the title and size to see the window pop up.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting up the Layout
&lt;/h2&gt;

&lt;p&gt;In Qt, layouts are what make widgets actually show up in a sensible way. to achieve what we have in the screenshot above we need to set up a vertical box layout or &lt;code&gt;QVBoxLayout&lt;/code&gt; if you are familiar with Qt terminology. A vertical box layout is a layout that arranges widgets vertically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;main_layout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qvboxlayout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;New2&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;qvboxlayout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetSpacing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main_layout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;qvboxlayout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetContentsMargins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main_layout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes a nice vertical layout with some breathing room for the elements it will contain.&lt;/p&gt;

&lt;p&gt;Now let's make our first row which will contain the input field and the add button.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;input_row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qhboxlayout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;New2&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;qhboxlayout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetSpacing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;item_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qlineedit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;New2&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;qlineedit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetPlaceholderText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item_input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"e.g. Milk, Eggs, Bread"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;add_btn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qpushbutton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;New3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Add"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;qpushbutton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetFixedWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;add_btn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we have here is a horizontal box layout or &lt;code&gt;QHBoxLayout&lt;/code&gt; that will contain our input field and add button. We set the spacing between the elements to 6 pixels for a bit of breathing room. Then we create a &lt;code&gt;QLineEdit&lt;/code&gt; widget which is the input field where the user can type in the name of the item they want to add to their shopping list. We also set a placeholder text to give the user an idea of what to type in. Finally we create a &lt;code&gt;QPushButton&lt;/code&gt; widget which is the add button that the user will click to add the item to their shopping list. We set its fixed width to 90 pixels so that it doesn't stretch too much.&lt;/p&gt;

&lt;p&gt;That's the nice part about Qt+Zig every call here is explicit and tells you exactly what it does, no magic or hidden behavior, no global "current layout" no hidden parent assignments, just pure explicitness which I find a lot nicer to read.&lt;/p&gt;

&lt;p&gt;Now we can attach our input row and add button to the horizontal layout and then the horizontal layout to the main vertical layout.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="n"&gt;qhboxlayout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item_input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;qhboxlayout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;add_btn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c"&gt;// ...More code will go here&lt;/span&gt;
&lt;span class="c"&gt;// Add the input_row to the main layout&lt;/span&gt;
&lt;span class="c"&gt;// qvboxlayout.AddLayout(main_layout, input_row);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see we are simply adding the widgets in the order we want them to appear in the layout, then taking that layout and adding it to the main layout. This should produce the top row of our application. But it won't do much so far, we still need to add the list view and the clear buttons. &lt;/p&gt;

&lt;h2&gt;
  
  
  Adding the List and Controls
&lt;/h2&gt;

&lt;p&gt;Next up is the list where items will appear and the two control buttons at the very bottom with the labels "Clear All" and "Clear Bought".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;list_label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qlabel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;New3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Items:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;qlabel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetStyleSheet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list_label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"font-weight: 600; margin-top:6px; margin-bottom:2px;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;list_widget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qlistwidget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;New2&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Qt comes with a lot of useful widgets that you can use out of the box, in this case we are using a &lt;code&gt;QLabel&lt;/code&gt; widget to display the "Items:" label above the list and a &lt;code&gt;QListWidget&lt;/code&gt; to display the list of items. The &lt;code&gt;QListWidget&lt;/code&gt; is a convenient widget that allows us to easily add, remove and manipulate items in the list. We also set a bit of styling on the label to make it look a bit nicer, if you are familiar with CSS this should look pretty straightforward to you.&lt;/p&gt;

&lt;p&gt;Now for the bottom control row:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;controls_row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qhboxlayout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;New2&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;clear_btn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qpushbutton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;New3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Clear All"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;qpushbutton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetFixedWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clear_btn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;110&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;clear_bought_btn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qpushbutton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;New3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Clear Bought"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;qpushbutton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetFixedWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clear_bought_btn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;qhboxlayout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;controls_row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clear_btn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;qhboxlayout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;controls_row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clear_bought_btn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we create another horizontal box layout for the control buttons, then we create two &lt;code&gt;QPushButton&lt;/code&gt; widgets for the "Clear All" and "Clear Bought" buttons. We set their fixed widths to make them look a bit more uniform. Finally we add the buttons to the horizontal layout.&lt;/p&gt;

&lt;p&gt;And just like that we can now bring it all together by adding everything to the main vertical layout in order.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="n"&gt;qvboxlayout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddLayout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main_layout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_row&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c"&gt;// input row goes here where it belongs&lt;/span&gt;
&lt;span class="n"&gt;qvboxlayout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main_layout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;list_label&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;qvboxlayout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main_layout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;list_widget&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;qvboxlayout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddLayout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main_layout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;controls_row&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point our layout hierarchy looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Main Window
├── Input Row
│   ├── QLineEdit (item_input)
│   └── QPushButton ("Add")
├── QLabel ("Items:")
├── QListWidget (list_widget)
└── Controls Row
    ├── QPushButton ("Clear All")
    └── QPushButton ("Clear Bought")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! Now we got our layouts and widgets all set up we can get to my favorite part which is the logic and interactivity/functionality.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Context System
&lt;/h2&gt;

&lt;p&gt;One of the more clever parts of this app is how it maps each widget to its corresponding Zig state.&lt;br&gt;
Because Qt signals are C-style callbacks with no built-in state, we use a global hash map to link each widget pointer to its appropriate state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;AppCtxMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AutoHashMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;?*&lt;/span&gt;&lt;span class="n"&gt;anyopaque&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ctx_map&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AppCtxMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each context holds references to the main widgets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;?*&lt;/span&gt;&lt;span class="n"&gt;anyopaque&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;item_input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;?*&lt;/span&gt;&lt;span class="n"&gt;anyopaque&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;list_widget&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;?*&lt;/span&gt;&lt;span class="n"&gt;anyopaque&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we initialize the app, we register everything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;ctx_map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;ctx_map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item_input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;ctx_map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;add_btn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;ctx_map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clear_btn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;ctx_map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list_widget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;ctx_map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clear_bought_btn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s how any callback can later say:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;get_ctx_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;?*&lt;/span&gt;&lt;span class="n"&gt;anyopaque&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?*&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ctx_map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;orelse&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only problem and downside with this is that we are relying heavily on opaque pointers which are not type safe at all, so we have to be extra careful when working with them to avoid segfaults and other memory issues and this is precisely where I ran into some self inflicted issues and headaches with this application, completely my own fault for accepting a lack of type safety and memory safety for the sake of convenience, but it is what it is and it's not like I was building a production ready application here, I just wanted to be clever and explore Zig and Qt together, so I accepted the tradeoff. If you decide to build something more serious with this approach I highly recommend you find a way to make it more type safe and memory safe. This is just an example of me experimenting and exploring.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hooking up the Logic
&lt;/h2&gt;

&lt;p&gt;Now that we have the UI and context system ready, it’s time to connect everything together. This is where the application actually comes alive.&lt;br&gt;
In Qt, widgets typically use a signal and slot mechanism, where signals (like button clicks) are connected to functions (slots) that respond to those events.&lt;br&gt;
In libqt6zig, these signals are exposed as plain callback functions that you can assign in Zig, so you can handle events like button clicks in a straightforward way.&lt;/p&gt;
&lt;h3&gt;
  
  
  Handling the Add Button
&lt;/h3&gt;

&lt;p&gt;When the user clicks Add, we want to: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Grab the text from the input field.&lt;/li&gt;
&lt;li&gt;If it’s not empty, create a new list item.&lt;/li&gt;
&lt;li&gt;Add it to the list widget.&lt;/li&gt;
&lt;li&gt;Clear the input field.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;on_add_clicked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;?*&lt;/span&gt;&lt;span class="n"&gt;anyopaque&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;callconv&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="py"&gt;C&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_ctx_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;orelse&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;item_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qlineedit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;item_input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item_text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;len&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;list_item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qlistwidgetitem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;New3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item_text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;qlistwidget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;list_widget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;list_item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;qlineedit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Clear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;item_input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We connect this function to the Add button’s clicked signal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="n"&gt;qpushbutton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnClicked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;add_btn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on_add_clicked&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handling the Clear Buttons
&lt;/h3&gt;

&lt;p&gt;Next, we want to make the "Clear All" and "Clear Bought" buttons actually do something. The logic is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clear All&lt;/strong&gt;: Remove all items from the list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clear Bought&lt;/strong&gt;: Remove only items that have been marked as bought (we’ll handle this later by checking a property or style on each item).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;on_clear_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;?*&lt;/span&gt;&lt;span class="n"&gt;anyopaque&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;callconv&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="py"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_ctx_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;qlistwidget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Clear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;list_widget&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;on_clear_bought&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;?*&lt;/span&gt;&lt;span class="n"&gt;anyopaque&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;callconv&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="py"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_ctx_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qlistwidget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;list_widget&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c"&gt;// iterate backwards so removing items doesn't shift unprocessed indices&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;c_int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;itm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qlistwidget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;list_widget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itm&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qlistwidgetitem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CheckState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itm&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c"&gt;// remove and delete the item&lt;/span&gt;
                &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;taken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qlistwidget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TakeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;list_widget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="c"&gt;// taken is removed from the widget; drop the pointer (Qt will clean up)&lt;/span&gt;
                &lt;span class="mi"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;taken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see in &lt;code&gt;on_clear_all&lt;/code&gt; the logic is pretty straightforward, we simply call &lt;code&gt;qlistwidget.Clear&lt;/code&gt; and give it a reference to our list widget, then Qt will handle the rest for us.&lt;/p&gt;

&lt;p&gt;Now the more clever part comes in &lt;code&gt;on_clear_bought&lt;/code&gt;, here we iterate through all the items in the list widget and check their state, however we know that we will sort bought items to the bottom of the list when the user ticks them as "done" or in this case "bought", so we can optimize this a bit by iterating backwards through the list and stopping as soon as we find an unbought item, because all bought items will be at the bottom of the list. This way we avoid unnecessary iterations and checks.&lt;/p&gt;

&lt;p&gt;All there's left to do is to actually hook these two up to their respective buttons:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="n"&gt;qpushbutton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnClicked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clear_btn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on_clear_all&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;qpushbutton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnClicked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clear_bought_btn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on_clear_bought&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Marking Items as Bought
&lt;/h3&gt;

&lt;p&gt;Finally, we need to handle changes to the items themselves. In our shopping list, items can be marked as bought by checking a checkbox next to them. In Qt, this is done via the CheckState property of a QListWidgetItem. Whenever the user checks or unchecks an item, we want to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Apply a strikethrough effect to indicate that the item is bought.&lt;/li&gt;
&lt;li&gt;Move the bought item to the bottom of the list to keep unbought items at the top.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;on_item_changed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;?*&lt;/span&gt;&lt;span class="n"&gt;anyopaque&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;?*&lt;/span&gt;&lt;span class="n"&gt;anyopaque&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;callconv&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="py"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_ctx_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qlistwidgetitem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CheckState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;is_checked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qlistwidgetitem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Font&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;qfont&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetStrikeOut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;is_checked&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;qlistwidgetitem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qlistwidget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;list_widget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;taken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qlistwidget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TakeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;list_widget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;taken&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;is_checked&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;qlistwidget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddItem2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;list_widget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;taken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;qlistwidget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;InsertItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;list_widget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;taken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how we use &lt;code&gt;TakeItem&lt;/code&gt; to temporarily remove the item from the list, then re-insert it in the appropriate position. This is a common trick in Qt when you want to reorder items dynamically.&lt;/p&gt;

&lt;p&gt;Then we connect this callback to the list widget:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="n"&gt;qlistwidget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnItemChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list_widget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on_item_changed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures that every time an item's checkbox is toggled, the visual state updates and the item is moved accordingly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling the Return Key
&lt;/h3&gt;

&lt;p&gt;For convenience, we also allow the user to add items by pressing Enter in the input field. This is done using the &lt;code&gt;OnReturnPressed&lt;/code&gt; signal of &lt;code&gt;QLineEdit&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;on_return_pressed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;?*&lt;/span&gt;&lt;span class="n"&gt;anyopaque&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;callconv&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="py"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_ctx_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qlineedit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;item_input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;allocator&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;len&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;add_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;allocator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;qlineedit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnReturnPressed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item_input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on_return_pressed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function simply grabs the text from the input, adds the item if it’s non-empty, and clears the input field.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Items Helper
&lt;/h3&gt;

&lt;p&gt;Both the Add button and the Enter key use the same helper function to actually create and insert the list item:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;add_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qlistwidgetitem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;New7&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;list_widget&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qlistwidgetitem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Flags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;qlistwidgetitem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetFlags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;qnamespace_enums&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ItemFlag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ItemIsUserCheckable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;qlistwidgetitem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetCheckState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;qnamespace_enums&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;CheckState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Unchecked&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;qlineedit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Clear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;item_input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes it easy to maintain consistent behavior across multiple input methods. Notice the 0x20 flag, this is Qt::ItemIsUserCheckable, which allows the checkbox to appear next to the item.&lt;/p&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;At this point, all the interactive logic is wired up. Users can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type an item and press Enter or click Add to add it.&lt;/li&gt;
&lt;li&gt;Check/uncheck items to mark them bought/unbought with a strikethrough effect.&lt;/li&gt;
&lt;li&gt;Clear all items or only bought items using the respective buttons.&lt;/li&gt;
&lt;li&gt;Automatically reorder items so unbought items remain at the top.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is a fully functional, minimal shopping list application built entirely in Zig using direct Qt bindings.&lt;/p&gt;

&lt;p&gt;With the functionality in place, the next step is to make the application actually look nice. Qt widgets come with default styles that are functional but not particularly attractive. Fortunately, Qt supports CSS-like styling through SetStyleSheet, and libqt6zig exposes this feature directly.&lt;/p&gt;

&lt;p&gt;Here’s the style sheet I used for the app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;const&lt;/span&gt; &lt;span class="nt"&gt;STYLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
  &lt;span class="s1"&gt;"QWidget { background: #121212; color: #e6e6e6; font-family: 'Sans'; }"&lt;/span&gt;
  &lt;span class="s1"&gt;"QListWidget { background: #1e1e1e; color: #e6e6e6; border: 1px solid #2a2a2a; }"&lt;/span&gt;
  &lt;span class="s1"&gt;"QListWidget::item { padding: 6px 4px; }"&lt;/span&gt;
  &lt;span class="s1"&gt;"QListWidget::item:selected { background: #2a2a2a; color: #ffffff; }"&lt;/span&gt;
  &lt;span class="s1"&gt;"QLineEdit { background: #222222; color: #e6e6e6; border: 1px solid #2a2a2a; padding: 6px; border-radius: 4px; }"&lt;/span&gt;
  &lt;span class="s1"&gt;"QLineEdit::placeholder { color: #9a9a9a; }"&lt;/span&gt;
  &lt;span class="s1"&gt;"QPushButton { background: #2a2a2a; color: #e6e6e6; border: 1px solid #333; padding: 4px 8px; border-radius: 4px; }"&lt;/span&gt;
  &lt;span class="s1"&gt;"QPushButton:hover { background: #313131; }"&lt;/span&gt;
  &lt;span class="s1"&gt;"QPushButton:pressed { background: #3a3a3a; }"&lt;/span&gt;
  &lt;span class="s1"&gt;"QLabel { color: #e6e6e6; font-weight: 600; }"&lt;/span&gt;
&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can apply this style sheet to the main window like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="n"&gt;qwidget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetStyleSheet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;STYLE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or alternatively, you can make a new file named &lt;code&gt;style.qss&lt;/code&gt; and load it at runtime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;qss_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"src/shoppinglist/style.qss"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;qss_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;openFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qss_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qss_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readToEndAlloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allocator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;qwidget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetStyleSheet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;allocator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;qwidget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetStyleSheet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;STYLE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way you can easily tweak the styles without recompiling the app each time, additionally you can store this file anywhere on your system and allow users to customize the look of the app by providing their own style sheets if you want to get fancy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the Application
&lt;/h2&gt;

&lt;p&gt;Now we just need to show the window and start the event loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="n"&gt;qwidget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="mi"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qapplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And there we have it, a simple yet fully functional shopping list application, built entirely with &lt;code&gt;Zig&lt;/code&gt; and &lt;code&gt;Qt&lt;/code&gt; using direct bindings, with not a hint of &lt;code&gt;QML&lt;/code&gt; in sight. Sure, I opted to rely heavily on opaque pointers, trading off some type safety for convenience and rapid development, and I fully accept that tradeoff. For a more serious project, I would strongly recommend finding a way to make the code safer and more type-aware. Yet, despite these compromises, I was able to produce a complete, cross-platform GUI application in surprisingly little time and with minimal code. I honestly didn’t think it would be this straightforward. This is a testament both to the power of &lt;code&gt;Zig&lt;/code&gt; as a systems programming language and to the excellent work of &lt;code&gt;rcalixte&lt;/code&gt; in creating the &lt;code&gt;libqt6zig&lt;/code&gt; bindings.&lt;/p&gt;

&lt;p&gt;This wasn’t the only experiment I tried with &lt;code&gt;libqt6zig&lt;/code&gt;. I also built a minimal launcher for my &lt;code&gt;Hyperland&lt;/code&gt; setup, which presented some intriguing challenges related more to &lt;code&gt;Wayland&lt;/code&gt; than the library itself. With a mix of clever window flags, absolute positioning, and careful sizing, I managed to create a “floating” window that escaped &lt;code&gt;Hyperland&lt;/code&gt;’s tiling behavior. Thankfully, &lt;code&gt;libqt6zig&lt;/code&gt; is robust enough that I didn’t need to manipulate &lt;code&gt;XCB&lt;/code&gt; or &lt;code&gt;wlroots&lt;/code&gt; directly. The bindings handled everything for me, which made the process far smoother, though it required some trial and error.&lt;/p&gt;

&lt;p&gt;During these experiments, I also felt the absence of features like generics and compile-time reflection, which would have allowed for cleaner, more type-safe abstractions. While &lt;code&gt;Zig&lt;/code&gt; does offer &lt;code&gt;comptime&lt;/code&gt; capabilities, I couldn’t quite achieve what I envisioned. I also missed having access to a &lt;code&gt;QTNativeWindow&lt;/code&gt; abstraction, which would have simplified certain window management tasks. These limitations stem more from the current state of the bindings than from &lt;code&gt;Qt&lt;/code&gt; itself. I’ve discussed these points with the library’s author, who is aware and might implement them in the future. Until then, patience is key. We’re benefiting from the dedication of a single maintainer producing high-quality, low-level bindings for &lt;code&gt;Zig&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reflections on the Library
&lt;/h2&gt;

&lt;p&gt;Working with &lt;code&gt;libqt6zig&lt;/code&gt; was an eye-opening experience. Right from the start, I was impressed by how faithfully it exposes Qt’s C++ API while keeping the interface accessible to Zig developers. The library is surprisingly complete, covering most of the widgets, layouts, and common functionality you would expect from a typical Qt project. Despite being maintained by a single developer, the bindings are robust and consistent, which made experimentation smooth and enjoyable.&lt;/p&gt;

&lt;p&gt;One thing that stood out is how much control you get over the GUI elements. Every widget, layout, and property is explicit, which forces you to think about your UI structure carefully. Unlike higher-level frameworks that abstract away the details, &lt;code&gt;libqt6zig&lt;/code&gt; lets you handle memory management, widget hierarchies, and layout logic directly. This level of control can be intimidating at first, but it quickly becomes empowering once you get used to it. You feel like you are working with the core of the system rather than relying on layers of abstraction.&lt;/p&gt;

&lt;p&gt;At the same time, the library has some limitations. Working with opaque pointers can feel risky because it removes compile-time type safety, and the lack of certain abstractions like generics or a &lt;code&gt;QTNativeWindow&lt;/code&gt; wrapper means you occasionally need to write more boilerplate. However, these trade-offs are manageable, and the experience is educational. You learn not only how Qt works but also how to structure a cross-platform GUI application effectively in Zig.&lt;/p&gt;

&lt;p&gt;Overall, my experience with &lt;code&gt;libqt6zig&lt;/code&gt; was extremely positive. It made creating a fully functional shopping list application straightforward and even enjoyable. The library is well-documented, the API feels natural for Zig, and the bindings handle the low-level details so you can focus on building your application rather than wrestling with the underlying system. The potential for future improvements is exciting, and I look forward to seeing how the library evolves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In conclusion, using &lt;code&gt;Zig&lt;/code&gt; with &lt;code&gt;libqt6zig&lt;/code&gt; provides a powerful combination for building cross-platform GUI applications. While it requires attention to memory management and pointer safety, the explicitness and control make it a rewarding experience. You can produce functional, responsive, and visually appealing applications with surprisingly little code. &lt;/p&gt;

&lt;p&gt;For anyone interested in exploring GUI development in Zig, &lt;code&gt;libqt6zig&lt;/code&gt; is an excellent starting point. It demonstrates that Zig can be used effectively beyond systems programming, opening the door to lightweight, high-performance desktop applications. The library is already impressive, and with continued development, it could become a go-to choice for Zig developers looking to build modern GUIs. My experiments with both the shopping list application and my Hyperland launcher showed that even complex interactions are achievable with patience and careful design. Overall, this experience reinforced my enthusiasm for Zig and its growing ecosystem, and it leaves me optimistic about the future of GUI development in this language.&lt;/p&gt;

&lt;h2&gt;
  
  
  References and Resources
&lt;/h2&gt;

&lt;p&gt;If you want to explore &lt;code&gt;libqt6zig&lt;/code&gt; yourself or learn more about the library and its author, here are some useful links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/dayvster/zig-qt-shoppinglist" rel="noopener noreferrer"&gt;Shopping List App Source Code&lt;/a&gt; – The complete source code for the shopping list application built in this article.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/rcalixte/libqt6zig" rel="noopener noreferrer"&gt;libqt6zig GitHub Repository&lt;/a&gt; – The library used for these experiments.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/rcalixte" rel="noopener noreferrer"&gt;rcalixte GitHub Profile&lt;/a&gt; – The author and maintainer of libqt6zig.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://doc.qt.io/" rel="noopener noreferrer"&gt;Qt Documentation&lt;/a&gt; – Official Qt documentation for widgets, layouts, and styling.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ziglang.org/" rel="noopener noreferrer"&gt;Zig Programming Language&lt;/a&gt; – Learn more about Zig, its syntax, and features.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://doc.qt.io/qt-6/stylesheet-syntax.html" rel="noopener noreferrer"&gt;QSS Styling Guide&lt;/a&gt; – Qt's CSS-like styling system for widgets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Exploring these resources will help you get started with Zig GUI development and allow you to dive deeper into creating more complex applications.&lt;/p&gt;

</description>
      <category>zig</category>
      <category>programming</category>
    </item>
    <item>
      <title>I Miss when Software Ended</title>
      <dc:creator>Dayvster 🌊</dc:creator>
      <pubDate>Thu, 16 Oct 2025 14:03:37 +0000</pubDate>
      <link>https://dev.to/dayvster/i-miss-when-software-ended-43h</link>
      <guid>https://dev.to/dayvster/i-miss-when-software-ended-43h</guid>
      <description>&lt;h2&gt;
  
  
  We used to ship software that ended
&lt;/h2&gt;

&lt;p&gt;Remember when software had an end, like a movie or a book? You bought it, installed it, used it. There were no weekly updates, no monthly subscriptions, no constant notifications to update or accept frankly ever more intrusive permissions or ridiculous terms of service. Software was a product, not a service. You owned it, you controlled it. It was yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Happened?
&lt;/h2&gt;

&lt;p&gt;Somewhere along the way, software companies decided that they could make more money by turning software into a service. Instead of selling you a product, they would sell you a subscription. This meant that you would have to pay them every month or year to continue using their software. This also meant that they could update the software whenever they wanted.&lt;/p&gt;

&lt;p&gt;Now in my honest opinion this was mostly a good thing, because it meant that software could be updated without much fuss, bugs and issues could be patched out and in the early days of SaaS you'd mostly get more value for your money, because you'd get new features and improvements without having to buy a new version of the software. &lt;/p&gt;

&lt;p&gt;Back then when software was a full on product and delivery of updates was not as seamless as it is today, you'd often have to wait quite a while for a patch to get released and available for download or purchase via service packs or even worst case scenario new versions of the software. Think Photoshop 6 to Photoshop 7, or Windows XP to Windows Vista. &lt;/p&gt;

&lt;p&gt;So SaaS did solve a really great problem there where you did not have to be locked to a certain version of the software with all it's bugs, issues, security flaws, or just lack of features. &lt;/p&gt;

&lt;h2&gt;
  
  
  When Updates Stopped feeling like progress
&lt;/h2&gt;

&lt;p&gt;However over time the promise got blurry, updates stopped feeling like more value delivered to the end user and more like obligations to keep the service running. Updates became more frequent, more intrusive and more often than not they would break things rather than fix them.&lt;/p&gt;

&lt;p&gt;Features quietly got removed, or hidden behind more premium paywalls so that companies could incentivize users to slowly upgrade to more expensive plans. Instead of delivering valuable updates or features SaaS was now used to fine tune companies revenue streams. &lt;/p&gt;

&lt;p&gt;And at this point I was still okay with it, because as an end user I still got value for my money, it cost more money sure but the way most companies structured their plans it was negligible for most common users.&lt;/p&gt;

&lt;p&gt;Can't fault a company for trying to make money off of their work right? As long as they pay their developers fairly and deliver a good product I don't see a problem with it.&lt;/p&gt;

&lt;p&gt;But it still felt odd because we were essentially renting software rather than owning it, we no longer bought a product but rather a service that was completely in the hands of the provider and for a while that relationship was pretty fair and balanced.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Subscription Model got out of hand
&lt;/h2&gt;

&lt;p&gt;But then came the era of "everything subscription". Suddenly every single piece of software you used was a subscription. No matter how small or trivial the functionality was it would sooner or later succumb to the SaaS demon. &lt;/p&gt;

&lt;p&gt;This is how we got simple things like Postman, Git clients, Email clients and in some extreme cases even very basic text editors that we suddenly had to pay a monthly fee to use.&lt;/p&gt;

&lt;p&gt;This is where I started to feel uneasy about the whole SaaS model, because no longer did the value provided to the end user justify the cost of the subscription. Do you as a user really need all the features that the $14 a month postman plan provides? Or do you really need to pay $5 a month for a git client how about that $20 a month email client? &lt;/p&gt;

&lt;p&gt;The price is not even my main problem, I want to support all of these companies and their developers, I want them to be able to make a living off of their work, but they do not offer me a way to pay them fairly.&lt;/p&gt;

&lt;p&gt;All of the examples above have free tiers and then the next payed tier is a monthly subscription filled to the brim with stuff that I will very very rarely need. &lt;/p&gt;

&lt;p&gt;I would love to be able to pay a one time fee of whatever their software is worth to them and I'd happily be locked at a certain version of their software that I can use for as long as I want. But I am not allowed to do that because the subscription model does not allow for it.&lt;/p&gt;

&lt;p&gt;I have to either use the free tier or pay a monthly subscription filled with stuff that I do not really need or want.&lt;/p&gt;

&lt;p&gt;But the worst part is yet to come.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lack of Ownership means Lack of Control
&lt;/h2&gt;

&lt;p&gt;This is where the SaaS model really starts to fall apart for me, because I no longer own the software I use. I’m merely renting it. And as a renter, I have no real control.&lt;/p&gt;

&lt;p&gt;The software we rely on for our work, our hobbies, and sometimes even our ability to function day to day can be taken away at any time for any reason. A company can shut it down, change the pricing, or rewrite the terms of service overnight. If you don’t like it, you’re out.&lt;/p&gt;

&lt;p&gt;In some extreme cases, users have been forced to waive their basic consumer rights just to keep using software or the hardware it runs on. Disney, for instance, once argued in court that by using their streaming service you agreed not to sue them even if they acted illegally or unethically. They actually tried to use that argument to dodge a lawsuit from a man whose wife died in Disneyland after an allergic reaction, even though the staff had been warned about her allergy.&lt;/p&gt;

&lt;p&gt;That’s the level of power imbalance we’ve created. Software you rent can demand that you accept terms that are questionable at best and dangerous at worst, and your only alternative is to lose access to something you depend on.&lt;/p&gt;

&lt;p&gt;This is not what SaaS was meant to be. The original idea was to deliver more value, not to take away control or ownership. SaaS should have evolved into a model of better service, not deeper dependency.&lt;/p&gt;

&lt;p&gt;And now, there’s a new layer to it. Companies like Adobe and Notion quietly use your work, your data, and your creations to train their AI models. All justified by a single line in an update that you “agreed” to when you clicked through just to get back to work.&lt;/p&gt;

&lt;p&gt;That’s not progress. That’s permission laundering.&lt;/p&gt;

&lt;p&gt;So where does this leave us? Somewhere between the promise SaaS once held and the reality many of us experience today. The model itself isn’t broken, but the way it’s often applied is. Updates, subscriptions, and cloud features should be tools to deliver value, not mechanisms to extract it.&lt;/p&gt;

&lt;p&gt;Maybe the answer isn’t abandoning SaaS entirely. Maybe it’s building a model that respects both the user and the developer, giving people choice, stability, and control while still funding ongoing improvements.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Third More Reasonable Way?
&lt;/h2&gt;

&lt;p&gt;There’s a middle ground that gets mostly ignored in today’s software landscape. It’s simple: give users choice. Let people decide how they want to interact with your product.&lt;/p&gt;

&lt;p&gt;Some users are perfectly fine renting access, staying on the bleeding edge, and paying for convenience. Others just want to own the software they rely on, lock in a version, and use it indefinitely without worrying about subscriptions, sudden paywalls, or disappearing features.&lt;/p&gt;

&lt;p&gt;Imagine a model where the same software offers both paths:&lt;/p&gt;

&lt;p&gt;A one-time purchase option for users who are happy to stay on version X. No forced upgrades. No subscription. Just a finished product they can control.&lt;/p&gt;

&lt;p&gt;A subscription option for users who want continuous updates, cloud features, and new tools as soon as they’re ready.&lt;/p&gt;

&lt;p&gt;This approach respects both sides. It honors the nostalgic value of software as a finished product while keeping the benefits of SaaS alive. Users get what they need, developers get paid fairly, and everyone keeps a sense of control.&lt;/p&gt;

&lt;p&gt;It’s not about rejecting SaaS. It’s about rebalancing it. Making updates and subscriptions tools for value, not leverage. Let people pay for what they actually want, and stop using access as a weapon.&lt;/p&gt;

&lt;p&gt;Software can still evolve. SaaS can still be great. But it doesn’t have to come at the cost of ownership, autonomy, or trust.&lt;/p&gt;

&lt;p&gt;This approach isn’t just about giving people options. It’s about &lt;strong&gt;returning control&lt;/strong&gt; to the users, respecting the work they create, and protecting their data. Ownership and privacy aren’t optional features, they are the foundation of trust between software and the people who rely on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reclaiming What’s Ours
&lt;/h2&gt;

&lt;p&gt;Software belongs to the people who bought it. Full stop. We should control our tools, our work, and our data without being coerced or trapped by subscriptions, paywalls, or shady terms. Ownership and privacy are not optional conveniences, they are fundamental rights.&lt;/p&gt;

&lt;p&gt;I am asking for a software world where users are not just passive consumers. Let us own what we create. Let us decide if we want to stay on a version indefinitely or pay for updates on our terms. Let companies innovate without taking control from the people who depend on their products.&lt;/p&gt;

&lt;p&gt;This is not a plea for nostalgia. It is a demand for respect, trust, and autonomy. SaaS can be powerful, but it must not come at the cost of ownership or privacy. Users deserve tools that serve them, not cages disguised as convenience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving Forward
&lt;/h2&gt;

&lt;p&gt;We don’t have to accept a software landscape where ownership, control, and privacy are optional. Developers and users can coexist in a fairer ecosystem, one where innovation thrives without taking advantage of those who rely on the software.&lt;/p&gt;

&lt;p&gt;If you’re a developer, consider offering choice. Let users pay for updates if they want them, but don’t trap those who are happy with a stable version. Respect the work your users create and give them control over their data.&lt;/p&gt;

&lt;p&gt;If you’re a user, don’t settle for losing ownership. Support companies that value transparency, respect privacy, and give you options. Demand software that works for you, not against you.&lt;/p&gt;

&lt;p&gt;SaaS can still be powerful, flexible, and convenient. But it should serve the people who use it, not turn them into passive renters of their own tools. Let’s build a future where software ends gracefully, respectfully, and on our terms, while still evolving for those who want it to.&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>Accidentally Made a Zig dotenv Parser</title>
      <dc:creator>Dayvster 🌊</dc:creator>
      <pubDate>Tue, 07 Oct 2025 19:00:00 +0000</pubDate>
      <link>https://dev.to/dayvster/accidentally-made-a-zig-dotenv-parser-311f</link>
      <guid>https://dev.to/dayvster/accidentally-made-a-zig-dotenv-parser-311f</guid>
      <description>&lt;p&gt;Recently I've made a Zig based CLI argument parser called &lt;a href="https://github.com/dayvster/argh" rel="noopener noreferrer"&gt;argh&lt;/a&gt; I've even written myself a roadmap I'd like to follow with this project. And so far I have been following it pretty well. However last week I began working on the next item on my roadmap which was to add support for environment variables to the arg parser, where you could set an environment variable to set the value of a flag. &lt;/p&gt;

&lt;p&gt;Not many use cases for it I know it's more of an edge case but I thought it would be a fun exercise to implement that. So I started working on it. I carefully scaffolded out the feature what functions I'd like to see in it how they should behave what guard rails I'd like to set to ensure type safety, memory safety, and so on.&lt;/p&gt;

&lt;p&gt;As I was implementing all the things I scaffolded out one by one I realized that I was basically writing a fully fledged dotenv parser. And I thought to myself "hey this is pretty cool I should probably make this a separate library so other people can use it too" and it's not deeply coupled to the arg parser so it makes sense to do that. So I did. I extracted the code out into its own repository called &lt;a href="https://github.com/dayvster/zdotenv" rel="noopener noreferrer"&gt;zdotenv&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Library
&lt;/h2&gt;

&lt;p&gt;The dotenv parser itself is pretty simple to use and I've even provided a basic example and tests in my repository that you can check out to see how it works. But regardless here's a quick example of how to use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;@import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"std"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Dotenv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;@import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dotenv"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="py"&gt;Dotenv&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;allocator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;page_allocator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allocator&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deinit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;_&lt;/span&gt;&lt;span class="p"&gt;][]&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"./examples/example.env"&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"FOO"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"FOO={s}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"FOO not found&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will load the environment variables from the specified file and you can then access them using the &lt;code&gt;get&lt;/code&gt; method. The &lt;code&gt;load&lt;/code&gt; method takes an array of file paths so you can load multiple dotenv files if you want to.&lt;/p&gt;

&lt;p&gt;That's it, that's all there is to it. It's simple and straightforward as a dotenv parser should ideally be.&lt;/p&gt;

&lt;p&gt;I've also added support for specifying custom .env file names since not everyone uses the default &lt;code&gt;.env&lt;/code&gt; file all the time, you may wanna dynamically load different env files based on the environment you're in (development, staging, production, etc). &lt;/p&gt;

&lt;p&gt;I've added support for comments, blank lines and values in double quotes. So it should cover most use cases out there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Loads key-value pairs from &lt;code&gt;.env&lt;/code&gt;&lt;/strong&gt; basically the core feature it does that it says, it parses an &lt;code&gt;.env&lt;/code&gt; file and loads the key-value pairs into memory so that you can access them later via the &lt;code&gt;get&lt;/code&gt; method.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Supports comments and blank lines&lt;/strong&gt; it ignores comments and blank lines so you can have a well formatted &lt;code&gt;.env&lt;/code&gt; file without any issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Provides easy access to values via hash map&lt;/strong&gt; you can access the values using the &lt;code&gt;get&lt;/code&gt; method which returns an optional value so you can handle the case where the key doesn't exist. I picked a hash map for this since generally speaking your &lt;code&gt;.env&lt;/code&gt; files won't ever be that large so we can just store them in memory for easy and fast access.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Memory safe&lt;/strong&gt; all allocations are freed with a &lt;code&gt;deinit&lt;/code&gt; method so you don't have to worry about memory leaks.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Well that's it for now. I'll be adding additional functionality and patches to the library as I see fit. But for now it's a pretty solid and usable dotenv parser that you can use in your Zig projects. I for one have already used it in my &lt;code&gt;notes CLI&lt;/code&gt; application I made a while back to compare Zig against Rust and it works like a charm.&lt;/p&gt;

&lt;p&gt;As always I welcome contributions and feedback so if you have any ideas or suggestions feel free to open an issue or a pull request on the repository.&lt;/p&gt;

&lt;p&gt;And if you have any questions or would like to reach out to me you can find me on &lt;a href="https://twitter.com/dayvsterdev" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; &lt;/p&gt;

</description>
      <category>zig</category>
      <category>programming</category>
      <category>discuss</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Is Odin Just a More Boring C</title>
      <dc:creator>Dayvster 🌊</dc:creator>
      <pubDate>Sat, 04 Oct 2025 15:21:49 +0000</pubDate>
      <link>https://dev.to/dayvster/is-odin-just-a-more-boring-c-9j7</link>
      <guid>https://dev.to/dayvster/is-odin-just-a-more-boring-c-9j7</guid>
      <description>&lt;h2&gt;
  
  
  Why I Tried Odin
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Background
&lt;/h3&gt;

&lt;p&gt;My recent posts have been diving deep into Zig and C, a shift from my earlier focus on React and JavaScript. This isn’t a pivot but a return to my roots. I started programming at 13 with C and C++, and over the years, I’ve built a wide range of projects in systems programming languages like C, C++, Rust, and now Zig. From hobby experiments and custom Linux utilities to professional embedded systems work think vehicle infotainment, tracking solutions, and low-level components I’ve always been drawn to the power and precision of systems programming. Alongside this, I’ve crafted tools for my own environment and tackled plenty of backend engineering, blending my full-stack expertise with a passion for low-level control.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Odin Caught My Eye
&lt;/h3&gt;

&lt;p&gt;I like many others initially dismissed Odin as that language that was primarily intended for game development. It took me a moment or should I say many moments to realize just how stupid that notion was. Because let's analyze what game development actually means, it means building complex systems that need to be efficient, performant and reliable. It means working with graphics, physics, input handling, networking and more. It means dealing with concurrency, memory management and low level optimizations. In other words, game development is a perfect fit for a systems programming language like Odin.&lt;/p&gt;

&lt;p&gt;So basically if it's intended for game development, then it should be a great fit for general systems programming, desktop applications and since game dev usually means manual memory management without a garbage collector, it should also be possible to some extent to use it for embedded systems. &lt;/p&gt;

&lt;p&gt;So after I've gave myself a good slap on the forehead for being a bit of an idiot. I decided why not give Odin a fair shot and build something useful with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Project
&lt;/h2&gt;

&lt;p&gt;Now I may have been a bit liberal with the word useful there, what I actually decided to build was something that I usually like to build whenever I wanna try out a new language, namely a tiny key-value store with a pub/sub system. It won't win any awards for originality and I'm pretty sure the folks over at redis aren't exactly shaking in their boots. It is the most basic most barebones implementation of both lacking any real useful features that would make it usable in a production environment. But it is a good exercise in understanding the language and its capabilities.&lt;/p&gt;

&lt;p&gt;Mainly because it involves a few different aspects of programming that are relevant to systems programming. It involves data structures, memory management, concurrency and networking. &lt;/p&gt;

&lt;p&gt;And even if you create something as basic and lacking as I have in this example, you still have room for experimentation and exploration to add more features. &lt;/p&gt;

&lt;h3&gt;
  
  
  Building a Tiny KV Store With Pub/Sub
&lt;/h3&gt;

&lt;p&gt;My initial minimal proof of concept was simple and straightforward.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import "core:fmt"
import "core:time"

KVStore :: struct {
    store: map[string]string,
}

kvstore_init :: proc() -&amp;gt; KVStore {
    return KVStore{store = map[string]string{}}
}

kv_put :: proc(kv: ^KVStore, key: string, value: string) {
    kv.store[key] = value
}

kv_get :: proc(kv: ^KVStore, key: string) -&amp;gt; string {
    if value, ok := kv.store[key]; ok {
        return value
    }
    return ""
}

PubSub :: struct {
    subscribers: map[string][]proc(msg: string),
}

pubsub_init :: proc() -&amp;gt; PubSub {
    return PubSub{subscribers = map[string][]proc(msg: string){}}
}

subscribe :: proc(ps: ^PubSub, topic: string, handler: proc(msg: string)) {
    if arr, ok := ps.subscribers[topic]; ok {
        new_arr := make([]proc(msg: string), len(arr)+1);
        for i in 0..&amp;lt;len(arr) {
            new_arr[i] = arr[i];
        }
        new_arr[len(arr)] = handler;
        ps.subscribers[topic] = new_arr;
    } else {
        ps.subscribers[topic] = []proc(msg: string){handler};
    }
}

publish :: proc(ps: ^PubSub, topic: string, msg: string) {
    if handlers, ok := ps.subscribers[topic]; ok {
        for handler in handlers {
            handler(msg);
        }
    }
}

kv: KVStore;

main :: proc() {
    kv = kvstore_init();
    ps := pubsub_init();

    handler1 :: proc(msg: string) {
        fmt.println("Sub1 got:", msg);
        kv_put(&amp;amp;kv, "last_msg", msg);
    }

    handler2 :: proc(msg: string) {
        fmt.println("Sub2 got:", msg);
    }

    handler3 :: proc(msg: string) {
        fmt.println("Sub3 got:", msg);
    }

    subscribe(&amp;amp;ps, "demo", handler1);
    subscribe(&amp;amp;ps, "demo", handler2);
    subscribe(&amp;amp;ps, "demo", handler3);

    publish(&amp;amp;ps, "demo", "Welcome to dayvster.com");

    time.sleep(2 * time.Second);
    publish(&amp;amp;ps, "demo", "Here's another message after 2 seconds");

    last := kv_get(&amp;amp;kv, "last_msg");
    fmt.println("Last in kvstore:", last);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see it currently lacks any real error handling, concurrency and persistence. But it does demonstrate the basic functionality of a key-value store with pub/sub capabilities. What I have done is created two main structures, &lt;code&gt;KVStore&lt;/code&gt; and &lt;code&gt;PubSub&lt;/code&gt;. The &lt;code&gt;KVStore&lt;/code&gt; structure contains a map to store key-value pairs and provides functions to put and get values. The &lt;code&gt;PubSub&lt;/code&gt; structure contains a map of subscribers for different topics and provides functions to subscribe to topics and publish messages.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;main&lt;/code&gt; function initializes the key-value store and pub/sub system, defines a few handlers for incoming messages, subscribes them to a topic, and then publishes some messages to demonstrate the functionality.&lt;/p&gt;

&lt;p&gt;From this basic example we've explored how to handle memory management in Odin, how to work with data structures like maps and slices, and how to define and use procedures. &lt;/p&gt;

&lt;h3&gt;
  
  
  Memory Management
&lt;/h3&gt;

&lt;p&gt;Like C and Zig, Odin employs manual memory management, but it offers user-friendly utilities to streamline the process, much like Zig, in contrast to C’s more rudimentary approach. For instance, the &lt;code&gt;make&lt;/code&gt; function in Odin enables the creation of slices with a defined length and capacity, akin to Zig’s slice allocation. In the code above, &lt;code&gt;make([]proc(msg: string), len(arr)+1)&lt;/code&gt; generates a slice of procedure pointers with a length of &lt;code&gt;len(arr)+1&lt;/code&gt;. Essentially, it allocates memory on the heap and returns a slice header, which includes a pointer to the allocated memory, along with the length and capacity of the slice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;but how and when is that memory freed?&lt;/strong&gt; In this code, memory allocated by &lt;code&gt;make&lt;/code&gt; (e.g., for the slice in &lt;code&gt;subscribe&lt;/code&gt;) and for maps (e.g., &lt;code&gt;kv.store&lt;/code&gt; and &lt;code&gt;ps.subscribers&lt;/code&gt;) is not explicitly freed. Since this is a short-lived program, the memory is reclaimed by the operating system when the program exits. However, in a long-running application, you’d need to use Odin’s delete procedure to free slices and maps explicitly. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
kvstore_deinit :: proc(kv: ^KVStore) {
    delete(kv.store);
}

pubsub_deinit :: proc(ps: ^PubSub) {
    for topic, handlers in ps.subscribers {
        delete(handlers);
    }
    delete(ps.subscribers);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So let's add that in the &lt;code&gt;main&lt;/code&gt; function before it exits to ensure we clean up properly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ... existing code ...
main :: proc() {
    // ... existing code ...
    pubsub_deinit(&amp;amp;ps);
    kvstore_deinit(&amp;amp;kv);
} // end of main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well would you look at that, we just added proper memory management to our tiny KV store with pub/sub system and all it took was a few lines of code. I'm still a huge fan of C but this does feel nice and clean, not to mention really readable and easy to understand.&lt;/p&gt;

&lt;p&gt;Is our code now perfect and fully memory safe? Not quite, it still needs error handling and thread safety(way later) for production use, but it’s a solid step toward responsible memory management.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding concurrency
&lt;/h3&gt;

&lt;p&gt;Enhancing Pub/Sub with Concurrency in Odin&lt;/p&gt;

&lt;p&gt;To make our pub/sub system more realistic, we've introduced concurrency to the publish procedure using Odin's core:thread library. This allows subscribers to process messages simultaneously, mimicking real-world pub/sub behavior. Since handler1 modifies kv.store via kv_put, we've added a mutex to KVStore to ensure thread-safe access to the shared map. Here's how it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Concurrent Execution with Threads&lt;/strong&gt;: The publish procedure now runs each handler in a separate thread created with thread.create. Each thread receives the handler and message via t.user_args, and thread.start kicks off execution. Threads are collected in a dynamic array (threads), which is cleaned up using defer delete(threads). The thread.join call ensures the program waits for all threads to finish, and thread.destroy frees thread resources. This setup enables handler1, handler2, and handler3 to process messages concurrently, with output order varying based on thread scheduling.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Thread Safety with Mutex&lt;/strong&gt;: Since handler1 updates kv.store via kv_put, concurrent access could lead to race conditions, as Odin's maps aren't inherently thread-safe. To address this, a sync.Mutex is added to KVStore. The kv_put and kv_get procedures lock the mutex during map access, ensuring only one thread modifies or reads kv.store at a time. The mutex is initialized in kvstore_init and destroyed in kvstore_deinit.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;publish :: proc(ps: ^PubSub, topic: string, msg: string) {
    if handlers, ok := ps.subscribers[topic]; ok {
        threads := make([dynamic]^thread.Thread, 0, len(handlers))
        defer delete(threads)

        // Allocate ThreadArgs for each handler
        thread_args := make([dynamic]^ThreadArgs, 0, len(handlers))
        defer {
            for args in thread_args {
                free(args)
            }
            delete(thread_args)
        }

        for handler in handlers {
            msg_ptr := new(string)
            msg_ptr^ = msg
            t := thread.create(proc(t: ^thread.Thread) {
                handler := cast(proc(msg: string)) t.user_args[0]
                msg_ptr := cast(^string) t.user_args[1]
                handler(msg_ptr^)
                free(msg_ptr)
            })
            t.user_args[0] = rawptr(handler)
            t.user_args[1] = rawptr(msg_ptr)
            thread.start(t)
            append(&amp;amp;threads, t)
        }

        for t in threads {
            thread.join(t)
            thread.destroy(t)
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This implementation adds concurrency by running each handler in its own thread, allowing parallel message processing. The mutex ensures thread safety for kv.store updates in handler1, preventing race conditions. Odin's core:thread library simplifies thread management, offering a clean, pthread-like experience. &lt;/p&gt;

&lt;p&gt;Odin’s threading feels like a bit like C’s pthreads but without the usual headache, and it’s honestly a breeze to read and write. For this demo, the mutex version keeps everything nice and tidy, However in a real application, you'd still want to consider more robust error handling and possibly a thread pool for efficiency and also some way to handle thread lifecycle and errors and so on...&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Persistence
&lt;/h2&gt;

&lt;p&gt;I haven't added persistence to this code-block personally because I feel that would quickly spiral the demo that I wanted to keep simple and focused into something much more complex. But if you wanted to add persistence, you could use Odin's &lt;code&gt;core:file&lt;/code&gt; library to read and write the &lt;code&gt;kv.store&lt;/code&gt; map to a file. You would need to serialize the map to a string format (like &lt;code&gt;JSON&lt;/code&gt; or &lt;code&gt;CSV&lt;/code&gt;) when saving and deserialize it when loading. Luckily odin has  &lt;code&gt;core:encoding/json&lt;/code&gt; and &lt;code&gt;core:encoding/csv&lt;/code&gt; libraries that can help with this. Which should at the very least make that step fairly trivial.&lt;/p&gt;

&lt;p&gt;So if you feel like it, give it a shot and let me know how it goes. Do note that this step is a lot harder than it may seem especially if you want to do it properly and performantly. &lt;/p&gt;

&lt;h2&gt;
  
  
  Now to Compile and Run
&lt;/h2&gt;

&lt;p&gt;Now here's the thing the first time I ran &lt;code&gt;odin build .&lt;/code&gt; I thought I messed up somewhere because, it basically took a split second and produced no output no warnings no nothing. But I did see that a binary was produced named after the folder I was in. So I ran it with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ ./kvpub
Sub1 got: Welcome to dayvster.com
Sub2 got: Welcome to dayvster.com
Sub3 got: Welcome to dayvster.com
Sub1 got: Here&lt;span class="s1"&gt;'s another message after 2 seconds
Sub2 got: Here'&lt;/span&gt;s another message after 2 seconds
Sub3 got: Here&lt;span class="s1"&gt;'s another message after 2 seconds
Last in kvstore: Here'&lt;/span&gt;s another message after 2 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And there you have it, a tiny key-value store with pub/sub capabilities built in Odin. That compiled bizarrely fast, in fact I used a util (&lt;a href="https://github.com/dayvster/pulse" rel="noopener noreferrer"&gt;pulse&lt;/a&gt;) I wrote to benchmark processes and their execution time and it clocked in at a blazing 0.4 seconds to compile&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ pulse &lt;span class="nt"&gt;--benchmark&lt;/span&gt; &lt;span class="nt"&gt;--cmd&lt;/span&gt; &lt;span class="s1"&gt;'odin build .'&lt;/span&gt; &lt;span class="nt"&gt;--runs&lt;/span&gt; 3

┌──────────────┬──────┬─────────┬─────────┬─────────┬───────────┬────────────┐
│ Command      ┆ Runs ┆ Avg &lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; ┆ Min &lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; ┆ Max &lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; ┆ Max CPU%  ┆ Max RAM MB │
╞══════════════╪══════╪═════════╪═════════╪═════════╪═══════════╪════════════╡
│ odin build &lt;span class="nb"&gt;.&lt;/span&gt; ┆ 3    ┆ 0.401   ┆ 0.401   ┆ 0.401   ┆ 0.00      ┆ 0.00       │
└──────────────┴──────┴─────────┴─────────┴─────────┴───────────┴────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well I couldn't believe that so I ran it again this time with &lt;code&gt;--runs 16&lt;/code&gt; to get a better average and it still came in at a very respectable &lt;code&gt;0.45&lt;/code&gt; (MAX) seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OK that is pretty impressive.&lt;/strong&gt; but consistent maybe my tool is broken? I'm not infallible after all. So I re-confirmed it why &lt;code&gt;hyperfine&lt;/code&gt; and it came out at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ hyperfine &lt;span class="s2"&gt;"odin build ."&lt;/span&gt;
Benchmark 1: odin build &lt;span class="nb"&gt;.&lt;/span&gt;
  Time &lt;span class="o"&gt;(&lt;/span&gt;mean ± σ&lt;span class="o"&gt;)&lt;/span&gt;:     385.1 ms ±  12.5 ms    &lt;span class="o"&gt;[&lt;/span&gt;User: 847.1 ms, System: 354.6 ms]
  Range &lt;span class="o"&gt;(&lt;/span&gt;min … max&lt;span class="o"&gt;)&lt;/span&gt;:   357.3 ms … 400.1 ms    10 runs

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;God damn that is fast, now I know the program is tiny and simple but still that is impressive and makes me wonder how it would handle a larger codebase. Please if you have any feedback or insights on this let me know I am really curious.&lt;/p&gt;

&lt;p&gt;just for sanitysake I also ran &lt;code&gt;time odin build .&lt;/code&gt; and it came out at you've guessed it &lt;code&gt;0.4&lt;/code&gt; seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Right so it's fast, but how's the experience?
&lt;/h3&gt;

&lt;p&gt;Well I have to say it was pretty smooth overall. The compiler is fast and the error messages are generally clear and helpful if not perhaps a bit... verbose for my taste&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For example&lt;/strong&gt; I've intentionally introduced a simple typo in the &lt;code&gt;map&lt;/code&gt; keyword and named is &lt;code&gt;masp&lt;/code&gt; to showcase what I mean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ odin build &lt;span class="nb"&gt;.&lt;/span&gt;
/home/dave/Workspace/TMP/odinest/main.odin&lt;span class="o"&gt;(&lt;/span&gt;44:31&lt;span class="o"&gt;)&lt;/span&gt; Error: Expected an operand, got &lt;span class="o"&gt;]&lt;/span&gt;
        subscribers: masp[string][]proc&lt;span class="o"&gt;(&lt;/span&gt;msg: string&lt;span class="o"&gt;)&lt;/span&gt;,
                                  ^
/home/dave/Workspace/TMP/odinest/main.odin&lt;span class="o"&gt;(&lt;/span&gt;44:32&lt;span class="o"&gt;)&lt;/span&gt; Syntax Error: Expected &lt;span class="s1"&gt;'}'&lt;/span&gt;, got &lt;span class="s1"&gt;'proc'&lt;/span&gt;
        subscribers: masp[string][]proc&lt;span class="o"&gt;(&lt;/span&gt;msg: string&lt;span class="o"&gt;)&lt;/span&gt;,
                                   ^
/home/dave/Workspace/TMP/odinest/main.odin&lt;span class="o"&gt;(&lt;/span&gt;44:40&lt;span class="o"&gt;)&lt;/span&gt; Syntax Error: Expected &lt;span class="s1"&gt;')'&lt;/span&gt;, got &lt;span class="s1"&gt;':'&lt;/span&gt;
        subscribers: masp[string][]proc&lt;span class="o"&gt;(&lt;/span&gt;msg: string&lt;span class="o"&gt;)&lt;/span&gt;,
                                           ^
/home/dave/Workspace/TMP/odinest/main.odin&lt;span class="o"&gt;(&lt;/span&gt;44:41&lt;span class="o"&gt;)&lt;/span&gt; Syntax Error: Expected &lt;span class="s1"&gt;';'&lt;/span&gt;, got identifier
        subscribers: masp[string][]proc&lt;span class="o"&gt;(&lt;/span&gt;msg: string&lt;span class="o"&gt;)&lt;/span&gt;,
                                            ^
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I chose specifically this map because I wanted to showcase how Odin handles errors when you try to build, it could simply say &lt;code&gt;Error: Unknown type 'masp'&lt;/code&gt; but instead it goes on to produce 4 separate errors that all stem from the same root cause. This is obviously because the parser gets confused and can't make sense of the code anymore. So essentially you get every single error that results from the initial mistake even if they are on the same line. Now would I love to see them condensed into a single error message? Because it stems from the same line and the same root cause? Yes I would. But that's just my personal preference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Odin Shines
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Simplicity and Readability
&lt;/h3&gt;

&lt;p&gt;Odin kinda feels like a modernized somehow even more boring C but in the best way possible. It's simple, straightforward and easy to read. It does not try to have some sort of clever syntax or fancy features, it really feels like a no-nonsense no frills language that wants you to start coding and being productive as quickly as possible.&lt;/p&gt;

&lt;p&gt;In fact this brings me to my next point.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Built in Libraries Galore
&lt;/h3&gt;

&lt;p&gt;I was frankly blown away with just how much is included in the standard and vendored(more on that later) libraries. I mean it has everything you'd expect from a modern systems programming language but it also comes with a ton of complete data structures, algorithms and utilities that you would usually have to rely on third-party libraries for in C or even Zig.&lt;/p&gt;

&lt;p&gt;For more info just look at &lt;a href="https://pkg.odin-lang.org/core/" rel="noopener noreferrer"&gt;Odin's Core Library&lt;/a&gt; and I mean really look at it and read it do not just skim it. Here's an example &lt;a href="https://pkg.odin-lang.org/core/flags/" rel="noopener noreferrer"&gt;flags&lt;/a&gt; which is a complete command line argument parser, or even &lt;a href="https://pkg.odin-lang.org/core/container/rbtree/" rel="noopener noreferrer"&gt;rbtree&lt;/a&gt; which is a complete implementation of a red-black tree data structure that you can just import and use right away&lt;/p&gt;

&lt;p&gt;But what really blew me away was&lt;/p&gt;

&lt;h3&gt;
  
  
  The Built in Vendor Libraries / Packages
&lt;/h3&gt;

&lt;p&gt;Odin comes with a set of vendor libraries that basically give you useful bindings to stuff like &lt;code&gt;SDL2/3&lt;/code&gt;, &lt;code&gt;OpenGL&lt;/code&gt;, &lt;code&gt;Vulkan&lt;/code&gt;, &lt;code&gt;Raylib&lt;/code&gt;, &lt;code&gt;DirectX&lt;/code&gt; and more. This is really impressive because it means you can start building games or graphics applications right away without having to worry about setting up bindings or dealing with C interop. Now I'm not super sure if these vendor bindings are all maintained and created by the Odin team from what I could gather so far, it would certainly seem so but I could be wrong. If you know more about this please let me know. &lt;/p&gt;

&lt;p&gt;But all that aside these bindings are really well done and easy to use. For example here's how you can create a simple window with SDL2 in Odin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import sdl "vendor:sdl2"

main :: proc() {
    sdl.Init(sdl.INIT_VIDEO)
    defer sdl.Quit()

    window := sdl.CreateWindow(
        "Odin SDL2 Black Window",
        sdl.WINDOWPOS_CENTERED,
        sdl.WINDOWPOS_CENTERED,
        800,
        600,
        sdl.WINDOW_SHOWN,
    )
    defer sdl.DestroyWindow(window)

    renderer := sdl.CreateRenderer(window, -1, sdl.RENDERER_ACCELERATED)
    defer sdl.DestroyRenderer(renderer)

    event: sdl.Event
    running := true
    for running {
        for sdl.PollEvent(&amp;amp;event) {
            if event.type == sdl.EventType.QUIT {
                running = false
            }
        }

        sdl.SetRenderDrawColor(renderer, 0, 0, 0, 255) 
        sdl.RenderClear(renderer) 
        sdl.RenderPresent(renderer) 
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code creates a simple window with a black background using SDL2. It's pretty straightforward and easy to understand, especially if you're already familiar with SDL2 or SDL3.&lt;/p&gt;

&lt;h3&gt;
  
  
  C Interop
&lt;/h3&gt;

&lt;p&gt;Odin makes it trivially easy to interop with C libraries, as long as that. This is done via their &lt;code&gt;foreign import&lt;/code&gt; where you'd create an import name and link to the library file and &lt;code&gt;foreign&lt;/code&gt; blocks to link to declared individual function or types.&lt;/p&gt;

&lt;p&gt;I could explain it with examples here but Odin's own documentation does a way better job and will keep this post from getting even longer than it already is. So please check out &lt;a href="https://odin-lang.org/news/binding-to-c/" rel="noopener noreferrer"&gt;Odin's C interop&lt;/a&gt; documentation for more info.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Odin Feels Awkward
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Standard Library Gaps
&lt;/h3&gt;

&lt;p&gt;While Odin's standard library is quite comprehensive, there are still some gaps and missing features that can make certain tasks more cumbersome. For example, while it has basic file I/O capabilities, it lacks more advanced features like file watching or asynchronous I/O. Additionally, while it has a decent set of data structures, it lacks some more specialized ones like tries or bloom filters I'd also love to see a b+ tree implementation in the core library. &lt;/p&gt;

&lt;p&gt;But those are at most nitpicks and finding third-party libraries or writing your own implementations is usually straightforward.&lt;/p&gt;

&lt;p&gt;However...&lt;/p&gt;

&lt;h3&gt;
  
  
  No Package Manager
&lt;/h3&gt;

&lt;p&gt;I really like languages that come with their own package manager, it makes it so much easier to discover, install and manage third-party libraries / dependencies. Odin currently lacks a built-in package manager, which means you have to manually download and include third-party libraries in your projects. This can be a bit of a hassle, especially I'd imagine for larger projects with multiple dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Smaller Nitpicks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;dir inconsistencies&lt;/strong&gt;: I love how it auto named my binary after the folder I was in but I wish it did the same whenever I ran &lt;code&gt;odin run&lt;/code&gt; and &lt;code&gt;odin build&lt;/code&gt; I had to explicitly specify &lt;code&gt;odin run .&lt;/code&gt; and &lt;code&gt;odin build .&lt;/code&gt; that felt a bit inconsistent to me because if it knows the folder we are in why not just use that as the default value when we wanna tell it to run or build in the current directory?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Error messages&lt;/strong&gt;: As mentioned earlier, while Odin's error messages are generally clear, they can sometimes be overly verbose, especially when multiple errors stem from a single root cause. It would be nice to see more concise error reporting in such cases.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So to fix this I'd love to either see error messages collapsed into a single message with an array of messages from the same line, or somehow grouped together into blocks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pointers are ^ and not *
&lt;/h3&gt;

&lt;p&gt;I'm on a German keyboard and the &lt;code&gt;^&lt;/code&gt; character is a bit of a pain to type, especially when compared to the &lt;code&gt;*&lt;/code&gt; character which is right next to the &lt;code&gt;Enter&lt;/code&gt; key on my keyboard. I get that Odin wants to differentiate itself from C and C++ but this small change feels unnecessary and adds a bit of friction to the coding experience.&lt;/p&gt;

&lt;p&gt;These are as the title says just minor nitpicks and in no way detract from the overall experience of using Odin, just minor annoyances that I personally had while using the language your experience may differ vastly and none of these may even bother you.&lt;/p&gt;

&lt;h2&gt;
  
  
  So is Odin just a More Boring C?
&lt;/h2&gt;

&lt;p&gt;In a way, yes kind of. I mean it's very similar in approach and philosophy but with more "guard rails" and helpful utilities to make the experience smoother and more enjoyable and the what I so far assume are first party bindings to popular libraries via the vendors package really makes it stand out in a great way, where you get a lot more consistency and predictability than you would if you were to use C with those same libraries. &lt;/p&gt;

&lt;p&gt;And I guess that's the strength of Odin, it's so boring that it just let's you be a productive programmer without getting in your way or trying to be too clever or fancy. &lt;/p&gt;

&lt;p&gt;I use boring here in an affectionate way, if you've ever read any of my other posts you'll know that I do not appreciate complexity and unnecessary cleverness in programming which is why I suppose I'm often quite critical of rust even though I do like it for certain use cases. &lt;/p&gt;

&lt;p&gt;In this case I'd say Odin is very similar to Go both are fantastic boring languages that let you get stuff done without much fuss or hassle. The only difference is that Go decided to ship with a garbage collector and Odin did not, which honestly for me personally makes Odin vastly more appealing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Syntax and Ergonomics
&lt;/h3&gt;

&lt;p&gt;Odin’s syntax is like C with a modern makeover clean, readable, and less prone to boilerplate. It did take me quite a while to get used to replacing my muscle memory for &lt;code&gt;*&lt;/code&gt; with &lt;code&gt;^&lt;/code&gt; for pointers and &lt;code&gt;func&lt;/code&gt;, &lt;code&gt;fn&lt;/code&gt;, &lt;code&gt;fun&lt;/code&gt;, &lt;code&gt;function&lt;/code&gt; with &lt;code&gt;proc&lt;/code&gt; for functions. But once I got over that initial hump, it felt pretty natural.&lt;/p&gt;

&lt;p&gt;Also &lt;code&gt;::&lt;/code&gt; for type declarations is a bit unusual and took me longer than I care to admit, as I'm fairly used to &lt;code&gt;::&lt;/code&gt; being used for scope resolution in languages like C++ and Rust. But again, once I got used to it, it felt fine.&lt;/p&gt;

&lt;p&gt;Everything else about the syntax felt pretty intuitive and straightforward. &lt;/p&gt;

&lt;h2&gt;
  
  
  Who Odin Might Be Right For
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Ideal Use Cases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Game Development&lt;/strong&gt;: Honestly I totally see where people are coming from when they say Odin is great for game development. The built-in vendor libraries for SDL2/3, OpenGL, Vulkan, Raylib and more make it super easy to get started with game development. Plus the language's performance and low-level capabilities are a great fit for the demands of game programming.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Systems Programming&lt;/strong&gt;: Odin's manual memory management, low-level access, and performance make it a solid choice for systems programming tasks like writing operating systems, device drivers, or embedded systems.&lt;br&gt;
I will absolutely be writing some utilities for my Linux setup in Odin in the near future.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Desktop Applications&lt;/strong&gt;: Again this is where those vendor libraries shine, making it easy to build cross-platform desktop applications with graphical interfaces as long as you're fine with doing some manual drawing of components, I'd love to see a binding for something like &lt;code&gt;GTK&lt;/code&gt; or &lt;code&gt;Qt&lt;/code&gt; in the vendor packages in the future.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;General Purpose Programming&lt;/strong&gt;: This brings me back to my intro where I said that it took me a while to realize that if Odin is good for game development then realistically by all means it should basically be good for anything and everything you wish to create with it. So yea give it a shot make something cool with it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Where It’s Not a Good Fit Yet
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Web Development&lt;/strong&gt;: The Net library is pretty darn nice and very extensive, however it does seem like it's maybe a bit more fit for general networking tasks rather than simplifying your life as a web backend developer. I'm sure there's already a bunch of third party libraries for this, but if you're a web dev you are almost spoiled for choice at the moment by languages that support web development out of the box with all the fixings and doodads.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Would I Use It Again?
&lt;/h3&gt;

&lt;p&gt;Absolutely in fact I will, I've already started planning some small utilities for my Linux setup in Odin. I really like the simplicity and readability of the language, as well as the comprehensive standard and vendor libraries. The performance is also impressive, especially the fast compile times.&lt;/p&gt;

&lt;h3&gt;
  
  
  Source Code and Feedback
&lt;/h3&gt;

&lt;p&gt;You can find the complete source code for the tiny key-value store with pub/sub capabilities on my GitHub: &lt;a href="https://github.com/dayvster/odin-kvpubsub" rel="noopener noreferrer"&gt;dayvster/odin-kvpub&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;If you create anything cool with it I'd love to see it so do hit me up on any of my socials.&lt;/p&gt;

&lt;p&gt;I'd love to hear your thoughts and experiences with Odin, whether you've used it before or are considering giving it a try. Feel free to leave a comment or reach out to me on Twitter &lt;a href="https://twitter.com/dayvsterdev" rel="noopener noreferrer"&gt;@dayvster&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Appreciate the time you took to read this post, and happy coding!&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>discuss</category>
    </item>
    <item>
      <title>My Battle Tested React Hooks Are Now Open Source</title>
      <dc:creator>Dayvster 🌊</dc:creator>
      <pubDate>Thu, 02 Oct 2025 14:53:23 +0000</pubDate>
      <link>https://dev.to/dayvster/my-battle-tested-react-hooks-are-now-open-source-13l0</link>
      <guid>https://dev.to/dayvster/my-battle-tested-react-hooks-are-now-open-source-13l0</guid>
      <description>&lt;h2&gt;
  
  
  Don't yap just give me the links
&lt;/h2&gt;

&lt;p&gt;Ok I get it you're busy here you go:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/dayvster/react-kata" rel="noopener noreferrer"&gt;GitHub Repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/react-kata" rel="noopener noreferrer"&gt;NPM Package&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have a great day!&lt;/p&gt;

&lt;h2&gt;
  
  
  What's This?
&lt;/h2&gt;

&lt;p&gt;I've been developing react since 2013 it's been through a lot of changes and updates over the years. We've seen patterns come and go, however when hooks were introduced in 2019, I was instantly ... hooked. No wait please don't leave, that was a good joke and you know it!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyhatyupz13iexdcn6kxa.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyhatyupz13iexdcn6kxa.gif" width="500" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Anyhow now that that's out of the way, I wanted to share with you a collection of various hooks that I have written over and over and over again in my past projects. These hooks have been battle tested in production applications and multiple client, personal and professional projects. &lt;/p&gt;

&lt;h2&gt;
  
  
  Why Open Source?
&lt;/h2&gt;

&lt;p&gt;Well I have this post on my blog about why I believe everyone should open source their work, &lt;a href="https://dayvster.com/blog/open-source-your-projects/" rel="noopener noreferrer"&gt;Open Source Your Projects&lt;/a&gt; I've been recently redesigning my website and fixing up my old blog posts and I stumbled upon this one decided to re-read it fully and discovered I am a hypocrite by value of pure laziness.&lt;/p&gt;

&lt;p&gt;Yes I did not open source these hooks sooner, because I was simply lazy. I had them in various different projects, repos, side projects etc. etc. But I never took the time to actually put them together in a single package and publish it. I just kept either navigating to these projects and constantly copy pasting them around OR worse yet I would rewrite them from scratch every time I needed them, which is just plain dumb.&lt;/p&gt;

&lt;p&gt;So I decided to stop being a hypocrite and finally open source a bunch of my stuff part of that was the task of collecting all of these react hooks and putting them together in a single package.&lt;/p&gt;

&lt;p&gt;So here it is&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing React-Kata
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/dayvster/react-kata" rel="noopener noreferrer"&gt;GitHub Repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/react-kata" rel="noopener noreferrer"&gt;NPM Package&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;React-Kata is a collection of battle-tested React hooks that I've used in various projects over the years. These hooks are designed to make your life easier and help you write cleaner, more efficient code. &lt;/p&gt;

&lt;h3&gt;
  
  
  Why React-Kata?
&lt;/h3&gt;

&lt;p&gt;Kata is a term used in martial arts to describe a set of movements and techniques that are practiced repeatedly to improve skill and mastery. Similarly, React-Kata is a collection of hooks that I've rewritten and reused and refined over and over again. So I thought it would be appropriate to name it React-Kata. Also I did martial arts as a younger person and wanted to show off a bit, lay off.&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;useLocalStorage&lt;/code&gt;: A hook that makes it easy to work with local storage in your React applications.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;useDebounce&lt;/code&gt;: A hook that debounces a value, making it easier to work with user input and avoid unnecessary re-renders.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;useThrottle&lt;/code&gt;: A hook that throttles a value, making it easier to work with user input and avoid unnecessary re-renders.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;useTimeout&lt;/code&gt;: A hook that makes it easy to work with timeouts in your React applications.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;useInterval&lt;/code&gt;: A hook that makes it easy to work with intervals in your React applications&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;useSessionStorage&lt;/code&gt;: A hook that makes it easy to work with session storage in your React applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and many many more in fact to see a lot of all of them check out the &lt;a href="https://github.com/dayvster/react-kata?tab=readme-ov-file#-hooks-overview" rel="noopener noreferrer"&gt;GitHub Repo Hooks overview&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  useShimmer
&lt;/h3&gt;

&lt;p&gt;This hook generates a shimmer effect SVG that can be used as a placeholder while content is loading. It takes width and height as parameters and returns an SVG string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useShimmer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-kata&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ShimmerDemo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shimmer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useShimmer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`data:image/svg+xml;utf8,&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shimmer&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Loading..."&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  useWhyDidYouUpdate
&lt;/h3&gt;

&lt;p&gt;This hook helps you identify why a component re-rendered by logging the changed props to the console or handling them with a custom callback.&lt;/p&gt;

&lt;p&gt;I can not overstate how often this one has saved my ass in the past.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useWhyDidYouUpdate&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-kata&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Demo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Logs changed props to console by default&lt;/span&gt;
  &lt;span class="nf"&gt;useWhyDidYouUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Demo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Or provide a callback to handle changes&lt;/span&gt;
  &lt;span class="nf"&gt;useWhyDidYouUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Demo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;changedProps&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Custom handling, e.g. send to analytics&lt;/span&gt;
    &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Changed: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProps&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  useTheme
&lt;/h3&gt;

&lt;p&gt;Let's you simply manage themes in your application. Supports auto, light, dark, and custom themes.&lt;br&gt;
For custom themes you simply input the name of the theme and it will be applied as the data-theme attribute on the html element. &lt;/p&gt;

&lt;p&gt;It will also be stored in local storage and retrieved from local storage on page load.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useTheme&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-kata&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Demo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Supports auto, light, dark, and custom themes&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toggleTheme&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useTheme&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;solarized&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Current theme: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;toggleTheme&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Toggle Theme&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;solarized&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Solarized&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Auto&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  useReload
&lt;/h3&gt;

&lt;p&gt;Allows you to pass a custom condition that when met will trigger a page reload.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useReload&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-kata&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Demo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Only reload if user confirms&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useReload&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;confirm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Reload?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;reload&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Reload Page&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope you find these hooks as useful as I have. If you have any suggestions for new hooks or improvements to existing ones, please feel free to open an issue or submit a pull request on GitHub, I'll always appreciate feedback, criticism, suggestions and especially Pull Requests.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Why Zig Feels More Practical Than Rust for Real-World CLI Tools</title>
      <dc:creator>Dayvster 🌊</dc:creator>
      <pubDate>Tue, 23 Sep 2025 09:59:39 +0000</pubDate>
      <link>https://dev.to/dayvster/why-zig-feels-more-practical-than-rust-for-real-world-cli-tools-2895</link>
      <guid>https://dev.to/dayvster/why-zig-feels-more-practical-than-rust-for-real-world-cli-tools-2895</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;So when it comes to memory management there are two terms you really need to know, the stack and the heap.&lt;/p&gt;

&lt;p&gt;The stack is a region of memory that stores temporary data that is only needed for a short period of time. It operates in a last-in, first-out (LIFO) manner, meaning that the most recently added data is the first to be removed, as the name suggests. Basically imagine a stack of plates, if you wanna remove one plate you remove the top one, remove the middle plate and disaster awaits in this analogy. The stack is typically used for storing function parameters, local variables, and return addresses. It is fast and efficient because it has a fixed size and does not require dynamic memory allocation.&lt;/p&gt;

&lt;p&gt;The size of the stack is usually limited, and if a program tries to use more stack space than is available, it can result in a stack overflow error. This can happen if a function calls itself recursively too many times or if a program allocates too much memory on the stack.&lt;/p&gt;

&lt;p&gt;Whereas the heap as the name suggests is a region of memory that is used for dynamic memory allocation. Unlike the stack, the heap does not have a fixed size and can grow or shrink as needed. The heap is typically used for storing data that needs to persist beyond the lifetime of a single function call, such as objects or data structures that are created at runtime. Imagine the heap as a pile of clothes in a disorganized household, you can add or remove clothes as needed and as long as the pile isn't too big you can find what you need with relative speed and ease. But it will quickly become a nightmare if you let it grow out of control. The heap is managed by the operating system and requires dynamic memory allocation, which can be slower and less efficient than stack allocation.&lt;/p&gt;

&lt;p&gt;The heap can also become fragmented over time, since we do not always store data in a contiguous block of memory. This can lead to performance issues and make it more difficult to allocate large blocks of memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rust's Borrow Checker
&lt;/h3&gt;

&lt;p&gt;Rust's borrow checker is a a pretty powerful tool that helps ensure memory safety during compile time. It enforces a set of rules that govern how references to data can be used, preventing common programming memory safety errors such as null pointer dereferencing, dangling pointers and so on. However you may have notice the word &lt;strong&gt;compile time&lt;/strong&gt; in the previous sentence. Now if you got any experience at systems programming you will know that compile time and runtime are two very different things. Basically compile time is when your code is being translated into machine code that the computer can understand, while runtime is when the program is actually running and executing its instructions. The borrow checker operates during compile time, which means that it can only catch memory safety issues that can be determined statically, before the program is actually run. &lt;/p&gt;

&lt;p&gt;This means that basically the borrow checker can only catch issues at comptime but it will not fix the underlying issue that is developers misunderstanding memory lifetimes or overcomplicated ownership. The compiler can only enforce the rules you’re trying to follow; it can’t teach you good patterns, and it won’t save you from bad design choices.&lt;/p&gt;

&lt;h3&gt;
  
  
  Story TIme
&lt;/h3&gt;

&lt;p&gt;Last weekend I've made a simple CLI tool for myself to help me manage my notes it parses &lt;code&gt;~/.notes&lt;/code&gt; into a list of notes, then builds a tag index mapping strings to references into that list. Straightforward, right? Not in Rust. The borrow checker blocks you the moment you try to add a new note while also holding references to the existing ones. Mutability and borrowing collide, lifetimes show up, and suddenly you’re restructuring your code around the compiler instead of the actual problem.&lt;/p&gt;

&lt;p&gt;In Zig, we would just allocate the list with an allocator, store pointers into it for the tag index, and mutate freely when we need to add or remove notes. No lifetimes, no extra wrappers, no compiler gymnastics, that’s a lot more straightforward.&lt;/p&gt;

&lt;h3&gt;
  
  
  But Dave isn't that the exact point of Rust's borrow checker?
&lt;/h3&gt;

&lt;p&gt;Yes it is, however by using Zig I managed to get most of the benefits of Rust's memory safety without the complexity or ceremony of the borrow checker. All it took was some basic understanding of memory management and a bit of discipline. I was able to produce two CLI's that are both memory safe and efficient however the Zig one was way more straightforward and easier to reason about and took less time to write.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Safety, Really for CLI Tools?
&lt;/h2&gt;

&lt;p&gt;This is where a lot of developers trip up, Rust markets itself as a language that produces safe software, great marketing hook, but one tiny problem, memory safety is one puzzle piece of overall software safety. I'm not sure if the Rust foundation does this on purpose sort of a blanket statement to make it seem like memory safety is the end all be all of software safety, or if they just don't want to constantly prefix safety with memory safety(even though they should).&lt;/p&gt;

&lt;p&gt;But back to the main point, memory safety is just one aspect of software safety. You can argue if it's a big or small piece of the puzzle, I'd say it depends on the software and use-case but it's definitely not the only piece. &lt;/p&gt;

&lt;p&gt;So What exactly is safety in terms of CLI tools?&lt;/p&gt;

&lt;p&gt;Memory safety alone does not make a program safe. Your CLI tool can still crash, produce wrong results, corrupt files, leak sensitive data, be vulnerable to various types of attacks or just behave in a way that is not expected. Let's go back to my &lt;code&gt;Notes CLI&lt;/code&gt; it's rust version may never segfault but it could silently overwrite my index or tags or corrupt my files if I make a mistake in my logic, or perhaps it could store my file in a temporary location that is world readable, exposing my notes to anyone on the system. Is that safe? No.&lt;/p&gt;

&lt;p&gt;Would using Zig solve any of those issues automatically, also no.&lt;br&gt;
Is my example a bit contrived, yes, but it illustrates the point that memory safety is not the only thing that matters when it comes to software safety.&lt;/p&gt;

&lt;p&gt;In fact you should also consider other aspects of safety such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Predictable Behavior&lt;/strong&gt;: The program should do what the user expects, even when input is malformed or unexpected. A CLI that panics on a missing file or fails silently on a corrupted note is not safe.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Avoiding Crashes or Silent Corruption&lt;/strong&gt;: The program should handle errors gracefully, providing meaningful feedback to the user instead of crashing or corrupting data. A CLI that crashes on a malformed note or silently overwrites existing notes is not safe.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Manageable Performance&lt;/strong&gt;: The program should perform well under expected workloads, avoiding excessive resource consumption or slowdowns. A CLI that becomes unresponsive when managing a large number of notes is not safe. This is where it really helps to understand memory allocations and performance characteristics of your language of choice.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sensitive Data Handling&lt;/strong&gt;: The program should protect sensitive data from unauthorized access or exposure. A CLI that stores notes in a world-readable temporary file is not safe.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Robustness Against Attacks&lt;/strong&gt;: The program should be resilient against common attack vectors, such as injection attacks or buffer overflows. A CLI that can be exploited to execute arbitrary code or corrupt data is not safe. And this is precisely where Rust's memory safety shines, it can help prevent certain types of vulnerabilities that arise from memory mismanagement. However, it's not a silver bullet that guarantees overall safety.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Borrow Checker: Strengths and Limitations
&lt;/h2&gt;

&lt;p&gt;The borrow checker is impressive. It prevents dangling references, double frees, and mutable aliasing at compile time, things that would otherwise cause segfaults or undefined behavior. It’s why Rust can claim “memory safe without a garbage collector.”&lt;/p&gt;

&lt;h3&gt;
  
  
  Strengths:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Zero data races / mutable aliasing issues&lt;/strong&gt;: The compiler guarantees that only one mutable reference exists at a time, and that immutable references cannot be combined with mutable ones.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Strong compile-time guarantees&lt;/strong&gt;: Many memory-related bugs are caught before you even run the program.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Early bug detection&lt;/strong&gt;: You find mistakes before shipping code, which is a huge win in long-lived services or concurrent systems.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Limitations / Pain Points:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cognitive overhead&lt;/strong&gt;: You’re constantly thinking about lifetimes, ownership, and borrow scopes, even for simple tasks. A small CLI like my notes tool suddenly feels like juggling hot potatoes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Boilerplate and contortions&lt;/strong&gt;: You end up introducing clones, wrappers (Rc, RefCell), or redesigning data structures just to satisfy the compiler. Your code starts serving the compiler, not the problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compile-time only&lt;/strong&gt;: The borrow checker cannot fix logic bugs, prevent silent corruption, or make your CLI behave predictably. It only ensures memory rules are followed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Edge cases get messy&lt;/strong&gt;: Shared caches, global state, or mutable indexes often trigger lifetime errors that are annoying to work around.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, the Rust borrow checker can feel more like a mental tax than a helpful tool, especially for short-lived CLI projects. You’re trading developer ergonomics for a compile-time guarantee that, in many CLI scenarios, may be overkill.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zig's Approach to Safety and Simplicity
&lt;/h2&gt;

&lt;p&gt;Zig takes a different approach to safety and simplicity. It provides manual memory management with optional safety checks, allowing developers to choose the level of control they need. This can lead to more straightforward code for certain use cases, like CLI tools. However where it really shines is how it does manual memory management, I've briefly touched upon this in my other blog post &lt;a href="https://dayvster.com/blog/zig-allocators-explained/" rel="noopener noreferrer"&gt;Zig Allocators Explained&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But basically long story short Zig gives you allocators, a set of tools that helps you manually manage your memory in a more structured and predictable way. You can choose to use a general purpose allocator like the &lt;code&gt;std.heap.page_allocator&lt;/code&gt; or you can create your own custom allocator that fits your specific needs. This allows you to have more control over how memory is allocated and deallocated, which can lead to more efficient and predictable memory usage. This combined with Zig's &lt;code&gt;defer&lt;/code&gt; statement which allows you to schedule cleanup code to run when a scope is exited, makes it easy to manage resources gives you most of the power of Rust's borrow checker at your disposal without the complexity and ritual. However it asks one thing in return of you, discipline, your software will be only as safe as you make it. We can make the same claim about Rust, you can throw &lt;code&gt;copy&lt;/code&gt; and &lt;code&gt;clone&lt;/code&gt; and &lt;code&gt;unsafe&lt;/code&gt; around your code and throw away all the benefits of the borrow checker in a heartbeat. &lt;/p&gt;

&lt;p&gt;The two languages are polar opposites in this regard, Zig places the burden on the developer and makes it easy for them to produce memory safe software, whereas Rust places the burden on the compiler and makes it hard for developers to produce memory unsafe software. &lt;/p&gt;

&lt;p&gt;Back to the main point, zig's approach to memory management is in my subjective opinion more practical for most of my use cases, especially for CLI tools. It allows me to write straightforward code that is easy to reason about and maintain, without the overhead of the borrow checker. I can allocate a list of notes, store pointers to them in a tag index, and mutate the list freely when I need to add or remove notes. No lifetimes, no extra wrappers, no compiler gymnastics, that’s a lot more straightforward.&lt;/p&gt;

&lt;p&gt;Oh I almost forgot, Zig also has the &lt;code&gt;comptime&lt;/code&gt; feature which allows you to execute code at compile time. This can be useful for generating code, performing static analysis, or optimizing performance and even for testing which is a really nice bonus and can be a small helper when it comes to memory safety. &lt;/p&gt;

&lt;h2&gt;
  
  
  Developer Ergonomics Matter and Developers are not Idiots
&lt;/h2&gt;

&lt;p&gt;When developing software we want to be productive and efficient, most of all we want to be correct and produce good software, however we also want to enjoy the process of creation and not feel like we are fighting the tools we use. Developer ergonomics is a term that refers to how easy and comfortable it is to use a programming language or framework. It encompasses things like syntax, tooling, documentation, and community support. A language with good developer ergonomics can make it easier to write correct code, while a language with poor developer ergonomics can make it harder to do so. I'd say as it currently stands Rust has poor developer ergonomics but produces memory safe software, whereas Zig has good developer ergonomics and allows me to produce memory safe software with a bit of discipline. &lt;/p&gt;

&lt;p&gt;I personally usually prefer languages where I do not have to succumb to too much ceremony and ritual to get things done, I want to be able to express my ideas in code without having to constantly think about the underlying mechanics of the language and yet I want to be responsible and produce good software. So with C and C++ this was a tiny bit harder as you basically had to learn some useful and practical memory management patterns and techniques, Zig comes with them baked in. &lt;/p&gt;

&lt;p&gt;I feel like Zig really respects it's developers and treats them like adults, it gives you the tools and expects you to use them wisely. Rust on the other hand feels like it treats developers like children that need to be constantly supervised and guided, which can be frustrating and demotivating.&lt;/p&gt;

&lt;p&gt;Developers are not idiots, sure even the smartest amongst us still produce memory safety issues or bugs in their software and it's silly to assume that with enough training and practice we can become perfect developers, but we can become better developers. We can learn from our mistakes and improve our skills, we can learn to write better code and produce better software.&lt;/p&gt;

&lt;p&gt;It's not good to abstract that away to the compiler and assume that it will magically make us better developers, I don't personally think it will. In fact not to sound too cliche but I think that the journey to becoming a better developer is a series of mistakes and fixes, we learn from our mistakes and improve our skills. What does it say about a language that tries to abstract away the mistakes we make, does it really help us become better developers ?&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Rust is amazing, if you’re building something massive, multithreaded, or long-lived, where compile-time guarantees actually save your life. The borrow checker, lifetimes, and ownership rules are a boon in large systems.&lt;/p&gt;

&lt;p&gt;But for small, practical CLI tools? Rust can feel like overkill. That’s where Zig shines. Lightweight, fast, and straightforward, you get memory safety without constantly bending over backward for the compiler. You can allocate a list, track pointers, and mutate freely without extra wrappers, lifetimes, or contortions. Iterating feels natural, the code is easier to reason about, and you get stuff done faster.&lt;/p&gt;

&lt;p&gt;Memory safety is important, but it’s just one piece of the puzzle. Predictable behavior, maintainable code, and robustness are just as critical, and that’s exactly why Zig often feels more practical for real-world CLI tools.&lt;/p&gt;

&lt;p&gt;At the end of the day, it’s not about which language is “better.” It’s about what fits your workflow and the kinds of projects you build. For me, Zig hits the sweet spot: memory safe, low ceremony, and developer-friendly, perfect for small tools that actually get things done.&lt;/p&gt;

</description>
      <category>zig</category>
      <category>programming</category>
      <category>discuss</category>
      <category>rust</category>
    </item>
    <item>
      <title>Are We Chasing Language Hype Over Solving Real Problems?</title>
      <dc:creator>Dayvster 🌊</dc:creator>
      <pubDate>Sat, 20 Sep 2025 09:33:46 +0000</pubDate>
      <link>https://dev.to/dayvster/are-we-chasing-language-hype-over-solving-real-problems-36b</link>
      <guid>https://dev.to/dayvster/are-we-chasing-language-hype-over-solving-real-problems-36b</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;As you may have heard or seen, there is a bit of controversy around Ubuntu adopting a rewritten version of GNU Core Utils in Rust. This has sparked a lot of debate in the tech community. This decision by Canonical got me thinking about this whole trend or push of rewriting existing software in Rust which seems to be happening a lot lately.&lt;/p&gt;

&lt;p&gt;To put it bluntly I was confused by the need to replace GNU Core Utils with a new implementation as GNU Core Utils has been around since arguably the 90s and more realistically 2000s and it has been battle tested and proven to be reliable, efficient, effective and most importantly secure as it had basically never had any major security vulnerabilities in its entire existence. So why then would we deem it necessary to replace it with a new implementation in Rust? Why would anyone go through the trouble of rewriting something that already works perfectly fine and has been doing so for decades? When the end result at best is going to be a tool that does the same thing as the original and in the very best case scenario offer the same performance?&lt;/p&gt;

&lt;p&gt;What bothers me even more is the bigger pattern this points to. Are we as developers more interested in chasing new languages and frameworks than actually solving real problems? I strongly subscribe to the idea that software development 60% problem solving and 40% creative exploration and innovation. But lately it feels like the balance is shifting more and more towards the latter. We seem to be more interested in trying out the latest and greatest languages and frameworks than actually solving real problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hype of New Languages and Shiny Object Syndrome
&lt;/h2&gt;

&lt;p&gt;We've all been there in the past haven't we? Getting excited about a new programming language or framework that promises to solve all our problems and make our lives easier. It's easy to get caught up in the hype and want to try out the latest and greatest technology, but it's important to remember that just because something is new doesn't mean it's better. We need to be careful not to fall into the trap of "shiny object syndrome" where we chase after the latest trends without considering whether they actually solve our problems or improve our workflows. It's important to evaluate new technologies based on their merits and how they fit into our existing systems and workflows, rather than simply jumping on the bandwagon because everyone else is doing it.&lt;/p&gt;

&lt;p&gt;Now for the important question: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I think the developers of coreutils-rs are doing this just because Rust is the new hotness?&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Short and simple: No, no I do not. I believe they have good intentions and are likely trying to improve upon the existing implementation in some way. However, I do not agree with them that there is a need for a rewritten version of GNU Core Utils in Rust. I also do not agree that GNU Core Utils is inherently insecure or unsafe.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why do we get Exited About New Languages?
&lt;/h3&gt;

&lt;p&gt;It's also important to briefly touch upon the psychological aspect of why we get excited about new languages. New languages often come with new features, syntax, and paradigms that can be appealing to developers. They may also promise to solve problems that existing languages struggle with, such as performance, concurrency, or memory safety. Additionally, new languages can offer a fresh perspective on programming and can inspire creativity and innovation. Not to mention the community aspect, new usually means a changing of the guard, new people, new ideas, new ways of thinking about problems. All of these factors can contribute to the excitement and enthusiasm that developers feel when a new language is introduced.&lt;/p&gt;

&lt;p&gt;This enthusiasm can sometimes lead to an almost zealous approach of wanting everything and anything to be written only in the new language by this new and fresh community of developers. This can lead to a situation where existing and well-established software is rewritten in the new language, even if there is no real need for it. This can be seen as a form of "language evangelism" where developers are trying to promote their favorite language by rewriting existing software in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Case of GNU Core Utils
&lt;/h2&gt;

&lt;p&gt;As I've briefly touched upon earlier, GNU Core Utils is a collection of basic file, shell and text manipulation utilities that are fundamental to the operation of Unix-like operating systems. These utilities include commands like &lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;cp&lt;/code&gt;, &lt;code&gt;mv&lt;/code&gt;, &lt;code&gt;rm&lt;/code&gt;, &lt;code&gt;cat&lt;/code&gt;, &lt;code&gt;echo&lt;/code&gt;, and many others. They are essential for performing everyday tasks in the command line interface (CLI) and are used by system administrators, developers, and users alike.&lt;/p&gt;

&lt;p&gt;Some of these can run hundreds of times per second, so performance is absolutely crucial. Even a small reduction in performance to a utility that is run by some OS critical daemon can have a significant impact on the overall performance of the system. GNU core utils has been optimized for this for about 30+ years at this point and is it really worth just tossing all of those lessons and optimizations out the window just to rewrite it in a new language?&lt;/p&gt;

&lt;p&gt;I've also briefly touched upon that at best in the absolute &lt;strong&gt;best case scenario&lt;/strong&gt; a rewritten version of GNU Core Utils in Rust would be able to match the performance of the original implementation. As we know GNU Core Utils are mostly written in C and some C++ mixed in sparingly. So far benchmarks have shown time and time again that at best with a lot of optimizations and tweaks Rust can only ever match the performance of C and in most cases it is actually slower. So the best case outcome of this rewrite is that we get a tool that does the same thing as the original and at best offers the same performance. So what is the actual benefit of this rewrite?&lt;/p&gt;

&lt;p&gt;Where is the value, what is the actual problem that is being solved here? &lt;/p&gt;

&lt;h2&gt;
  
  
  When Hype Overshadows Real Problems
&lt;/h2&gt;

&lt;p&gt;This is the crux of the issue, it's very very easy to get swept up in the excitement of a new language and want to use it for everything and anything under the sun. As developers we love novelty and communities with enthusiasm and fresh ideas. It's stimulating it's fun it feels like progress it feels like we are finally building something again instead of just rehashing and maintaining. We all know from personal experience that creating a new project is more fun and enjoyable than maintaining and existing one and this is a natural human tendency.&lt;/p&gt;

&lt;p&gt;Now do I think this is one of the reasons the developers of coreutils-rs are doing this?&lt;/p&gt;

&lt;p&gt;Yes, I do. &lt;/p&gt;

&lt;p&gt;But in the end they are solving a problem that does not exist. &lt;/p&gt;

&lt;h3&gt;
  
  
  It's not just about Core Utils
&lt;/h3&gt;

&lt;p&gt;Now with how often I've mentioned this specific example of GNU Core Utils you might think I want to single them out or have some sort of grudge or specific issue with this particular project. No, not really... I think this project is indicative of the larger issue we face in the tech community. It's very easy to get caught up in the excitement of new languages and frameworks and want to use them for everything and anything. This can lead to a situation where we are rewriting existing software in new languages without considering whether it actually solves any real problems or improves our workflows. &lt;/p&gt;

&lt;h2&gt;
  
  
  Problem Solving Should Be Our North Star
&lt;/h2&gt;

&lt;p&gt;At the end of the day, software development is about solving real problems, not about chasing novelty, shiny new languages, or personal curiosity. Every line of code we write, every framework we adopt, every library we integrate should be justified by the problems it helps solve, not by the hype around it.&lt;/p&gt;

&lt;p&gt;Yet increasingly, it feels like the industry is losing sight of this. We celebrate engineers for building in the “new hot language,” for rewriting tools, or for adopting the latest framework, even when the original solution worked perfectly fine. We reward novelty over necessity, excitement over impact.&lt;/p&gt;

&lt;p&gt;This is not a phenomenon isolated to just core utils or systems programming, it happens in web development, mobile development, data science, and pretty much every other area of software development. We too often abandon tried and true solutions and ideas of new and exciting shiny ones without considering whether they actually solve any real problems or improve our workflows.&lt;/p&gt;

&lt;p&gt;For example web development went full circle with React Server Components where we went from separation of concerns straight back to PHP style mixing of HTML and logic, server rendering and delivering interactive components to the client. Or the whole GraphQL craze where traditional REST APIs were abandoned en masse for a new and exciting way of doing things that promised to solve the dreaded problem of "over-fetching" and "under-fetching" of data. Yet in reality, it introduced a whole new set of problems and complexities that were not present in traditional REST APIs.&lt;/p&gt;

&lt;p&gt;Or perhaps the whole microservices and microfrontend craze where a lot of projects were abandoned or rewritten to be split into smaller and smaller pieces,&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Was it all bad? Should we always just stick to what works and only ever maintain legacy projects and systems? Heck no!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There is definitely a place for innovation and new ideas in software development. New languages, frameworks, and tools can bring fresh perspectives and approaches to problem-solving. However, it's important to evaluate these new technologies based on their merits and how they fit into our existing systems and workflows, rather than simply jumping on the bandwagon because everyone else is doing it.&lt;/p&gt;

&lt;p&gt;We need to be more critical and thoughtful about the technologies we adopt and the projects we undertake. We need to ask ourselves whether a new language or framework actually solves a real problem or improves our workflows, or if we're just chasing novelty for its own sake.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Final Thought
&lt;/h2&gt;

&lt;p&gt;At the end of the day, it’s not about Rust, React, GraphQL, or the latest microservices fad. It’s about solving real problems. Every line of code, every framework, every rewrite should have a purpose beyond curiosity or hype.&lt;/p&gt;

&lt;p&gt;We live in a culture that rewards novelty, celebrates “cool” tech, and often mistakes excitement for progress. But progress isn’t measured in how many new languages you touch, or how many shiny rewrites you ship, it’s measured in impact, in the problems you actually solve for users, teams, and systems.&lt;/p&gt;

&lt;p&gt;So next time you feel the pull of the newest hot language, framework, or tool, pause. Ask yourself: “Am I solving a real problem here, or just chasing excitement?”&lt;/p&gt;

&lt;p&gt;Because at the end of the day, engineering isn’t about what’s trendy, it’s about what works, what matters, and what actually makes a difference. And that, my friends, is the craft we should all be sharpening.&lt;/p&gt;

</description>
      <category>software</category>
      <category>programming</category>
      <category>discuss</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Dev Culture Is Dying The Curious Developer Is Gone</title>
      <dc:creator>Dayvster 🌊</dc:creator>
      <pubDate>Thu, 18 Sep 2025 08:25:24 +0000</pubDate>
      <link>https://dev.to/dayvster/dev-culture-is-dying-the-curious-developer-is-gone-lkn</link>
      <guid>https://dev.to/dayvster/dev-culture-is-dying-the-curious-developer-is-gone-lkn</guid>
      <description>&lt;h2&gt;
  
  
  When Curiosity Lead the Way
&lt;/h2&gt;

&lt;p&gt;If you have been in software development for a while, you might remember a time when developers were launching unique and innovative products and projects just for the sake of curiosity, learning or even just because they had a particular interest in a specific topic.&lt;/p&gt;

&lt;p&gt;This curiosity and problem solving mindset gave us some of the best tools that we still use today such as VLC, Linux, Git, Apache HTTP Server, Docker(arguably), and many many more.&lt;/p&gt;

&lt;p&gt;These tools were not created by large corporations or solopreneurs looking to increase their MMR or ARR. They were created by curious developers who wanted to solve a unique problem they had or even just wanted to learn something new.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nights Spend Chasing ideas and Tinkering
&lt;/h3&gt;

&lt;p&gt;I still remember back in the 2000s (2003-2009) the nights I spent tinkering with new technologies, frameworks, and programming languages. I would often find myself staying up late into the night, fueled by curiosity and a desire to learn more about the craft of software development. I would make the dumbest of projects and the strangest of shortcuts just because I could and just to see if it would work. Even if it would only serve me and no one else, I would still make it because it was simply fun.&lt;/p&gt;

&lt;h3&gt;
  
  
  Learning Without a Purpose
&lt;/h3&gt;

&lt;p&gt;There is something to be said about learning without a clear purpose, goal or even a expected reward at the end of your journey. It allows you to explore new ideas and concepts without the pressure of having to deliver a specific outcome. It allows you to be creative and even tinker with suboptimal implementations and solutions or even some that are flat out insane or idiotic.&lt;/p&gt;

&lt;p&gt;Because at the end of your journey, you will not be met with disappointment that you did not create a new product or service that will generate passive income or be used by hundreds of thousands of people. No, that was never your expectation going into it in the first place, you started the journey simply because you were curious and you wanted to create something even if your target demographic was just yourself.&lt;/p&gt;

&lt;p&gt;This in many ways leads to a better learning journey and a more fulfilling experience as you are not bound by the constraints of having to deliver a specific outcome or meet certain expectations. You can simply explore and learn at your own pace and in your own way. Don't get me wrong this does not apply only to new comers to the field or junior software developers, this applies to every single developer out there even the most experienced ones.&lt;/p&gt;

&lt;p&gt;Personally I'd consider myself fairly experienced in the field of software development, I started learning C++ back in 2003 and my first job as a software developer was in 2008. I've been in the field for a while now. In fact the longer I am in the field the more I realize that I know nothing. There is always something new to learn and explore and I find myself constantly tinkering with technologies be they new or old. Learning is not only about new and shiny tech, sometimes it's assembly or system design, microcontrollers, embedded etc.&lt;/p&gt;

&lt;p&gt;This is plain and simply the &lt;strong&gt;tinkerers mindset&lt;/strong&gt; and I believe that this mindset is slowly dying out in the field of software development. I find less and less like-minded people and I encounter more and more push back along the lines of "Why are you wasting your time with that? You should be focusing on X, Y or Z instead." or "That is not going to help you in your career." or even "You should be focusing on building products that will generate passive income or be used by hundreds of thousands of people."&lt;/p&gt;

&lt;h2&gt;
  
  
  The Era of Metrics and Shiny Things
&lt;/h2&gt;

&lt;p&gt;I find that there has been a strong shift in developer culture over the past decade or so. A very strong and worrying shift towards metrics, revenue optimization, delivering "value" and "building for the masses". I'm not sure this is a good shift but it is one that is happening nonetheless. &lt;/p&gt;

&lt;p&gt;It seems to me that the focus has shifted from curiosity, learning and a joy for creating cool things to a focus on metrics, observables, problem solving for your niche audience. &lt;/p&gt;

&lt;p&gt;I see countless developers spending their free time using their free time using technologies they do not enjoy building products they do not care about for an audience they do not understand, simply because they believe that this is what they should be doing in order to be successful as a software developer or to be taken seriously in the field.&lt;/p&gt;

&lt;p&gt;Many who I talk to believe that this will set them apart from the rest of the pack or that they are a temporarily embarrassed startup CTO/founder or that they are building the next big thing that will paint their name in the stars and grant them the fame and respect of their fellow developers. &lt;/p&gt;

&lt;p&gt;But how can you ever hope to build something that huge if you do not even care about it? If the problem you are solving is not even a problem you yourself have or worse yet care about?&lt;/p&gt;

&lt;p&gt;This is where a deeper issue shows itself, When you don't care about what you are building you start looking elsewhere for that sense of progress, accomplishment or even identity. You become a Next.js developer, a React developer, a Rust developer etc... You start to identify yourself by the tools you use rather than the problems you solve or the things you create.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chasing Every new Framework or Idea
&lt;/h3&gt;

&lt;p&gt;If you've identified with anything in this article so far, then take a moment and answer this to yourself honestly. How often did you find yourself working on your product or project only get think oh but this new framework/library/module/plugin is so much better, I should be using that instead of what I am doing right now, I need to improve my stack, I need to be using the latest and greatest. Because I am building something that will eventually be used by hundreds of thousands of people, so why stunt my growth, why risk being left behind?&lt;/p&gt;

&lt;p&gt;Naturally your webapp has to use the latest version of React or Next.js with it's latest features and optimizations, a year or so back (2023-2024) that was React server components.&lt;/p&gt;

&lt;p&gt;Or maybe you just had to switch to the newest version of Vue.js or Angular because they have some new feature that will make your life easier or your app faster or more scalable.&lt;/p&gt;

&lt;p&gt;Or perhaps your utilities or backend are written in Go or Node or C# and really you should be using Rust because it's just so damn fast and memory efficient. You can't pass that up can you?&lt;/p&gt;

&lt;p&gt;So you title yourself after whatever language, framework or library you are currently using. You are no longer a software developer, you are a Next.js developer, a specialist in your field.&lt;/p&gt;

&lt;p&gt;you chase every new shiny thing and you write a product or service in that shiny thing optimizing for MMR, ARR, DAU, MAU, SEO rankings, conversion rates and all that jazz. Wondering why your product or service is not taking off, why no one is using it, why you are not getting any traction. &lt;/p&gt;

&lt;p&gt;Weird... you used all the right things, you've used the technology that you should have used you've optimized for all the right metrics, you've done everything by the book. So why is it not working?&lt;/p&gt;

&lt;h2&gt;
  
  
  What we Lost Along the Way
&lt;/h2&gt;

&lt;p&gt;Constantly adopting the latest and greatest thing, not because it inspires you or because you care about it, but simply because you think you should be using it in order to be successful is a recipe for disaster. Not just for you for out entire developer culture as a whole. &lt;/p&gt;

&lt;p&gt;I don't want to sound overly dramatic but I do lament the loss of the curious developer, the tinkerer, the obsessed creative that just wants to build something cool even if nobody cares about it, even if it only solves their own problem.&lt;/p&gt;

&lt;p&gt;I think we are slowly killing this mindset, it's slowly disappearing from our culture wether that will be good or bad only time will tell. If you were to ask me I'd say it's a very bad thing.&lt;/p&gt;

&lt;p&gt;Don't get me wrong we have occasional bright sparks of innovation and creativity HTMX, Bun, Astro, Zig and many other come to mind. But these are few and far between but they show that there are still curious developers out there, they are just harder to find and shrinking in numbers and being drowned out by the noise of metric seeking and revenue optimizations. &lt;/p&gt;

&lt;h2&gt;
  
  
  The World Moves On, But Some of Us Remember
&lt;/h2&gt;

&lt;p&gt;I don't want to sound too much like a middle aged man lamenting a world that is changing around him. I understand the world moves on, you get more and more jaded and cynical as you get older. But I assure you this is not that. This is a pattern I've been noticing for a while and it worries me. The tools the projects that were built by curious developers are still around and still in use but compared to before we get relatively few new ones that are truly build out of curiosity and passion.&lt;/p&gt;

&lt;p&gt;There are occasional sparks but not like before.&lt;/p&gt;

&lt;p&gt;Think of all the amazing software you use today, think which ones were made by insanely curious developers and think of how old that software is or when it came out and then think of more modern software and how many of those were made by massive corporations or solopreneurs or even just flat bought out or sold out.&lt;/p&gt;

&lt;p&gt;I think we are losing something very important in our culture and I hope we can find it again before it's too late. Before the curious developer is gone for good and we are left with a sea of software built with no privacy concerns, horrible monetization strategies, bloated frameworks and libraries and no ownership mindset, not for you the consumer and not by the creator.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Death of Ownership is not Just for the Consumer
&lt;/h2&gt;

&lt;p&gt;We've all seen the shifting tide, consumers no longer own their software, you may buy the newest Adobe suite, or JetBrains IDE, latest iPhone or Android or even the latest Windows, but you do not own it. It can be taken away from you at any time, you simply rent it, you pay a monthly fee to use it. You do not own it, you simply have a license to use it.&lt;/p&gt;

&lt;p&gt;But do we ever take time to consider the loss of ownership for the creators? The developers, the curious tinkerers, the obsessed creatives that build these tools and software. Do they own it? Or do they simply rent it out to the highest bidder or sell it off to the largest corporation? Do people still want to build something that is uniquely their or do they simply want to build the latest and greatest SASS that they can rent out to the masses?&lt;/p&gt;

&lt;p&gt;Do they care about the software they build or do they simply care about the metrics, the revenue, the growth? &lt;/p&gt;

&lt;p&gt;You can argue that Linus Torvalds owns Linux and cares about Linux the kernel, you can argue that Jean Baptiste Kempf owns VLC and cares about VLC. Does Solomon Hykes own Docker and care about Docker? Does Daniel Ek own Spotify and care about Spotify? Does Mark Zuckerberg own Facebook and care about Facebook?&lt;/p&gt;

&lt;p&gt;Are they owners, true owners of the product they built or did they simply become renters of their own creation, a slave to the metrics and revenue optimization?&lt;/p&gt;

&lt;p&gt;Again I don't want to be overly dramatic but this is an important question we all should be asking ourselves. I know I am asking myself this question more and more often as I see the world around me change and the developer culture shift towards metrics and revenue optimization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Carving Space for Curiosity and Innovation
&lt;/h2&gt;

&lt;p&gt;I implore you to find time in your life to be curious and creative to tinker and build something just for the sake of building it. Even if no one else cares about it, even if it only solves your own problem. Make something cool, something unique don't care about others build it for yourself, built it because you want to and can.&lt;/p&gt;

&lt;p&gt;Don't let the world tell you what you should be doing, what you should be building, what you should be using, no matter how ambitious or dumb or idiotic it may seem to others, make it because you want to, because it makes you happy, because it makes you feel alive.&lt;/p&gt;

&lt;p&gt;Software development is a unique craft, it's equal parts creative and equal parts engineering, two opposing forces that when combined can create something truly amazing. Fight the temptation to add marketing into the mix and dilute the craft with it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build what you Can't Ship
&lt;/h3&gt;

&lt;p&gt;Have a project in mind that you've always wanted to tackle but it never made sense to you to do it because it would never be used by anyone else or it would never make you any money? Do it anyway, build it, tinker with it, learn from it. Who cares if you can't ship it to the masses, who cares if it's useless. Make it, create something from nothing, just because you can.&lt;/p&gt;

&lt;h3&gt;
  
  
  Share the Spark
&lt;/h3&gt;

&lt;p&gt;You might think this goes against my previous point, but it really does not. Share your work, share your creations, bring others into your world, if nobody responds who cares, you made it, you created something from nothing, maybe the value is in the journey and not the destination. Maybe the value is in the learning and not the outcome. Maybe the value is in the process and not the product.&lt;/p&gt;

&lt;p&gt;And who knows maybe your unique problem will be shared by others, maybe your unique solution will inspire others to create something new, something unique. Maybe your curiosity will spark a fire in someone else and they will go on to create something truly amazing.&lt;/p&gt;

&lt;p&gt;It's not impossible it happened for Linux, it happened for VLC heck it happened for Git. &lt;/p&gt;

&lt;p&gt;Just try to conceptualize what an insane idea Git was even when SVN was a well established and widely used version control system. Who in their right mind would think that a distributed version control system would be a good idea? Yet here we are, Git is the de facto standard for version control in software development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I wrote this article to lament the loss of the curious spark in our developer culture, not to criticize or judge anyone. I understand the world moves on, I understand that we all have to make a living and that we all have to pay the bills. But I also believe that we should not lose sight of what makes software development such a unique and special craft. &lt;/p&gt;

&lt;p&gt;If you've made it this far, thank you, sincerely thank you. It's one of my longer articles and I appreciate you taking the time to read it. I hope it has sparked something in you, I hope it has made you think about your own journey as a software developer and I hope it has inspired you to be curious and creative again.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>discuss</category>
      <category>career</category>
    </item>
    <item>
      <title>Why I Still Reach for C for Certain Projects</title>
      <dc:creator>Dayvster 🌊</dc:creator>
      <pubDate>Tue, 16 Sep 2025 16:50:45 +0000</pubDate>
      <link>https://dev.to/dayvster/why-i-still-reach-for-c-for-certain-projects-k5b</link>
      <guid>https://dev.to/dayvster/why-i-still-reach-for-c-for-certain-projects-k5b</guid>
      <description>&lt;h2&gt;
  
  
  The Project That Made Me Choose C Again
&lt;/h2&gt;

&lt;p&gt;So a while back I got tired of xserver and decided to give wayland a try. I use Arch (BTW) with a tiling window manager (dwm) and I wanted to try something new, since I had a couple of annoyances with xserver. I've heard some good things about wayland so I thought you know what, why not let's give it a shot.&lt;/p&gt;

&lt;p&gt;After 30-45min my Arch and Hyprland setup was done and ready to go. I was pretty happy with it, but I was missing some features I've previously had such as notifications for when somebody posts new content to their RSS feed. I quite like RSS feeds I use them to keep up to date with blogs, news, streams, releases etc. So I thought to myself, why not write a small program that checks my RSS feeds and sends me a notification when there's something new.&lt;/p&gt;

&lt;p&gt;Now the way I was gonna go about this was pretty simple. I would write a simple C daemon that would run in the background, check my RSS feeds on a random interval between 5-15min and if there was something new it would send out a notification using &lt;code&gt;notify-send&lt;/code&gt;. Then comes the tricky part I wanted &lt;code&gt;swaync&lt;/code&gt; to allow me to offer two buttons on the notification, one to open the link in my default browser and one to ignore the notification and mark it as ignored in a flat file on my system so that I could see if I have to remove certain feeds that I tend to ignore a lot.&lt;/p&gt;

&lt;p&gt;Now here's the problem, you can't really do that with &lt;code&gt;swaync&lt;/code&gt;, I mean it does support buttons but it doesn't really let you handle the button clicks in a way that would allow you to do what I wanted. So I had to come up with a workaround. The workaround was to have another C program run as a daemon that would listen on the DBus for a very specific notfication that would contain an action with a string of &lt;code&gt;openUrl("url")&lt;/code&gt; or &lt;code&gt;ignoreUrl("url")&lt;/code&gt; and then handle the action accordingly. This way I could have &lt;code&gt;swaync&lt;/code&gt; send out a notification with the buttons and when the user(me) clicks on one of the buttons it would send out a DBus notification that my second daemon would pick up and handle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why C Was the Right Choice Here?
&lt;/h2&gt;

&lt;p&gt;Now you might be wondering why I chose C for this project and not something like python which would allow me to write this specific program much faster and enjoy the rest of my weekend. Well my answer to you is simple, I don't like python and I didn't feel like wasting multiple 100s of MBs of RAM for something this simple and small. &lt;/p&gt;

&lt;p&gt;In fact I DID write it in Python and Go first with their respective DBus libraries, both were super easy to work with and I had an initial working prototype within less than an hour. But as I ran a simple &lt;code&gt;htop&lt;/code&gt; I saw that the python version was using around 150MB of RAM and the Go version was using around 50MB of RAM. Now don't get me wrong, I'm not on an old thinkpad I have RAM to spare 32Gb to be exact. But why waste it on something this small and simple.&lt;/p&gt;

&lt;p&gt;Plus C is just so much more fun and exciting to work with. So I set up my C project which was just a simple &lt;code&gt;Makefile&lt;/code&gt; and a couple of &lt;code&gt;.c&lt;/code&gt; and &lt;code&gt;.h&lt;/code&gt; files. I imported &lt;code&gt;dbus/dbus.h&lt;/code&gt; and got to work. Now I'd be lying if I said it took me no time at all, in fact it took me roughly 3-4h which is a lot longer than python or Go. But in the end I had a working prototype that was using around 1-2MB of RAM and was super fast and responsive. I also got to brush up on my DBus skills which were a bit rusty since I hadn't worked with it in a while.&lt;/p&gt;

&lt;p&gt;Now getting a simple program like that from 150 to 50 to 1-2Mb of RAM usage is a huge performance improvement and it really showcases the power and strength of C as a programming language. Look at this this way I may have spent multiple hours longer writing this program in C but it will just continue to run in the background using a fraction of the resources that python or Go would have used for a long time to come. &lt;/p&gt;

&lt;p&gt;Additionally this probably won't be the only modification I make to my system now imagine if I were this lackluster with 10-20 small programs that I run in the background and let's make a wild assumption that each of those programs would use about 50-200Mb of RAM. Now we're looking at 500-4000Mb of RAM usage, or precisely one chrome tab!&lt;/p&gt;

&lt;p&gt;No I don't think that's an acceptable tradeoff for a couple of hours of my time. So in the end I think C was the right choice for this project and I'm pretty happy with the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Modern Languages Aren't Always the Best Fit
&lt;/h2&gt;

&lt;p&gt;That long rant above is a cute story / anecdote. But I feel like it also highlights a bigger issue that developers face today. A lot of times we just wanna complete a certain task as quickly as possible and have it running before we've had our second coffee. I get it we're all busy people and when resources are as cheap as they are today saving a few Mb of RAM or a few ms of CPU time isn't really a big deal. &lt;/p&gt;

&lt;p&gt;But there is something so nice and satisfying about writing a small program in C that does exactly what you want it to do and nothing more. It's like a breath of fresh air in a world where everything is becoming more and more bloated and resource hungry. It feels a bit zen taking everything down to the bare minimum and just focusing on the task at hand.&lt;/p&gt;

&lt;p&gt;I'd liken developing in C to woodworking, you have to be precise and careful with your cuts, but in the end you get a beautiful product that you can be uniquely proud of. Sure you could go pick up a flat pack from your local IKEA and have a nice looking table within an hour. But it will be souless and generic and not really made well, not something you can proudly say to your visitors "Hey I built this myself". I mean you could... but they probably won't really be as impressed with your flat pack assembly as if you were to show them a hand crafted table you made yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  C Excels in Low-Level System Programming and Efficiency
&lt;/h2&gt;

&lt;p&gt;So maybe the example project that I described above in the beginning of the article is too rickety or not really high brow enough that's fair I get that. But even if you think my project was stupid or silly or could have been done better or easier there is no denying that C simply is the gold standard for low level systems programming and efficiency.&lt;/p&gt;

&lt;p&gt;C gives you unparalleled control over system resources, memory management, and performance optimization. This makes it the go-to choice for developing operating systems, embedded systems, and performance-critical applications where every byte of memory and every CPU cycle counts.&lt;/p&gt;

&lt;p&gt;For example, the Linux kernel, which powers a vast majority of servers, desktops, and mobile devices worldwide, is primarily written in C. This is because C allows developers to write code that can directly interact with hardware and manage system resources efficiently.&lt;/p&gt;

&lt;p&gt;If you drive any modern vehicle that was created in the past decade or so, chances are that you have multiple microcontrollers in your car that are running C code to manage everything from engine performance, emergency breaking systems (AEB, ABS), distance control systems (ACC) and so on. For these systems you are very limited in the amount of RAM usage you can afford and most importantly you can not afford too much latency or delays in processing.&lt;/p&gt;

&lt;p&gt;Now how often have you heard about these systems failing and leading to fatal crashes? Not very often I'd wager. Of course safety standards, regulations and certifications play a big role in this but the choice of programming language is also a big factor. C is a mature and well-established language with a long history of use in safety-critical systems, making it a reliable choice for such applications.&lt;/p&gt;

&lt;p&gt;It does it's job exceptionally well and it does it with a very small footprint and most importantly you can run it in perpetuity on very limited hardware without worry over failure or issues due to resource exhaustion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real World Scenarios Where C Shines
&lt;/h2&gt;

&lt;p&gt;Here are a few real-world scenarios where C is the preferred choice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Operating Systems&lt;/strong&gt;: As mentioned earlier, the Linux kernel is written in C. Other operating systems like Windows and macOS also have significant portions written in C.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embedded Systems&lt;/strong&gt;: C is widely used in embedded systems development for devices like microcontrollers, IoT devices, and automotive systems due to its efficiency and low-level hardware access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Game Development&lt;/strong&gt;: Many game engines and performance-critical game components are written in C or C++ to achieve high performance and low latency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database Systems&lt;/strong&gt;: Many database management systems, such as MySQL and PostgreSQL, are implemented in C to ensure efficient data handling and query processing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network Programming&lt;/strong&gt;: C is often used for developing network protocols and applications due to its ability to handle low-level socket programming and efficient data transmission.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compilers and Interpreters&lt;/strong&gt;: Many programming language compilers and interpreters are written in C to leverage its performance and low-level capabilities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High-Performance Computing&lt;/strong&gt;: C is used in scientific computing and simulations where performance is critical, such as in numerical libraries and parallel computing frameworks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System Utilities&lt;/strong&gt;: Many system utilities and command-line tools in Unix-like operating systems are written in C for efficiency and direct system access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Device Drivers&lt;/strong&gt;: C is the language of choice for writing device drivers that allow the operating system to communicate with hardware devices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cryptography&lt;/strong&gt;: Many cryptographic libraries and algorithms are implemented in C to ensure high performance and security.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audio/Video Processing&lt;/strong&gt;: Libraries like FFmpeg are written in C to handle multimedia processing efficiently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Servers&lt;/strong&gt;: Popular web servers like Apache and Nginx are written in C to handle high loads and provide fast response times.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You get my point, C is everywhere and it excels in scenarios where performance, efficiency, and low-level system access are paramount. &lt;/p&gt;

&lt;h2&gt;
  
  
  When C Isn’t the Right Choice ?
&lt;/h2&gt;

&lt;p&gt;Now as you can see I'm a pretty big proponent of C and you'll often hear people who exclusively develop in C (sadly not me), say: "Anything and everything under the sun can be written in C". Which is true, if you were to remove all programming languages from existence save one, my choice would always be C as we could potentially rebuild everything from the ground up.&lt;/p&gt;

&lt;p&gt;However even I have to admit that C may not always be the best choice everything has it's ups and downs.&lt;/p&gt;

&lt;p&gt;So C may not be your best choice when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rapid Prototyping&lt;/strong&gt;: If you need to quickly prototype an idea or concept, higher-level languages like Python or JavaScript may be more suitable due to their ease of use and extensive libraries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Development&lt;/strong&gt;: For building web applications, languages like JavaScript (with frameworks like React, Angular, or Vue) or Python (with frameworks like Django or Flask) are often preferred due to their ease of use and extensive web development libraries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Science and Machine Learning&lt;/strong&gt;: Languages like Python and R are commonly used in data science and machine learning due to their extensive libraries and frameworks (e.g., TensorFlow, PyTorch, Pandas).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mobile App Development&lt;/strong&gt;: For mobile app development, languages like Swift (for iOS) and Kotlin (for Android) are often preferred due to their platform-specific features and ease of use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Safety&lt;/strong&gt;: If memory safety is a primary concern, languages like Rust or Go may be more suitable due to their built-in memory safety features and garbage collection(Go).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ease of Learning&lt;/strong&gt;: If you're new to programming, languages like Python or JavaScript may be easier to learn due to their simpler syntax and extensive learning resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Community and Ecosystem&lt;/strong&gt;: If you need access to a large community and ecosystem of libraries and frameworks, languages like JavaScript, Python, or Java may be more suitable due to their extensive ecosystems.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;So there you have it — why I still reach for C for certain projects. It’s not always the fastest or easiest choice, but for small, efficient programs that need to run lean and mean, it often makes sense. C isn’t flashy or trendy, but it gets the job done, and sometimes that’s all that matters.&lt;/p&gt;

&lt;p&gt;Curious to hear how others tackle similar projects — C, Rust, Go, or something else?&lt;/p&gt;

</description>
      <category>c</category>
      <category>programming</category>
      <category>softwaredevelopment</category>
    </item>
  </channel>
</rss>
