{
  "@context": "https://schema.org",
  "@id": "https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-15-my-c23-conventions/#webpage",
  "@type": [
    "WebPage",
    "ArchiveComponent"
  ],
  "articleBody": "This is one of a three-part mini series about how I’m using modern C as of late 2026.\n\nBefore C23, I was [taken in](https://www.youtube.com/watch?v=443UNeGrFoM) by *How I Program C* for years and years, and would even feel guilty for choosing C99 instead of `-ansi -pedantic`. But holy crap man I’m done being a doomsday prepper! These days I maintain a large-enough project with enough users where I could stand less foot-shooting. (I’ve also got bills to pay.)\n\nSo here’s three modern C conventions which started to rise naturally from adding `-std=c23` to my zig build:\n\n## 1\\. Unused? Maybe. (Yes)\n\nOur first convention is centered around `[[nodiscard]]`. If I draw an app window or panel I require the caller to interrogate the rect of the thing that was just rendered:\n\n```c\n[[nodiscard]] rect_t draw_screen(/* ... */) {\n    rect_t screen_r = { };\n    // Draw stuff\n    // ...\n    return screen_r;\n}\n```\n\nIt’s a genuine business logic bug if the caller doesn’t capture the rectangle for later processing. Therefore `[[nodiscard]]` has been such a godsend for fixing bugs… Except there’s a choice few instances where I will draw something with *no real use* for the resulting rect. I’d rather ignore the return value:\n\n```c\nenum screen_opt popup_options = /* ... */ ;\ndraw_screen(popup_options, /* ... */); // ERROR: damn you [[nodiscard]]\n```\n\nClang will error out here. I could maybe capture the var and never use it but…\n\n```c\nrect_t rect = draw_screen(/* ... */); // ERROR: unused variable (I have `-Werror` enabled)\n```\n\nPre-C23 one might solve this through an `UNUSED()` macro:\n\n```c\n#ifndef UNUSED\n#define UNUSED(x) (void)(x)\n#endif\n\nrect_t rect = draw_screen(/* ... */);\nUNUSED(rect); // Silences the compiler, but now I'm sad.\n```\n\nBut now with C23 we can delete the macro in favor of the native `[[maybe_unused]]`:\n\n```c\n[[maybe_unused]] rect_t rect = draw_screen(...); // OK\n```\n\nPhew I feel better. I’ve also noticed this is happening all over the place: lots of different routines returning different types that are marked with `[[nodiscard]]`. So for all the unused cases why not just go for:\n\n```c\n[[maybe_unused]] auto _ = foo(); // A new pattern emerges\n```\n\nBonus: For any macro fans out there a fellow indie dev (Zardium) offered an additional improvement. From our chat server:\n\n> Zardium: #define \\_ \\[\\[maybe\\_unused\\]\\] auto CONCAT\\_MACRO(zz,**COUNTER**)\n\n> Abner: ahh, unique varname. nice one\n\n> Zardium: you could take the auto out if you don’t want it to look so weird. then it acts exactly like the \\_ in other languages, where it can be redefined\n\n> Abner: Yep totally. Although here I was celebrating my removal of a macro… XD\n\n## 2\\. More Perfect Unions\n\nI’ve been cheated and lied to: designated initializers DO NOT clear my struct to zero and I can’t even trust `{0}` anymore. (Talk about a betrayal.) The problem is I’ll often have data types like this contrived timestamp:\n\n```c\ntypedef struct {\n    Type type;\n    char buf[MAX];\n    union {\n        time_t time;\n        float dt;\n        date_t date;\n    };\n} stamp_t;\n```\n\nAnd if I partially initialize one:\n\n```c\nstamp_t stamp = { .type = STAMP_TYPE_BLA_BLA };\n```\n\nWhile it does mean we successfully zeroed out `buf` (thanks to [empty initialization](https://en.cppreference.com/c/language/initialization#Empty_initialization)), those union members are just a different beast. It also doesn’t matter if we go with `stamp_t stamp = {0}` instead. My dear reader: I’ve made the costly mistake of assuming `date` would clear to zero!\n\nAs I Spongebob-panicked in public, Lobsters member [accelbread](https://lobste.rs/~accelbread) offered a sage revelation:\n\n> accelbread: It used to be that GCC would fully zero the union anyways, and clang would only zero the time\\_t, though around a year ago they swapped behaviors iirc. Either way, standard only requires the time\\_t be zeroed and not whole union. GCC has a flag to always zero whole union. Also C23 initialization always inits whole union\n\nWait WHAT? clang and gcc SWAPPED what now? I won’t investigate further. Everything about this is *dangerous af* unless we hug C23 tightly and never let go:\n\n```c\nstamp_t stamp = { }; // Fully clears to zero (C23)\nstamp.type = STAMP_TYPE_BLA_BLA;\n```\n\nAccelbread went on to say two more things:\n\n1. *“Note the C23 zero init whole thing is nothing in the brackets, not 0 in brackets; { 0 } does same thing as before C23”*\n2. *“Also if you control union layout, putting largest member first is safest”*\n\nYessir.\n\n## 3\\. Enums In C Were Magic (And I Don’t Like It)\n\nIf I want to customize some dialog box I’ll use bit flags:\n\n```c\nenum {\n    OPT_AUTOSCROLL = (1 << 0),\n    OPT_NOCURSOR   = (1 << 1),\n    OPT_CENTERED   = (1 << 2),\n    OPT_NOCLOSE    = (1 << 3),\n    // ...\n};\n\n[[nodiscard]] bool draw_warning(const char* msg, int opt);\n```\n\nEven if we disagree with this approach lots of relevant libraries or projects do it:\n\n```c\n_ = draw_warning(\"you done messed up A-A-RON\", OPT_CENTERED | OPT_NOCLOSE);\n```\n\nOf course, erroneous bit shifting will land anyone into trouble and yield comical numbers. Thankfully C enums are a plain `int` underneath so the compiler will warn us if we defined a bad constant inside the enum that won’t fit.\n\n:)\n\nI’m kidding. Sigh. Well, I’m growing fearful of sage revelations but this one comes from [Modern C](https://gustedt.gitlabpages.inria.fr/modern-c/) by Jens Gustedt:\n\n> Jens: If enumeration constants do not fit into signed int, if possible, the enumeration type is adjusted such that it can store all enumeration constants.\n\nNoooooo!\n\n> Jens: The property that an an enumeration type is adjusted such that it fits all its constants could have surprising effects\n\n*sobs*\n\n> Jens: So, it is preferable to specify the underlying integer type explicitly\n\nTHERE IS HOPE?\n\n```c\nenum options : int {\n    OPT_AUTOSCROLL = (1 << 0),\n    OPT_NOCURSOR   = (1 << 1),\n    OPT_CENTERED   = (1 << 2),\n    OPT_NOCLOSE    = (1 << 3),\n    // ...\n};\n```\n\nIt compiles! We shoot. We score. We stick with C23.\n\n## Epilogue\n\nIn part two of our mini series I’ll discuss three more conventions that I’ve fleshed out thanks to modern C: starting with safer macro usage (forgive me Zig folks.)\n\n*P.S. If you want to shape this discussion [maybe join](https://handmadecities.com/chat) the Handmade Cities server!*\n\n-Abner\n",
  "authors": [
    "abner.page by abnercoimbre"
  ],
  "captured_at": "2026-10-16T22:01:23.127136231+00:00",
  "category": "news",
  "content_html": "<p>This is one of a three-part mini series about how I’m using modern C as of late 2026.</p>\n<p>Before C23, I was <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.youtube.com/watch?v=443UNeGrFoM\">taken in</a> by <em>How I Program C</em> for years and years, and would even feel guilty for choosing C99 instead of <code>-ansi -pedantic</code>. But holy crap man I’m done being a doomsday prepper! These days I maintain a large-enough project with enough users where I could stand less foot-shooting. (I’ve also got bills to pay.)</p>\n<p>So here’s three modern C conventions which started to rise naturally from adding <code>-std=c23</code> to my zig build:</p>\n<h2>1. Unused? Maybe. (Yes)</h2>\n<p>Our first convention is centered around <code>[[nodiscard]]</code>. If I draw an app window or panel I require the caller to interrogate the rect of the thing that was just rendered:</p>\n<pre><code class=\"language-c\"><span class=\"code-snippet\" data-language=\"C\"><span class=\"syntax-source syntax-c\"><span class=\"syntax-meta syntax-brackets syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c\">[</span><span class=\"syntax-meta syntax-brackets syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c\">[</span>nodiscard<span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c\">]</span></span><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c\">]</span></span> rect_t <span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-variable syntax-function syntax-c\">draw_screen</span><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-c\">(</span></span></span><span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-comment syntax-block syntax-c\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-c\">/*</span> ... <span class=\"syntax-punctuation syntax-definition syntax-comment syntax-c\">*/</span></span></span></span><span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-c\">)</span></span></span> <span class=\"syntax-meta syntax-block syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-block syntax-begin syntax-c\">{</span>\n    rect_t screen_r <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-c\">=</span> <span class=\"syntax-meta syntax-block syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-block syntax-begin syntax-c\">{</span> <span class=\"syntax-punctuation syntax-section syntax-block syntax-end syntax-c\">}</span></span><span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span>\n    <span class=\"syntax-comment syntax-line syntax-double-slash syntax-c\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-c\">//</span> Draw stuff\n</span>    <span class=\"syntax-comment syntax-line syntax-double-slash syntax-c\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-c\">//</span> ...\n</span>    <span class=\"syntax-keyword syntax-control syntax-c\">return</span> screen_r<span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span>\n<span class=\"syntax-punctuation syntax-section syntax-block syntax-end syntax-c\">}</span></span>\n</span></span></code></pre>\n<p>It’s a genuine business logic bug if the caller doesn’t capture the rectangle for later processing. Therefore <code>[[nodiscard]]</code> has been such a godsend for fixing bugs… Except there’s a choice few instances where I will draw something with <em>no real use</em> for the resulting rect. I’d rather ignore the return value:</p>\n<pre><code class=\"language-c\"><span class=\"code-snippet\" data-language=\"C\"><span class=\"syntax-source syntax-c\"><span class=\"syntax-storage syntax-type syntax-c\">enum</span> screen_opt popup_options <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-c\">=</span> <span class=\"syntax-comment syntax-block syntax-c\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-c\">/*</span> ... <span class=\"syntax-punctuation syntax-definition syntax-comment syntax-c\">*/</span></span> <span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span>\n<span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-variable syntax-function syntax-c\">draw_screen</span><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-c\">(</span></span></span><span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-meta syntax-group syntax-c\">popup_options<span class=\"syntax-punctuation syntax-separator syntax-c\">,</span> <span class=\"syntax-comment syntax-block syntax-c\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-c\">/*</span> ... <span class=\"syntax-punctuation syntax-definition syntax-comment syntax-c\">*/</span></span></span><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-c\">)</span></span></span><span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span> <span class=\"syntax-comment syntax-line syntax-double-slash syntax-c\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-c\">//</span> ERROR: damn you [[nodiscard]]\n</span></span></span></code></pre>\n<p>Clang will error out here. I could maybe capture the var and never use it but…</p>\n<pre><code class=\"language-c\"><span class=\"code-snippet\" data-language=\"C\"><span class=\"syntax-source syntax-c\">rect_t rect <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-c\">=</span> <span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-variable syntax-function syntax-c\">draw_screen</span><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-c\">(</span></span></span><span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-comment syntax-block syntax-c\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-c\">/*</span> ... <span class=\"syntax-punctuation syntax-definition syntax-comment syntax-c\">*/</span></span></span></span><span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-c\">)</span></span></span><span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span> <span class=\"syntax-comment syntax-line syntax-double-slash syntax-c\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-c\">//</span> ERROR: unused variable (I have `-Werror` enabled)\n</span></span></span></code></pre>\n<p>Pre-C23 one might solve this through an <code>UNUSED()</code> macro:</p>\n<pre><code class=\"language-c\"><span class=\"code-snippet\" data-language=\"C\"><span class=\"syntax-source syntax-c\"><span class=\"syntax-meta syntax-preprocessor syntax-c\"><span class=\"syntax-keyword syntax-control syntax-import syntax-c\">#ifndef</span> UNUSED\n</span><span class=\"syntax-meta syntax-preprocessor syntax-macro syntax-c\"><span class=\"syntax-keyword syntax-control syntax-import syntax-define syntax-c\">#define</span></span><span class=\"syntax-meta syntax-preprocessor syntax-macro syntax-c\"> <span class=\"syntax-entity syntax-name syntax-function syntax-preprocessor syntax-c\">UNUSED</span></span><span class=\"syntax-meta syntax-preprocessor syntax-macro syntax-parameters syntax-c\"><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-c\">(</span><span class=\"syntax-variable syntax-parameter syntax-c\">x</span><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-c\">)</span></span></span><span class=\"syntax-meta syntax-preprocessor syntax-macro syntax-c\"> <span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-c\">(</span><span class=\"syntax-storage syntax-type syntax-c\">void</span><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-c\">)</span></span><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-c\">(</span>x<span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-c\">)</span></span></span>\n<span class=\"syntax-meta syntax-preprocessor syntax-c\"><span class=\"syntax-keyword syntax-control syntax-import syntax-c\">#endif</span></span>\n\nrect_t rect <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-c\">=</span> <span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-variable syntax-function syntax-c\">draw_screen</span><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-c\">(</span></span></span><span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-comment syntax-block syntax-c\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-c\">/*</span> ... <span class=\"syntax-punctuation syntax-definition syntax-comment syntax-c\">*/</span></span></span></span><span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-c\">)</span></span></span><span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span>\n<span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-variable syntax-function syntax-c\">UNUSED</span><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-c\">(</span></span></span><span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-meta syntax-group syntax-c\">rect</span><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-c\">)</span></span></span><span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span> <span class=\"syntax-comment syntax-line syntax-double-slash syntax-c\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-c\">//</span> Silences the compiler, but now I&#39;m sad.\n</span></span></span></code></pre>\n<p>But now with C23 we can delete the macro in favor of the native <code>[[maybe_unused]]</code>:</p>\n<pre><code class=\"language-c\"><span class=\"code-snippet\" data-language=\"C\"><span class=\"syntax-source syntax-c\"><span class=\"syntax-meta syntax-brackets syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c\">[</span><span class=\"syntax-meta syntax-brackets syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c\">[</span>maybe_unused<span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c\">]</span></span><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c\">]</span></span> rect_t rect <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-c\">=</span> <span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-variable syntax-function syntax-c\">draw_screen</span><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-c\">(</span></span></span><span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-keyword syntax-operator syntax-variadic syntax-c\">...</span></span></span><span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-c\">)</span></span></span><span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span> <span class=\"syntax-comment syntax-line syntax-double-slash syntax-c\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-c\">//</span> OK\n</span></span></span></code></pre>\n<p>Phew I feel better. I’ve also noticed this is happening all over the place: lots of different routines returning different types that are marked with <code>[[nodiscard]]</code>. So for all the unused cases why not just go for:</p>\n<pre><code class=\"language-c\"><span class=\"code-snippet\" data-language=\"C\"><span class=\"syntax-source syntax-c\"><span class=\"syntax-meta syntax-brackets syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c\">[</span><span class=\"syntax-meta syntax-brackets syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c\">[</span>maybe_unused<span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c\">]</span></span><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c\">]</span></span> <span class=\"syntax-storage syntax-type syntax-c\">auto</span> _ <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-c\">=</span> <span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-variable syntax-function syntax-c\">foo</span><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-c\">(</span></span></span><span class=\"syntax-meta syntax-function-call syntax-c\"></span><span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-c\">)</span></span></span><span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span> <span class=\"syntax-comment syntax-line syntax-double-slash syntax-c\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-c\">//</span> A new pattern emerges\n</span></span></span></code></pre>\n<p>Bonus: For any macro fans out there a fellow indie dev (Zardium) offered an additional improvement. From our chat server:</p>\n<blockquote>\n<p>Zardium: #define _ [[maybe_unused]] auto CONCAT_MACRO(zz,<strong>COUNTER</strong>)</p>\n</blockquote>\n<blockquote>\n<p>Abner: ahh, unique varname. nice one</p>\n</blockquote>\n<blockquote>\n<p>Zardium: you could take the auto out if you don’t want it to look so weird. then it acts exactly like the _ in other languages, where it can be redefined</p>\n</blockquote>\n<blockquote>\n<p>Abner: Yep totally. Although here I was celebrating my removal of a macro… XD</p>\n</blockquote>\n<h2>2. More Perfect Unions</h2>\n<p>I’ve been cheated and lied to: designated initializers DO NOT clear my struct to zero and I can’t even trust <code>{0}</code> anymore. (Talk about a betrayal.) The problem is I’ll often have data types like this contrived timestamp:</p>\n<pre><code class=\"language-c\"><span class=\"code-snippet\" data-language=\"C\"><span class=\"syntax-source syntax-c\"><span class=\"syntax-storage syntax-type syntax-c\">typedef</span> <span class=\"syntax-storage syntax-type syntax-c\">struct</span> <span class=\"syntax-meta syntax-block syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-block syntax-begin syntax-c\">{</span>\n    Type type<span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span>\n    <span class=\"syntax-storage syntax-type syntax-c\">char</span> buf<span class=\"syntax-meta syntax-brackets syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c\">[</span>MAX<span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c\">]</span></span><span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span>\n    <span class=\"syntax-storage syntax-type syntax-c\">union</span> <span class=\"syntax-meta syntax-block syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-block syntax-begin syntax-c\">{</span>\n        <span class=\"syntax-support syntax-type syntax-sys-types syntax-c\">time_t</span> time<span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span>\n        <span class=\"syntax-storage syntax-type syntax-c\">float</span> dt<span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span>\n        date_t date<span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span>\n    <span class=\"syntax-punctuation syntax-section syntax-block syntax-end syntax-c\">}</span></span><span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span>\n<span class=\"syntax-punctuation syntax-section syntax-block syntax-end syntax-c\">}</span></span> <span class=\"syntax-entity syntax-name syntax-type syntax-typedef syntax-c\">stamp_t</span><span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span>\n</span></span></code></pre>\n<p>And if I partially initialize one:</p>\n<pre><code class=\"language-c\"><span class=\"code-snippet\" data-language=\"C\"><span class=\"syntax-source syntax-c\">stamp_t stamp <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-c\">=</span> <span class=\"syntax-meta syntax-block syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-block syntax-begin syntax-c\">{</span> <span class=\"syntax-punctuation syntax-accessor syntax-c\">.</span><span class=\"syntax-variable syntax-other syntax-member syntax-c\">type</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-c\">=</span> STAMP_TYPE_BLA_BLA <span class=\"syntax-punctuation syntax-section syntax-block syntax-end syntax-c\">}</span></span><span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span>\n</span></span></code></pre>\n<p>While it does mean we successfully zeroed out <code>buf</code> (thanks to <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://en.cppreference.com/c/language/initialization#Empty_initialization\">empty initialization</a>), those union members are just a different beast. It also doesn’t matter if we go with <code>stamp_t stamp = {0}</code> instead. My dear reader: I’ve made the costly mistake of assuming <code>date</code> would clear to zero!</p>\n<p>As I Spongebob-panicked in public, Lobsters member <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://lobste.rs/~accelbread\">accelbread</a> offered a sage revelation:</p>\n<blockquote>\n<p>accelbread: It used to be that GCC would fully zero the union anyways, and clang would only zero the time_t, though around a year ago they swapped behaviors iirc. Either way, standard only requires the time_t be zeroed and not whole union. GCC has a flag to always zero whole union. Also C23 initialization always inits whole union</p>\n</blockquote>\n<p>Wait WHAT? clang and gcc SWAPPED what now? I won’t investigate further. Everything about this is <em>dangerous af</em> unless we hug C23 tightly and never let go:</p>\n<pre><code class=\"language-c\"><span class=\"code-snippet\" data-language=\"C\"><span class=\"syntax-source syntax-c\">stamp_t stamp <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-c\">=</span> <span class=\"syntax-meta syntax-block syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-block syntax-begin syntax-c\">{</span> <span class=\"syntax-punctuation syntax-section syntax-block syntax-end syntax-c\">}</span></span><span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span> <span class=\"syntax-comment syntax-line syntax-double-slash syntax-c\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-c\">//</span> Fully clears to zero (C23)\n</span>stamp<span class=\"syntax-punctuation syntax-accessor syntax-c\">.</span><span class=\"syntax-variable syntax-other syntax-member syntax-c\">type</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-c\">=</span> STAMP_TYPE_BLA_BLA<span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span>\n</span></span></code></pre>\n<p>Accelbread went on to say two more things:</p>\n<ol>\n<li><em>“Note the C23 zero init whole thing is nothing in the brackets, not 0 in brackets; { 0 } does same thing as before C23”</em></li>\n<li><em>“Also if you control union layout, putting largest member first is safest”</em></li>\n</ol>\n<p>Yessir.</p>\n<h2>3. Enums In C Were Magic (And I Don’t Like It)</h2>\n<p>If I want to customize some dialog box I’ll use bit flags:</p>\n<pre><code class=\"language-c\"><span class=\"code-snippet\" data-language=\"C\"><span class=\"syntax-source syntax-c\"><span class=\"syntax-meta syntax-enum syntax-c\"><span class=\"syntax-storage syntax-type syntax-c\">enum</span> <span class=\"syntax-meta syntax-block syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-block syntax-begin syntax-c\">{</span></span></span><span class=\"syntax-meta syntax-enum syntax-c\"><span class=\"syntax-meta syntax-block syntax-c\">\n    OPT_AUTOSCROLL <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-c\">=</span> <span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-c\">(</span><span class=\"syntax-constant syntax-numeric syntax-c\">1</span> <span class=\"syntax-keyword syntax-operator syntax-arithmetic syntax-c\">&lt;&lt;</span> <span class=\"syntax-constant syntax-numeric syntax-c\">0</span><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-c\">)</span></span><span class=\"syntax-punctuation syntax-separator syntax-c\">,</span>\n    OPT_NOCURSOR   <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-c\">=</span> <span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-c\">(</span><span class=\"syntax-constant syntax-numeric syntax-c\">1</span> <span class=\"syntax-keyword syntax-operator syntax-arithmetic syntax-c\">&lt;&lt;</span> <span class=\"syntax-constant syntax-numeric syntax-c\">1</span><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-c\">)</span></span><span class=\"syntax-punctuation syntax-separator syntax-c\">,</span>\n    OPT_CENTERED   <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-c\">=</span> <span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-c\">(</span><span class=\"syntax-constant syntax-numeric syntax-c\">1</span> <span class=\"syntax-keyword syntax-operator syntax-arithmetic syntax-c\">&lt;&lt;</span> <span class=\"syntax-constant syntax-numeric syntax-c\">2</span><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-c\">)</span></span><span class=\"syntax-punctuation syntax-separator syntax-c\">,</span>\n    OPT_NOCLOSE    <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-c\">=</span> <span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-c\">(</span><span class=\"syntax-constant syntax-numeric syntax-c\">1</span> <span class=\"syntax-keyword syntax-operator syntax-arithmetic syntax-c\">&lt;&lt;</span> <span class=\"syntax-constant syntax-numeric syntax-c\">3</span><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-c\">)</span></span><span class=\"syntax-punctuation syntax-separator syntax-c\">,</span>\n    <span class=\"syntax-comment syntax-line syntax-double-slash syntax-c\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-c\">//</span> ...\n</span></span></span><span class=\"syntax-meta syntax-enum syntax-c\"><span class=\"syntax-meta syntax-block syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-block syntax-end syntax-c\">}</span></span></span><span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span>\n\n<span class=\"syntax-meta syntax-brackets syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c\">[</span><span class=\"syntax-meta syntax-brackets syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c\">[</span>nodiscard<span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c\">]</span></span><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c\">]</span></span> <span class=\"syntax-storage syntax-type syntax-c\">bool</span> <span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-variable syntax-function syntax-c\">draw_warning</span><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-c\">(</span></span></span><span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-storage syntax-modifier syntax-c\">const</span> <span class=\"syntax-storage syntax-type syntax-c\">char</span><span class=\"syntax-keyword syntax-operator syntax-c\">*</span> msg<span class=\"syntax-punctuation syntax-separator syntax-c\">,</span> <span class=\"syntax-storage syntax-type syntax-c\">int</span> opt</span></span><span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-c\">)</span></span></span><span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span>\n</span></span></code></pre>\n<p>Even if we disagree with this approach lots of relevant libraries or projects do it:</p>\n<pre><code class=\"language-c\"><span class=\"code-snippet\" data-language=\"C\"><span class=\"syntax-source syntax-c\">_ <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-c\">=</span> <span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-variable syntax-function syntax-c\">draw_warning</span><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-c\">(</span></span></span><span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-string syntax-quoted syntax-double syntax-c\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-c\">&quot;</span>you done messed up A-A-RON<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-c\">&quot;</span></span><span class=\"syntax-punctuation syntax-separator syntax-c\">,</span> OPT_CENTERED <span class=\"syntax-keyword syntax-operator syntax-arithmetic syntax-c\">|</span> OPT_NOCLOSE</span></span><span class=\"syntax-meta syntax-function-call syntax-c\"><span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-c\">)</span></span></span><span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span>\n</span></span></code></pre>\n<p>Of course, erroneous bit shifting will land anyone into trouble and yield comical numbers. Thankfully C enums are a plain <code>int</code> underneath so the compiler will warn us if we defined a bad constant inside the enum that won’t fit.</p>\n<p>:)</p>\n<p>I’m kidding. Sigh. Well, I’m growing fearful of sage revelations but this one comes from <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://gustedt.gitlabpages.inria.fr/modern-c/\">Modern C</a> by Jens Gustedt:</p>\n<blockquote>\n<p>Jens: If enumeration constants do not fit into signed int, if possible, the enumeration type is adjusted such that it can store all enumeration constants.</p>\n</blockquote>\n<p>Noooooo!</p>\n<blockquote>\n<p>Jens: The property that an an enumeration type is adjusted such that it fits all its constants could have surprising effects</p>\n</blockquote>\n<p><em>sobs</em></p>\n<blockquote>\n<p>Jens: So, it is preferable to specify the underlying integer type explicitly</p>\n</blockquote>\n<p>THERE IS HOPE?</p>\n<pre><code class=\"language-c\"><span class=\"code-snippet\" data-language=\"C\"><span class=\"syntax-source syntax-c\"><span class=\"syntax-meta syntax-enum syntax-c\"><span class=\"syntax-storage syntax-type syntax-c\">enum</span> <span class=\"syntax-entity syntax-name syntax-enum syntax-c\">options</span> : <span class=\"syntax-entity syntax-name syntax-enum syntax-c\">int</span> <span class=\"syntax-meta syntax-block syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-block syntax-begin syntax-c\">{</span></span></span><span class=\"syntax-meta syntax-enum syntax-c\"><span class=\"syntax-meta syntax-block syntax-c\">\n    OPT_AUTOSCROLL <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-c\">=</span> <span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-c\">(</span><span class=\"syntax-constant syntax-numeric syntax-c\">1</span> <span class=\"syntax-keyword syntax-operator syntax-arithmetic syntax-c\">&lt;&lt;</span> <span class=\"syntax-constant syntax-numeric syntax-c\">0</span><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-c\">)</span></span><span class=\"syntax-punctuation syntax-separator syntax-c\">,</span>\n    OPT_NOCURSOR   <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-c\">=</span> <span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-c\">(</span><span class=\"syntax-constant syntax-numeric syntax-c\">1</span> <span class=\"syntax-keyword syntax-operator syntax-arithmetic syntax-c\">&lt;&lt;</span> <span class=\"syntax-constant syntax-numeric syntax-c\">1</span><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-c\">)</span></span><span class=\"syntax-punctuation syntax-separator syntax-c\">,</span>\n    OPT_CENTERED   <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-c\">=</span> <span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-c\">(</span><span class=\"syntax-constant syntax-numeric syntax-c\">1</span> <span class=\"syntax-keyword syntax-operator syntax-arithmetic syntax-c\">&lt;&lt;</span> <span class=\"syntax-constant syntax-numeric syntax-c\">2</span><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-c\">)</span></span><span class=\"syntax-punctuation syntax-separator syntax-c\">,</span>\n    OPT_NOCLOSE    <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-c\">=</span> <span class=\"syntax-meta syntax-group syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-group syntax-begin syntax-c\">(</span><span class=\"syntax-constant syntax-numeric syntax-c\">1</span> <span class=\"syntax-keyword syntax-operator syntax-arithmetic syntax-c\">&lt;&lt;</span> <span class=\"syntax-constant syntax-numeric syntax-c\">3</span><span class=\"syntax-punctuation syntax-section syntax-group syntax-end syntax-c\">)</span></span><span class=\"syntax-punctuation syntax-separator syntax-c\">,</span>\n    <span class=\"syntax-comment syntax-line syntax-double-slash syntax-c\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-c\">//</span> ...\n</span></span></span><span class=\"syntax-meta syntax-enum syntax-c\"><span class=\"syntax-meta syntax-block syntax-c\"><span class=\"syntax-punctuation syntax-section syntax-block syntax-end syntax-c\">}</span></span></span><span class=\"syntax-punctuation syntax-terminator syntax-c\">;</span>\n</span></span></code></pre>\n<p>It compiles! We shoot. We score. We stick with C23.</p>\n<h2>Epilogue</h2>\n<p>In part two of our mini series I’ll discuss three more conventions that I’ve fleshed out thanks to modern C: starting with safer macro usage (forgive me Zig folks.)</p>\n<p><em>P.S. If you want to shape this discussion <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://handmadecities.com/chat\">maybe join</a> the Handmade Cities server!</em></p>\n<p>-Abner</p>\n",
  "content_markdown": "This is one of a three-part mini series about how I’m using modern C as of late 2026.\n\nBefore C23, I was [taken in](https://www.youtube.com/watch?v=443UNeGrFoM) by *How I Program C* for years and years, and would even feel guilty for choosing C99 instead of `-ansi -pedantic`. But holy crap man I’m done being a doomsday prepper! These days I maintain a large-enough project with enough users where I could stand less foot-shooting. (I’ve also got bills to pay.)\n\nSo here’s three modern C conventions which started to rise naturally from adding `-std=c23` to my zig build:\n\n## 1\\. Unused? Maybe. (Yes)\n\nOur first convention is centered around `[[nodiscard]]`. If I draw an app window or panel I require the caller to interrogate the rect of the thing that was just rendered:\n\n```c\n[[nodiscard]] rect_t draw_screen(/* ... */) {\n    rect_t screen_r = { };\n    // Draw stuff\n    // ...\n    return screen_r;\n}\n```\n\nIt’s a genuine business logic bug if the caller doesn’t capture the rectangle for later processing. Therefore `[[nodiscard]]` has been such a godsend for fixing bugs… Except there’s a choice few instances where I will draw something with *no real use* for the resulting rect. I’d rather ignore the return value:\n\n```c\nenum screen_opt popup_options = /* ... */ ;\ndraw_screen(popup_options, /* ... */); // ERROR: damn you [[nodiscard]]\n```\n\nClang will error out here. I could maybe capture the var and never use it but…\n\n```c\nrect_t rect = draw_screen(/* ... */); // ERROR: unused variable (I have `-Werror` enabled)\n```\n\nPre-C23 one might solve this through an `UNUSED()` macro:\n\n```c\n#ifndef UNUSED\n#define UNUSED(x) (void)(x)\n#endif\n\nrect_t rect = draw_screen(/* ... */);\nUNUSED(rect); // Silences the compiler, but now I'm sad.\n```\n\nBut now with C23 we can delete the macro in favor of the native `[[maybe_unused]]`:\n\n```c\n[[maybe_unused]] rect_t rect = draw_screen(...); // OK\n```\n\nPhew I feel better. I’ve also noticed this is happening all over the place: lots of different routines returning different types that are marked with `[[nodiscard]]`. So for all the unused cases why not just go for:\n\n```c\n[[maybe_unused]] auto _ = foo(); // A new pattern emerges\n```\n\nBonus: For any macro fans out there a fellow indie dev (Zardium) offered an additional improvement. From our chat server:\n\n> Zardium: #define \\_ \\[\\[maybe\\_unused\\]\\] auto CONCAT\\_MACRO(zz,**COUNTER**)\n\n> Abner: ahh, unique varname. nice one\n\n> Zardium: you could take the auto out if you don’t want it to look so weird. then it acts exactly like the \\_ in other languages, where it can be redefined\n\n> Abner: Yep totally. Although here I was celebrating my removal of a macro… XD\n\n## 2\\. More Perfect Unions\n\nI’ve been cheated and lied to: designated initializers DO NOT clear my struct to zero and I can’t even trust `{0}` anymore. (Talk about a betrayal.) The problem is I’ll often have data types like this contrived timestamp:\n\n```c\ntypedef struct {\n    Type type;\n    char buf[MAX];\n    union {\n        time_t time;\n        float dt;\n        date_t date;\n    };\n} stamp_t;\n```\n\nAnd if I partially initialize one:\n\n```c\nstamp_t stamp = { .type = STAMP_TYPE_BLA_BLA };\n```\n\nWhile it does mean we successfully zeroed out `buf` (thanks to [empty initialization](https://en.cppreference.com/c/language/initialization#Empty_initialization)), those union members are just a different beast. It also doesn’t matter if we go with `stamp_t stamp = {0}` instead. My dear reader: I’ve made the costly mistake of assuming `date` would clear to zero!\n\nAs I Spongebob-panicked in public, Lobsters member [accelbread](https://lobste.rs/~accelbread) offered a sage revelation:\n\n> accelbread: It used to be that GCC would fully zero the union anyways, and clang would only zero the time\\_t, though around a year ago they swapped behaviors iirc. Either way, standard only requires the time\\_t be zeroed and not whole union. GCC has a flag to always zero whole union. Also C23 initialization always inits whole union\n\nWait WHAT? clang and gcc SWAPPED what now? I won’t investigate further. Everything about this is *dangerous af* unless we hug C23 tightly and never let go:\n\n```c\nstamp_t stamp = { }; // Fully clears to zero (C23)\nstamp.type = STAMP_TYPE_BLA_BLA;\n```\n\nAccelbread went on to say two more things:\n\n1. *“Note the C23 zero init whole thing is nothing in the brackets, not 0 in brackets; { 0 } does same thing as before C23”*\n2. *“Also if you control union layout, putting largest member first is safest”*\n\nYessir.\n\n## 3\\. Enums In C Were Magic (And I Don’t Like It)\n\nIf I want to customize some dialog box I’ll use bit flags:\n\n```c\nenum {\n    OPT_AUTOSCROLL = (1 << 0),\n    OPT_NOCURSOR   = (1 << 1),\n    OPT_CENTERED   = (1 << 2),\n    OPT_NOCLOSE    = (1 << 3),\n    // ...\n};\n\n[[nodiscard]] bool draw_warning(const char* msg, int opt);\n```\n\nEven if we disagree with this approach lots of relevant libraries or projects do it:\n\n```c\n_ = draw_warning(\"you done messed up A-A-RON\", OPT_CENTERED | OPT_NOCLOSE);\n```\n\nOf course, erroneous bit shifting will land anyone into trouble and yield comical numbers. Thankfully C enums are a plain `int` underneath so the compiler will warn us if we defined a bad constant inside the enum that won’t fit.\n\n:)\n\nI’m kidding. Sigh. Well, I’m growing fearful of sage revelations but this one comes from [Modern C](https://gustedt.gitlabpages.inria.fr/modern-c/) by Jens Gustedt:\n\n> Jens: If enumeration constants do not fit into signed int, if possible, the enumeration type is adjusted such that it can store all enumeration constants.\n\nNoooooo!\n\n> Jens: The property that an an enumeration type is adjusted such that it fits all its constants could have surprising effects\n\n*sobs*\n\n> Jens: So, it is preferable to specify the underlying integer type explicitly\n\nTHERE IS HOPE?\n\n```c\nenum options : int {\n    OPT_AUTOSCROLL = (1 << 0),\n    OPT_NOCURSOR   = (1 << 1),\n    OPT_CENTERED   = (1 << 2),\n    OPT_NOCLOSE    = (1 << 3),\n    // ...\n};\n```\n\nIt compiles! We shoot. We score. We stick with C23.\n\n## Epilogue\n\nIn part two of our mini series I’ll discuss three more conventions that I’ve fleshed out thanks to modern C: starting with safer macro usage (forgive me Zig folks.)\n\n*P.S. If you want to shape this discussion [maybe join](https://handmadecities.com/chat) the Handmade Cities server!*\n\n-Abner\n",
  "dateCreated": "2026-10-16T22:01:23.127136231+00:00",
  "date_created": "2026-10-15T15:32:15+00:00",
  "date_modified": "2026-10-15T15:32:15+00:00",
  "date_published": "2026-10-15T15:32:15+00:00",
  "description": "Comments",
  "external_url": "https://abner.page/post/c23-conventions-1/",
  "git": {
    "commit": "ebb0941c5c06a2ab2377342d607535262a3e969e",
    "path": "items/lobste-rs/2026/10/2026-10-15-my-c23-conventions.md",
    "permalink": "https://github.com/aymericbeaumet/aggr-instance/blob/ebb0941c5c06a2ab2377342d607535262a3e969e/items/lobste-rs/2026/10/2026-10-15-my-c23-conventions.md"
  },
  "id": "urn:aggr:item:30606cb70eb4e7f84f6bdd70afea228b1000544d",
  "isBasedOn": {
    "@id": "https://abner.page/post/c23-conventions-1/"
  },
  "mainEntity": {
    "@id": "https://abner.page/post/c23-conventions-1/",
    "@type": "CreativeWork",
    "archivedAt": {
      "@id": "https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-15-my-c23-conventions/#webpage"
    },
    "author": [
      {
        "@type": "Person",
        "name": "abner.page by abnercoimbre"
      }
    ],
    "dateModified": "2026-10-15T15:32:15+00:00",
    "datePublished": "2026-10-15T15:32:15+00:00",
    "headline": "My C23 Conventions",
    "timeRequired": "PT5M",
    "url": "https://abner.page/post/c23-conventions-1/",
    "wordCount": 927
  },
  "name": "Archived snapshot: My C23 Conventions",
  "preview": null,
  "reading_minutes": 5,
  "replicated_at": null,
  "source": "lobste-rs",
  "source_name": "Lobsters: Top Stories of the Past Week",
  "summary": "Comments",
  "tags": [
    "c"
  ],
  "temporalCoverage": "2026-10-16T22:01:23.127136231+00:00",
  "title": "My C23 Conventions",
  "url": "https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-15-my-c23-conventions/",
  "word_count": 927
}