<feed xmlns="http://www.w3.org/2005/Atom"> <id>https://tech.dreamleaves.org/</id><title>Red Leaves</title><subtitle>Je me remets à lire, autant me remettre à écrire aussi</subtitle> <updated>2026-04-11T11:29:38+02:00</updated> <author> <name>Arnaud 'red' Rouyer</name> <uri>https://tech.dreamleaves.org/</uri> </author><link rel="self" type="application/atom+xml" href="https://tech.dreamleaves.org/feed.xml"/><link rel="alternate" type="text/html" hreflang="en" href="https://tech.dreamleaves.org/"/> <generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator> <rights> © 2026 Arnaud 'red' Rouyer </rights> <icon>/assets/img/favicons/favicon.ico</icon> <logo>/assets/img/favicons/favicon-96x96.png</logo> <entry><title>Cloudflare's Gateway Drug</title><link href="https://tech.dreamleaves.org/posts/cloudflare-gateway-drug/" rel="alternate" type="text/html" title="Cloudflare&amp;apos;s Gateway Drug" /><published>2025-06-06T00:00:00+02:00</published> <updated>2025-06-06T00:00:00+02:00</updated> <id>https://tech.dreamleaves.org/posts/cloudflare-gateway-drug/</id> <content type="html"><![CDATA[<p>When you’re not yet huge enough to run your own datacenter, or rent a 2U server colocation, hosting web applications can be a pain, and as DHH eloquently wrote it: <a href="https://world.hey.com/dhh/merchants-of-complexity-4851301b">the merchants of complexity</a> made sure all developers found themselves faced with overpriced technologies they wouldn’t actually need.</p><p>But it turns out there’s a much simpler (and often free!) path to getting projects online, if you know where to look.</p><h1 id="the-myth-of-the-million-visitors">The myth of the million visitors</h1><p>Three years ago, I was writing the API for a chat app where the higher-ups suddenly decided we required a Kubernetes cluster to handle the influx of users that would be coming our way on launch day. It took three months and a team of two freelancers. In all fairness, those freelancers built exactly what was asked of them: our very own K8S cluster, with production/staging support, CD on git deploy, and even auto-rent of short-burst Amazon EC2 instances to get them for a lower price. Seriously, what could go wrong?</p><p>The infrastructure was bulletproof. No traffic ever bothered to shoot at us.</p><h1 id="before">Before</h1><p>As mainly a web developer, each project always comes with a pain: where am I gonna host it? I’m not a viral developer most of the time, my projects could actually be run and served off my own laptop<sup id="fnref:badnetwork"><a href="#fn:badnetwork" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>. What then?</p><ul><li><strong>Spin up a t3.nano on AWS?</strong> No, I don’t want to set up a server every time I want to put something online?<li><strong>One giant server for everything<sup id="fnref:oneserver"><a href="#fn:oneserver" class="footnote" rel="footnote" role="doc-noteref">2</a></sup>?</strong> That’s too much work, too expensive upfront, and often, too many app environments to handle together. And on <strong>AWS</strong>, do I really want to live in fear of the EGRESS FEES!?<li><strong>So-called “serverless” <sup id="fnref:serverless"><a href="#fn:serverless" class="footnote" rel="footnote" role="doc-noteref">3</a></sup> platforms like <a href="https://www.heroku.com/">Heroku</a> or <a href="https://fly.io/">Fly</a>?</strong> They are actually nice, but some of them (looking at you <a href="https://vercel.com/">Vercel</a>!) have a tendency to produce bills in the six-figure ranges<sup id="fnref:horrorstories"><a href="#fn:horrorstories" class="footnote" rel="footnote" role="doc-noteref">4</a></sup>.<li><strong><a href="https://pages.github.com/">Github Pages</a>?</strong> It’s free, comes with <a href="https://github.com/features/actions">GitHub actions</a> for deployments, and…there’s messing with <code class="language-plaintext highlighter-rouge">gh-pages</code>.</ul><p>So <strong>Heroku</strong> ended up as my default: cheap, easy, and if my project ever uses too much resources, they’ll gladly put it down. Kind of reassuring, actually.</p><p>But then I stumbled onto <strong>Cloudflare</strong>, and everything changed.</p><h1 id="hosting-my-own-blog">Hosting my own blog</h1><p>In May of 2024, I got back into the habit of reading books<sup id="fnref:readingbooks"><a href="#fn:readingbooks" class="footnote" rel="footnote" role="doc-noteref">5</a></sup>, and a bad one got me to write about them<sup id="fnref:verybadbook"><a href="#fn:verybadbook" class="footnote" rel="footnote" role="doc-noteref">6</a></sup>. So I went for a static blog with <a href="https://jekyllrb.com/">Jekyll</a><sup id="fnref:whyjekyll"><a href="#fn:whyjekyll" class="footnote" rel="footnote" role="doc-noteref">7</a></sup>, and my first instinct was to look at <strong>Github Pages</strong>, but again, <code class="language-plaintext highlighter-rouge">gh-pages</code>: I didn’t want to deal with “artifacts” or any of those strange branch-switching shenanigans.</p><p>At that point, I still thought <strong>Cloudflare</strong> was only about those annoying “Are you human?” pages on direct-download sites, CDN, or protecting sites from DDoS and all, which are not problems I’ve ever come into contact with. So I had no idea how I stumbled onto <strong>Cloudflare Pages</strong>. Surely the pricing for that kind of around-the-world service would be expensive, right?</p><blockquote><p>On both free and paid plans, requests to static assets are free and unlimited. A request is considered static when it does not invoke Functions. <br /> ― <a href="https://developers.cloudflare.com/pages/functions/pricing/">Cloudflare Pages Pricing</a></p></blockquote><p>In <strong>Ruby on Rails</strong> terms, “static assets” means stuff like the CSS and the JavaScript that the server would create once at build time, and serve as-is to visitors. But if I’m using <strong>Jekyll</strong>, then that would mean the HTML files generated are ALSO static assets, right? Is it saying <strong>Cloudflare</strong> would host my blog FOR FREE!?</p><p>Yes. Yes, it is.</p><h1 id="hosting-sites-for-other-people-and-feeling-good-about-it">Hosting sites for other people (and feeling good about it)</h1><p>Chances are, if you’re reading this, you’re the most tech-aware person of your family or friend group, and you’re often asked: “Can you make me a website for my (small company/association/shop idea)?”. It’s okay, we’ve all been there, and I’m pretty sure we’ve all gone through the same thought process:</p><blockquote><p>Sure, I can make a website. Do they have a domain name or should I set it up? Do they have hosting? Should I set it up all by myself? I guess they won’t need something as powerful as a full Rails engine, or a machine with 4TB of RAM. Should I go for something static then? But where? How will I pay? How do I make sure the domain/website is under their name even if I’m doing all the legwork? And what if they want to update some text, should I add an admin panel, but then it would require auth,… Do I need to set up anything LetsEncrypt again? <br /> ― You, me, and everybody else, before going to <a href="https://wordpress.com/">Wordpress.com</a>.</p></blockquote><p>So when my aunt came to me for help, I was relieved: her association already had the domain and hosting. You know the kind: <em>shared</em> hosting, with its own FTP server. As I’m writing this, I’m trying to remember: did the admin panel indicate they were running PHP 4 or 5? Because I know it was neither 7 nor 8<sup id="fnref:php6"><a href="#fn:php6" class="footnote" rel="footnote" role="doc-noteref">8</a></sup> and it’s making me shiver.</p><p>Thankfully, she just wanted me to rework an existing landing page, and it was static: no strange PHP scripts, no MySQL injections<sup id="fnref:injection"><a href="#fn:injection" class="footnote" rel="footnote" role="doc-noteref">9</a></sup> to sanitize,… Since static page means static assets, I put everything on <strong>GitHub</strong>, connected the repository to Cloudflare, changed the DNS records in their domain’s admin panel, and <em>voila!</em>, now if I (or a future developer) wanted to update the contents of their website, a simple <code class="language-plaintext highlighter-rouge">git push</code> would suffice, and the site would automatically deploy, and with a working SSL certificate!</p><p>The next person who needed my help for a “website” was a friend who wanted to host an image online, so a QR code would lead to it, as part of a birthday treasure hunt. I could have sent her to <a href="https://imgur.com/">Imgur</a>, or said no. But now, what was once the daunting task of hosting was so painless that I set it up while half-watching a TV show: I deployed the image under a subdomain of my main site, and it just worked.</p><p>That’s when I realized how happy I was. Hosting had become simple: not a bill to stress over, or a sysadmin side quest. Now, I could just put a file online, instantly, for anyone, with zero drama about pricing, or the egress fees of an <strong>AWS S3</strong> bucket. And I had <strong>Cloudflare</strong> to thank for it.</p><h1 id="code-everywhere">Code everywhere</h1><p>A few months later, one of my brothers was the one to ask for my help. With crypto. Oh boy.</p><p>There was no trading involved, no <em>degens</em> to cater to,… But my brother had found a few wallets doing extraordinary calls, and he didn’t trust copy bot services. So while he could manually check the wallets online, having code to automate the process would make his “work”<sup id="fnref:tradingnotjob"><a href="#fn:tradingnotjob" class="footnote" rel="footnote" role="doc-noteref">10</a></sup> easier.</p><p>I hadn’t touched crypto in a while, and my latest crypto project<sup id="fnref:valentinecoin"><a href="#fn:valentinecoin" class="footnote" rel="footnote" role="doc-noteref">11</a></sup> was a lot of fun to code for. So I went along with it, looked up the documentation, wrote a JS script, and…now what? My brother did have a computer, an old Windows laptop from who-knows-when, and I wasn’t sure he could even run the script. Would you believe who came to the rescue again?</p><p>For another project, I had been looking at whether <strong>Cloudflare</strong> supported CRON triggers. Well, if it could run mail jobs at 2am, surely it could also run my bot’s “check-balances-and-send-message” loops. But for what price, would that be free too?</p><p><a href="https://developers.cloudflare.com/workers/platform/pricing/">Yes. Yes, it is.</a></p><p>The craziness of this pricing can’t be overstated: for my brother’s crypto fun, I had a CRON job running and sending him regular updates. I was even using a <strong>D1 Database</strong> to store the balances (which was also <a href="https://developers.cloudflare.com/d1/platform/pricing/">free for that level of usage</a>), and best of all, I didn’t have to worry about any kind of intrusion, which I would have with a regular server.</p><p>I don’t know how much my brother made, as I don’t really care about trading, but a few months later, he bought himself a MacBook Air, and later offered to buy me a PS5 Pro when I mentioned I was waiting for <a href="https://www.youtube.com/watch?v=wbLstJHlC4U">Death Stranding 2</a> coming out in June.</p><p>Which is very nice from him, since, so far in this story, I still hadn’t paid a cent to <strong>Cloudflare</strong>.</p><h1 id="finally-paying-for-the-product">Finally paying for the product</h1><p>I finally paid for Cloudflare.</p><p>I still had a professional project running on <strong>Heroku</strong>, both <strong>Rails</strong> server and <strong>PostgreSQL</strong> database, and I wanted to move some parts of the server API to faster NodeJS servers. Unfortunately, the provided database was VERY conservative with the number of allowed client connections. So <a href="https://blog.cloudflare.com/hyperdrive-making-regional-databases-feel-distributed/">Hyperdrive</a> felt like an awesome solution, both from a technological standpoint and as an answer to my needs. There was no free tier<sup id="fnref:hyperdrivefreetier"><a href="#fn:hyperdrivefreetier" class="footnote" rel="footnote" role="doc-noteref">12</a></sup>, but just subscribing to the “Workers Plan” enabled unlimited usage, plus it would even increase the (very generous) limits of all other products.</p><p>After a lot of tomfoolery regarding client connection limits and worldwide latency<sup id="fnref:comingsoon"><a href="#fn:comingsoon" class="footnote" rel="footnote" role="doc-noteref">13</a></sup>, I ended up with a NodeJS API available worldwide, connecting to my database through <strong>Hyperdrive</strong>, with a performance measured in the hundreds of milliseconds, all in one night’s work, and a commitment to a monthly payment of only <strong>5 DOLLARS</strong> per month. Take that, Kubernetes!</p><p>Honestly, it didn’t even feel strange to finally “give in” and punch in my credit card digits: after so much free utility, it was a no-brainer. For the price of a fancy coffee<sup id="fnref:starbucks"><a href="#fn:starbucks" class="footnote" rel="footnote" role="doc-noteref">14</a></sup>, I got performance and global reach my old infra couldn’t touch!</p><h1 id="the-future">The future</h1><p>Hey, if someone at <strong>Cloudflare</strong> is reading this, do you have any opening for a developer evangelist in France?</p><p>When I look at the <strong>Node</strong> APIs I had started writing to decouple some parts of my <strong>Rails</strong> monolith, they are hosted on either <strong>Heroku</strong>, or <strong>Fly</strong>, and these products are great, but getting one too many “{application_name} ran out of memory and crashed” emails from them was the nail in the coffin. Sure, I’m not leaving <strong>Heroku</strong> anytime soon, because <a href="https://activeadmin.info/">ActiveAdmin</a> is still a wonderful backoffice solution and requires <strong>Rails</strong>, but for anything that only needs <strong>JavaScript</strong>, I can’t imagine going anywhere other than the <strong>Workers</strong>.</p><p>Which is even funnier when I consider that I’ve barely touched all the available services: I set up <a href="https://developers.cloudflare.com/email-routing/">Email routing</a> because I don’t need a full-priced tool just to redirect domain emails to my Gmail, I’m still scratching the surface of <a href="https://developers.cloudflare.com/workflows/">Workflows</a> (integrating that into my CRONs), I don’t need <a href="https://developers.cloudflare.com/r2/">R2</a> yet, and I haven’t tried <a href="https://developers.cloudflare.com/queues/">Queues</a> but they look interesting. And there are <a href="https://developers.cloudflare.com/products/?product-group=Developer+platform">a bunch more I haven’t even looked at yet</a>!</p><p>But hey, if you’d rather spend time on Twitter circlejerking about the size of the Kubernetes cluster serving five users, or complaining about the six-figure bill that crept up on you<sup id="fnref:sixfigures"><a href="#fn:sixfigures" class="footnote" rel="footnote" role="doc-noteref">15</a></sup>, you do you.</p><p>Me? I’m building on Cloudflare.</p><hr /><div class="footnotes" role="doc-endnotes"><ol><li id="fn:badnetwork"><p>I could, if I had something better than 2MB/s on my home line. <a href="#fnref:badnetwork" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a></p><li id="fn:oneserver"><p>Most notably, “indie” maker <a href="https://x.com/levelsio">@levelsio</a> does that. <a href="#fnref:oneserver" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a></p><li id="fn:serverless"><p>Kudos to whoever coined this term that’s so far away from reality! <a href="#fnref:serverless" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a></p><li id="fn:horrorstories"><p>If you don’t believe me, check out <a href="https://serverlesshorrors.com/">Serverless Horrors</a>! <a href="#fnref:horrorstories" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a></p><li id="fn:readingbooks"><p>The book to kickstart it was <a href="https://x.com/jakeadelstein">Jake Adelstein</a>’s <a href="https://blog.dreamleaves.org/posts/tokyo-vice-book/">Tokyo Vice</a>. <a href="#fnref:readingbooks" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a></p><li id="fn:verybadbook"><p>This book shall live in infamy: <a href="https://blog.dreamleaves.org/posts/aux-origines-de-castlevania-sotn/">Aux Origines de Castlevania Symphony of the Night</a>. <a href="#fnref:verybadbook" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a></p><li id="fn:whyjekyll"><p>Why choose Jekyll? I’m a ruby boy at heart, and I liked the theme I had seen. <a href="#fnref:whyjekyll" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a></p><li id="fn:php6"><p>Can you believe <a href="https://ma.ttias.be/php6-missing-version-number/">there never was a PHP 6 major version</a>? <a href="#fnref:php6" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a></p><li id="fn:injection"><p>A staple of all developers who ever touched PHP is learning about <a href="https://en.wikipedia.org/wiki/SQL_injection">SQL injections</a>, sometimes the hard way. <a href="#fnref:injection" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a></p><li id="fn:tradingnotjob"><p>I don’t believe day-trading to be a real job, more like an addiction. <a href="#fnref:tradingnotjob" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a></p><li id="fn:valentinecoin"><p>For Valentine’s Day 2018, I launched <a href="https://www.thevalentinecoin.com/">The Valentine Coin</a> with a friend. <a href="#fnref:valentinecoin" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a></p><li id="fn:hyperdrivefreetier"><p>Quite funnily, <strong>Hyperdrive</strong> now has a free tier, but it’s too low for my requirements. <a href="#fnref:hyperdrivefreetier" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a></p><li id="fn:comingsoon"><p>Which will be covered in a further post. <a href="#fnref:comingsoon" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a></p><li id="fn:starbucks"><p>And to think that ten years ago, I was buying myself one of those “fancy coffees” every day, if not two per day… <a href="#fnref:starbucks" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a></p><li id="fn:sixfigures"><p>Don’t forget to tweet “VICTORY!” when the company discounts it to four in “a commercial gesture”. <a href="#fnref:sixfigures" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a></p></ol></div>]]></content> <author> <name>Arnaud 'red' Rouyer</name> </author> <summary>Or how I learned to stop worrying and embrace the Edge.</summary> </entry> <entry><title>Adding Giscus comments to a static blog</title><link href="https://tech.dreamleaves.org/posts/adding-giscus-comments-to-a-static-blog/" rel="alternate" type="text/html" title="Adding Giscus comments to a static blog" /><published>2024-10-31T00:00:00+01:00</published> <updated>2024-10-31T00:00:00+01:00</updated> <id>https://tech.dreamleaves.org/posts/adding-giscus-comments-to-a-static-blog/</id> <content type="html"><![CDATA[<p>For my <a href="https://blog.dreamleaves.org/">personal blog</a>, which I started a few months ago already, I don’t really need comments. It’s more of a “keep-a-useful-calendar-of-medias-I-consume” list, I write it in French, and it’s not meant to open discussion, so I wasn’t looking at comments. At all.</p><p>However, for this tech-related blog, <a href="https://www.reddit.com/r/rust/comments/1gdd0md/trimming_down_a_rust_binary_in_half/">posting the first article on Reddit</a> gave way to very interesting discussions and commentaries. So I figured: “Heh, why not?”.</p><h1 id="the-state-of-internet-comments-in-2024">The state of Internet comments in 2024</h1><p>My very first personal blog, back in 2004, was written in PHP and hosted on a Free server<sup id="fnref:free"><a href="#fn:free" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>. The (anonymous) comments were saved in own my database, and when I went back to check on it twenty years later, the place was littered with spam, and what I’m pretty sure were various attempts at HTML injections.</p><p>Clearly, nobody wants that. And nobody wants to spend time away from writing in order to play warden and build an user-authentification service just for a few comments<sup id="fnref:fewcomments"><a href="#fn:fewcomments" class="footnote" rel="footnote" role="doc-noteref">2</a></sup>. Plus, this blog’s HTML is only rendered once I deploy it, so there’ no way I can add comments anywhere, I guess.</p><p>And then there is <a href="https://en.wikipedia.org/wiki/Disqus">Disqus</a>.</p><p>Disqus looks nice. It’s a testament to its beauty in design that every solution tries to imitate Disqus today. I remember over time seeing a LOT of competitors<sup id="fnref:moot"><a href="#fn:moot" class="footnote" rel="footnote" role="doc-noteref">3</a></sup>, but only Disqus survived the test of time, and grew old enough to drink some alcohol in France.</p><p>However, Disqus is also a proprietary solution, I’m not entirely sure of where they stand on <a href="https://en.wikipedia.org/wiki/General_Data_Protection_Regulation">GDPR</a>, I’ve seen enough Disqus conversations get derailed by trolls,… This is not the right choice for a tech blog.</p><h1 id="enter-giscus">Enter Giscus</h1><p>On the paper, <a href="https://giscus.app/">Giscus</a> fills what I’m missing:</p><ul><li>Open-source.<li>Commenting requires a GitHub account.<li>Comments are hosted on GitHub.</ul><p>Therefore, only developers will comment. Maybe it’s gonna end up being a fate worse than trolls.</p><h2 id="setting-up-giscus">Setting up Giscus</h2><p>Setting up Giscus is actually a breeze and their homepage is a very nice setup wizard. My only issues were:</p><ul><li>Which repository is best suited for Giscus?</ul><p>Giscus comment sections are tied to a repository, which left me puzzled, as I wasn’t sure WHICH repository I should tie them to: <a href="https://github.com/joshleaves/redrust/">this blog’s repository</a>? Or another and completely unrelated one?</p><p>The answer is…it’s up to you, but it must be public. So if your blog is in a private repository, you’ll need a specific repo for the comments. I chose to have everything together to have the full “static-page-blog-on-github” experience, but it’s nice to have a choice.</p><ul><li>What are those pathname/url/&amp;lt;title&amp;gt; options?</ul><p>Just pick one and forget about it. Having this option feels like too much hassle for what it is, and if you ever change an article’s title/URL, you’ll be screwed anyway. I’d have like an option to map the conversation to something like <code class="language-plaintext highlighter-rouge">&amp;lt;meta property="giscus:uuid" value=&amp;gt;</code>, and maybe this will be available in the future, BUT this would break the nice display of the discussions on Github.</p><p>For my part, I started with <code class="language-plaintext highlighter-rouge">pathname</code> but just having an URL as the title of the GitHub Discussion was irking me, so I went the <code class="language-plaintext highlighter-rouge">og:title</code> route and customised it a little to add the date.</p><ul><li>Which theme should I use on my blog?</ul><p>I’m used to Dark Mode on Github, but this blog is very LIGHT. Starting with a dark theme was a wrong (and very ugly) move, and trying one of the “no-border” themes rendered the comment section almost invisible.</p><p>The preview on the setup wizard isn’t very effective in guessing how it’s gonna fit on your blog, so apart from manually trying each theme, just decide on <code class="language-plaintext highlighter-rouge">light_protanopia</code> or <code class="language-plaintext highlighter-rouge">dark_protanopia</code> and roll on with it.</p><h2 id="adding-giscus">Adding Giscus</h2><p>You’ll get a nice <code class="language-plaintext highlighter-rouge">&amp;lt;script&amp;gt;</code> tag to copy-paste into your template. The whole process is <a href="https://github.com/joshleaves/redrust/commit/9f7e5257d1887207baaa70d71334851811ddb4dc">so simple, it’s almost ridiculous</a>.</p><h1 id="and-then">And then?</h1><p>An input will be available when your visitors reach the end of your posts, and they’ll be able to post comments.</p><p>Whenever a comment is posted:</p><ul><li>you’ll get notification in GitHub (nice!).<li>you’ll be able to follow (and moderate?) the discussion in your repository’s <a href="https://github.com/joshleaves/redrust/discussions">Discussions</a> tab.</ul><p>Happy commenting.</p><hr /><div class="footnotes" role="doc-endnotes"><ol><li id="fn:free"><p>“Free” as in both <a href="https://en.wiktionary.org/wiki/free_of_charge">“free of charge”</a>, and <a href="https://en.wikipedia.org/wiki/Free_(ISP)">“Free the French ISP”</a>. <a href="#fnref:free" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a></p><li id="fn:fewcomments"><p>But I’m keeping hope I will become famous in the future and get tons of comments! <a href="#fnref:fewcomments" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a></p><li id="fn:moot"><p>I remember being quite fond of <a href="https://news.ycombinator.com/item?id=6818416">moot</a> back then, <a href="https://en.wikipedia.org/wiki/Christopher_Poole">just for its name</a>, but the product is dead as nails today. <a href="#fnref:moot" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a></p></ol></div>]]></content> <author> <name>Arnaud 'red' Rouyer</name> </author> <summary>How easy it is to add Giscus to a blog?</summary> </entry> <entry><title>Trimming down a rust binary in half</title><link href="https://tech.dreamleaves.org/posts/trimming-down-a-rust-binary-in-half/" rel="alternate" type="text/html" title="Trimming down a rust binary in half" /><published>2024-10-27T00:00:00+02:00</published> <updated>2024-10-27T00:00:00+02:00</updated> <id>https://tech.dreamleaves.org/posts/trimming-down-a-rust-binary-in-half/</id> <content type="html"><![CDATA[<div class="alert alert-success alert-white rounded"><div class="icon"><i class="fa fa-check"></i></div><strong>I posted this on reddit!</strong><p>The <a href="conversations that ensued">https://www.reddit.com/r/rust/comments/1gdd0md/trimming_down_a_rust_binary_in_half/</a> may interest you, but the best parts have been added to this post.</p></div><p>Lately, I’ve stumbled on a <a href="https://kobzol.github.io/rust/cargo/2024/01/23/making-rust-binaries-smaller-by-default.html">blog post about Rust binary sizes</a>. I haven’t done much compilation<sup id="fnref:compilation"><a href="#fn:compilation" class="footnote" rel="footnote" role="doc-noteref">1</a></sup> since I last touched C in school, but I was intrigued by the subject and decided to look at how binary size reduction could impact my own Rust project: <a href="https://github.com/joshleaves/advent-rs">advent-rs</a>, a simple binary taking year/day/part/filename parameters and solving exercises from the <a href="https://adventofcode.com/">AdventOfCode</a> online contest.</p><h2 id="starting-point">Starting point</h2><p>There’s already some stuff I know and set up properly, so I already got the correct defaults for my <code class="language-plaintext highlighter-rouge">Cargo.toml</code>.</p><div class="language-rust highlighter-rouge"><div class="highlight">class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre><td class="rouge-code"><pre><span class="p">[</span><span class="n">profile</span><span class="py">.release</span><span class="p">]</span>
<span class="n">opt</span><span class="o">-</span><span class="n">level</span> <span class="o">=</span> <span class="mi">3</span>
</pre></div></div><p>Let’s look at the resulting file through <code class="language-plaintext highlighter-rouge">size</code><sup id="fnref:size"><a href="#fn:size" class="footnote" rel="footnote" role="doc-noteref">2</a></sup> and <code class="language-plaintext highlighter-rouge">ls</code>:</p><div class="language-bash highlighter-rouge"><div class="highlight">class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre><td class="rouge-code"><pre><span class="nv">$ </span>size target/release/advent-rs
__TEXT	__DATA	__OBJC	others	dec	hex
1409024	16384	  0	      4295393280	4296818688	1001c4000

<span class="nv">$ </span><span class="nb">ls</span> <span class="nt">-lah</span>  target/release/advent-rs
 <span class="nt">-rwxr-xr-x</span>@ 1 red  staff   1.8M Oct 27 08:49 target/release/advent-rs
</pre></div></div><p>So we are standing at 1.8M, for a binary that’s solving around 150 exercices. That doesn’t sound so bad, but again, I haven’t compiled code in a long time, so I have no idea where I stand. Let’s check the first binary I can think of.</p><div class="language-bash highlighter-rouge"><div class="highlight">class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre><td class="rouge-code"><pre><span class="nv">$ </span><span class="nb">ls</span> <span class="nt">-lah</span> /bin/ls
<span class="nt">-rwxr-xr-x</span>  1 root  wheel   151K Sep  5 11:17 /bin/ls
</pre></div></div><p>Well, I definitely need to do some trimming.</p><h2 id="optimizing-compilation">Optimizing compilation</h2><p>First things first, let’s check compilation.</p><h3 id="start-with-stripping">Start with stripping</h3><p>As I remember from my C days, and as the blog clearly states: not stripping symbols from a release is the root of all evil.</p><div class="alert alert-warning alert-white rounded"><div class="icon"><i class="fa fa-warning"></i></div><strong>This is not an absolute!</strong><p>As <a href="https://www.reddit.com/r/rust/comments/1gdd0md/trimming_down_a_rust_binary_in_half/lu0rcsh/">fredbrancz</a> rightly said, if you release a binary to the public at large and later require debugging it, missing the symbols will come back at you fast!</p></div><div class="language-rust highlighter-rouge"><div class="highlight">class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre><td class="rouge-code"><pre><span class="p">[</span><span class="n">profile</span><span class="py">.release</span><span class="p">]</span>
<span class="n">opt</span><span class="o">-</span><span class="n">level</span> <span class="o">=</span>  <span class="mi">3</span>
<span class="n">strip</span> <span class="o">=</span> <span class="k">true</span>
</pre></div></div><p>Build, check file.</p><div class="language-bash highlighter-rouge"><div class="highlight">class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre><td class="rouge-code"><pre><span class="nv">$ </span>/bin/ls <span class="nt">-lah</span>  target/release/advent-rs
<span class="nt">-rwxr-xr-x</span>@ 1 red  staff   1.5M Oct 27 09:03 target/release/advent-rs
</pre></div></div><p>Already 300kb trimmed off!</p><h3 id="halt-and-catch-fire">Halt and catch fire</h3><p>A <strong>backtrace</strong> is a very useful to have when debugging, all interpreted languages come with them, and for a while, I wasn’t even surprised to see them pop up in Rust, in spite of C never printing one, only going as far as telling me “Segmentation fault” when my carefully-crafted binary would have the politeness to tell me on its own that I was doing something wrong.</p><p>Well, Rust should not have them.</p><p>Wait, let me explain myself here: backtraces are not a normal feature of a compiled language, since the backtraces you are used to actually come courtesy of the interpreter. So in a compiled language with no interpreter, where do they come from?</p><p>Yes, from your binary.</p><div class="language-rust highlighter-rouge"><div class="highlight">class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre><td class="rouge-code"><pre><span class="p">[</span><span class="n">profile</span><span class="py">.release</span><span class="p">]</span>
<span class="n">opt</span><span class="o">-</span><span class="n">level</span> <span class="o">=</span>  <span class="mi">3</span>
<span class="n">strip</span> <span class="o">=</span> <span class="k">true</span>
<span class="n">panic</span> <span class="o">=</span> <span class="s">"abort"</span>
</pre></div></div><p>Build, check file.</p><div class="language-bash highlighter-rouge"><div class="highlight">class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre><td class="rouge-code"><pre><span class="nv">$ </span>/bin/ls <span class="nt">-lah</span>  target/release/advent-rs
<span class="nt">-rwxr-xr-x</span>@ 1 red  staff   1.2M Oct 27 09:05 target/release/advent-rs
</pre></div></div><p>Can you believe this shaved off another 300kb?</p><h3 id="insane-stuff-i-dont-want">Insane stuff I don’t want</h3><p>The <a href="https://doc.rust-lang.org/cargo/reference/profiles.html">Cargo documentation</a> gives us more options we can experiment with for fun. They don’t work for me but your mileage may vary.</p><h4 id="optimize-for-size">Optimize for size</h4><p>After <code class="language-plaintext highlighter-rouge">3</code>, there are <code class="language-plaintext highlighter-rouge">opt-level</code> values that target smaller binaries. I can try with <code class="language-plaintext highlighter-rouge">s</code>or <code class="language-plaintext highlighter-rouge">z</code> and they will respectively shave off 100 and 200kb, BUT the tradeoff here is execution speed, which is not a value I want to allow myself to play with<sup id="fnref:speed"><a href="#fn:speed" class="footnote" rel="footnote" role="doc-noteref">3</a></sup>.</p><h4 id="dont-check-integer-overflow">Don’t check integer overflow</h4><p><del>You can disable checking for integer overflow with <code class="language-plaintext highlighter-rouge">overflow-checks = false</code> from your binary. Obviously, this is NOT recommended when you’re playing with user input, and in my case, it won’t even register any change.</del></p><div class="alert alert-danger alert-white rounded"><div class="icon"><i class="fa fa-times-circle"></i></div><strong>Wrong!</strong><p>As <a href="https://www.reddit.com/r/rust/comments/1gdd0md/trimming_down_a_rust_binary_in_half/lu17nls/">GamerCounter</a> pointed out, these checks are removed by default in release mode.</p></div><h4 id="link-time-optimization">Link-time optimization</h4><p>It seems that other than compiling, you can also optimise linking<sup id="fnref:linking"><a href="#fn:linking" class="footnote" rel="footnote" role="doc-noteref">4</a></sup> with <code class="language-plaintext highlighter-rouge">lto = true</code>. I don’t recommend it since it doubled my build time AND didn’t give me a good size reduction…</p><div class="alert alert-warning alert-white rounded"><div class="icon"><i class="fa fa-warning"></i></div><strong>Attention!</strong><p>I wasn’t clear enough on this, but as <a href="https://www.reddit.com/r/rust/comments/1gdd0md/trimming_down_a_rust_binary_in_half/lu1eg7j/">VorpalWay</a> and <a href="https://www.reddit.com/r/rust/comments/1gdd0md/trimming_down_a_rust_binary_in_half/lu1l7eg/">hubbamybubba</a> correctly pointed out, the benefits of this parameter depends on your project.</p></div><h1 id="cleaning-up-crates">Cleaning up crates</h1><p>The other thing that will take up space is…code. More specifically, code you wouldn’t need. Let’s check my dependencies:</p><div class="language-rust highlighter-rouge"><div class="highlight">class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre><td class="rouge-code"><pre><span class="p">[</span><span class="n">dependencies</span><span class="p">]</span>
<span class="n">clap</span> <span class="o">=</span> <span class="p">{</span> <span class="n">version</span> <span class="o">=</span> <span class="s">"4.5.1"</span><span class="p">,</span> <span class="n">features</span> <span class="o">=</span> <span class="p">[</span><span class="s">"derive"</span><span class="p">]</span> <span class="p">}</span>
<span class="n">itertools</span> <span class="o">=</span> <span class="s">"0.12.1"</span>
<span class="n">md</span><span class="o">-</span><span class="mi">5</span> <span class="o">=</span> <span class="s">"0.10.6"</span>
<span class="n">mutants</span> <span class="o">=</span> <span class="s">"0.0.3"</span>

<span class="p">[</span><span class="n">dev</span><span class="o">-</span><span class="n">dependencies</span><span class="p">]</span>
<span class="n">assert_cmd</span> <span class="o">=</span> <span class="s">"2.0.13"</span>
<span class="n">predicates</span> <span class="o">=</span> <span class="s">"3.1.0"</span>
<span class="n">criterion</span> <span class="o">=</span> <span class="p">{</span> <span class="n">version</span> <span class="o">=</span> <span class="s">"0.5.1"</span><span class="p">,</span> <span class="n">features</span> <span class="o">=</span> <span class="p">[</span><span class="s">"html_reports"</span><span class="p">]</span> <span class="p">}</span>
</pre></div></div><p>Nothing too barbaric here, <a href="https://github.com/clap-rs/clap">clap</a> is the <em>de-facto</em> crate to parse command-line arguments, <a href="https://docs.rs/itertools/latest/itertools/">itertools</a> is required because a ton of exercises require iterating over data in strange ways, I require <a href="https://docs.rs/md-5/latest/md5/">md-5</a> for four exercices, and <a href="https://github.com/sourcefrog/cargo-mutants">mutants</a> is allowed here only to <a href="https://mutants.rs/attrs.html">skip some tests when running mutations</a>.</p><p>As for the development dependencies, I know I got nothing to fear from them.</p><h1 id="removing-a-crate">Removing a crate</h1><p>You may feel like the above dependencies are <strong>sane</strong>, that I need all of them, that there is no way my project could function without any of them,… But what if it could?</p><h2 id="past-experience">Past experience</h2><p>When I started writing all of these parsers I required for handling input data, I often used the <a href="https://docs.rs/regex/latest/regex/">regex crate</a>. While it was very useful, I knew from my benchmarks that using old-school “split-on-this-char” approach was faster<sup id="fnref:speed:1"><a href="#fn:speed" class="footnote" rel="footnote" role="doc-noteref">3</a></sup>, so I slowly started phasing it out.</p><p>As of <a href="https://github.com/joshleaves/advent-rs/commit/50cb0325dcaa039049106057a2c64e635b3e8955#diff-2e9d962a08321605940b5a657135052fbcef87b5e360662bb527c96d9a615542L35">Year 2017: Day 15</a>, I removed the crate from my <code class="language-plaintext highlighter-rouge">Cargo.toml</code>.</p><h2 id="future-ideas">Future ideas</h2><p>While I know that “rolling out your own crypto” is a bad idea, the crate <code class="language-plaintext highlighter-rouge">md-5</code>can clearly be replaced. Plus the fact that its input and outputs are of different types mean one exercise could actually be way faster<sup id="fnref:fastermd5"><a href="#fn:fastermd5" class="footnote" rel="footnote" role="doc-noteref">5</a></sup> if I rolled out my own!</p><h1 id="okay-now-wheres-the-bloat">Okay, now where’s the bloat?</h1><p>I found out the appropriately-named tool <a href="https://github.com/RazrFalcon/cargo-bloat">cargo-bloat</a> on GitHub.</p><p>Let’s install and try it, it’s just a command<sup id="fnref:cargo-bloat"><a href="#fn:cargo-bloat" class="footnote" rel="footnote" role="doc-noteref">6</a></sup> after all.</p><div class="language-bash highlighter-rouge"><div class="highlight">class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
</pre><td class="rouge-code"><pre><span class="nv">$ </span>cargo bloat <span class="nt">--release</span>
    Finished <span class="sb">`</span>release<span class="sb">`</span> profile <span class="o">[</span>optimized] target<span class="o">(</span>s<span class="o">)</span> <span class="k">in </span>0.01s
    Analyzing target/release/advent-rs

 File  .text     Size        Crate Name
 1.1%   1.6%  16.0KiB clap_builder clap_builder::parser::parser::Parser::get_matches_with
 1.1%   1.6%  15.8KiB    <span class="o">[</span>Unknown] __mh_execute_header
 0.7%   1.1%  10.5KiB clap_builder clap_builder::builder::command::Command::_build_self
 0.6%   0.9%   8.9KiB          std std::backtrace_rs::symbolize::gimli::resolve
 0.6%   0.9%   8.7KiB          std std::backtrace_rs::symbolize::gimli::Context::new
 0.5%   0.7%   7.1KiB          std gimli::read::dwarf::Unit&amp;lt;R&amp;gt;::new
 0.4%   0.7%   6.5KiB clap_builder &amp;lt;clap_builder::error::format::RichFormatter as clap_builder::error::format::ErrorFormatter&amp;gt;::format_error
 0.4%   0.6%   6.1KiB clap_builder clap_builder::parser::validator::Validator::validate
 0.4%   0.6%   5.8KiB    advent_rs core::slice::sort::stable::quicksort::quicksort
 0.4%   0.6%   5.6KiB          std addr2line::ResUnit&amp;lt;R&amp;gt;::find_function_or_location::
 0.4%   0.5%   5.2KiB          std addr2line::Lines::parse
 0.4%   0.5%   5.2KiB clap_builder &amp;lt;alloc::vec::Vec&amp;lt;T,A&amp;gt; as core::clone::Clone&amp;gt;::clone
 0.3%   0.5%   5.0KiB clap_builder clap_builder::output::help_template::HelpTemplate::write_all_args
 0.3%   0.5%   5.0KiB clap_builder clap_builder::output::usage::Usage::write_arg_usage
 0.3%   0.5%   4.9KiB    advent_rs advent_rs::year_2016::day_10::solve
 0.3%   0.4%   4.3KiB clap_builder clap_builder::parser::parser::Parser::react
 0.3%   0.4%   4.2KiB clap_builder clap_builder::output::usage::Usage::write_usage_no_title
 0.3%   0.4%   4.2KiB    advent_rs advent_rs::year_2017::day_21::Image::mutate
 0.3%   0.4%   4.1KiB          std addr2line::function::Function&amp;lt;R&amp;gt;::parse_children
 0.3%   0.4%   3.9KiB clap_builder clap_builder::output::help_template::HelpTemplate::write_templated_help
58.8%  87.9% 872.3KiB              And 1925 smaller methods. Use <span class="nt">-n</span> N to show more.
66.9% 100.0% 992.5KiB              .text section size, the file size is 1.4MiB
</pre></div></div><div class="content-img" style="width: 50%"> <img src="/assets/images/2024-10-27/fortune-teller.jpeg" title=" Never going to that fortune-teller ever again " /><div class="img-alt"><p>Never going to that fortune-teller ever again</p></div></div><p>Something must be wrong, let’s try another command.</p><div class="language-bash highlighter-rouge"><div class="highlight">class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
</pre><td class="rouge-code"><pre><span class="nv">$ </span>cargo bloat <span class="nt">--release</span> <span class="nt">--crates</span>
    Finished <span class="sb">`</span>release<span class="sb">`</span> profile <span class="o">[</span>optimized] target<span class="o">(</span>s<span class="o">)</span> <span class="k">in </span>0.01s
    Analyzing target/release/advent-rs

 File  .text     Size Crate
25.9%  38.8% 384.8KiB std
22.3%  33.4% 331.1KiB advent_rs
15.8%  23.5% 233.6KiB clap_builder
 1.2%   1.7%  17.1KiB itertools
 1.1%   1.6%  15.9KiB <span class="o">[</span>Unknown]
 0.7%   1.0%  10.0KiB md5
 0.3%   0.5%   5.0KiB digest
 0.3%   0.5%   4.6KiB strsim
 0.2%   0.3%   3.3KiB clap_lex
 0.1%   0.2%   2.1KiB anstyle
 0.1%   0.2%   1.8KiB anstream
 0.0%   0.0%      16B colorchoice
66.9% 100.0% 992.5KiB .text section size, the file size is 1.4MiB
</pre></div></div><p>First off, I’m surprised that md5 is only taking around 10Kb, so maybe I won’t need to replace it until I really want to shave a few milliseconds off that one exercise<sup id="fnref:fastermd5:1"><a href="#fn:fastermd5" class="footnote" rel="footnote" role="doc-noteref">5</a></sup>. But for the rest…</p><h1 id="what-the-fuck-clap">What the fuck Clap?</h1><p>It seems I’m <a href="https://github.com/clap-rs/clap/issues/1365">not the first person to complain about clap’s binary size</a>. I’m even surprised it can even get to this size because its only use is to parse an array of strings.</p><p>I mean, seriously:</p><div class="language-rust highlighter-rouge"><div class="highlight">class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
</pre><td class="rouge-code"><pre><span class="nd">#[derive(Parser,</span> <span class="nd">Debug)]</span>
<span class="nd">#[command(version,</span> <span class="nd">about,</span> <span class="nd">long_about</span> <span class="nd">=</span> <span class="nd">None)]</span>
<span class="k">struct</span> <span class="n">Args</span> <span class="p">{</span>
  <span class="cd">/// The year of the exercise, from 2015 to today</span>
  <span class="nd">#[arg(short,</span> <span class="nd">long,</span> <span class="nd">value_parser</span> <span class="nd">=</span> <span class="nd">clap::value_parser</span><span class="err">!</span><span class="nd">(u16))]</span>
  <span class="n">year</span><span class="p">:</span>  <span class="nb">u16</span><span class="p">,</span>
  
  <span class="cd">/// The day of the exercise, from 1 to 25</span>
  <span class="nd">#[arg(short,</span> <span class="nd">long,</span> <span class="nd">value_parser</span> <span class="nd">=</span> <span class="nd">clap::value_parser</span><span class="err">!</span><span class="nd">(u8))]</span>
  <span class="n">day</span><span class="p">:</span>  <span class="nb">u8</span><span class="p">,</span>

  <span class="cd">/// The part of the exercise, 1 or 2</span>
  <span class="nd">#[arg(short,</span> <span class="nd">long,</span> <span class="nd">default_value_t</span> <span class="nd">=</span> <span class="mi">1</span><span class="nd">,</span> <span class="nd">value_parser</span> <span class="nd">=</span> <span class="nd">clap::value_parser</span><span class="err">!</span><span class="nd">(u8))]</span>
  <span class="n">part</span><span class="p">:</span>  <span class="nb">u8</span><span class="p">,</span>

  <span class="cd">/// File name</span>
  <span class="nd">#[arg(help</span> <span class="nd">=</span>  <span class="s">"Input file path (will read from STDIN if empty)"</span><span class="nd">,</span> <span class="nd">value_parser</span> <span class="nd">=</span> <span class="nd">clap::value_parser</span><span class="err">!</span><span class="nd">(PathBuf))]</span>
  <span class="n">input</span><span class="p">:</span> <span class="nb">Option</span><span class="o">&amp;lt;</span><span class="n">PathBuf</span><span class="o">&amp;gt;</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">let</span>  <span class="n">args</span>  <span class="o">=</span> <span class="nn">Args</span><span class="p">::</span><span class="nf">parse</span><span class="p">();</span>
<span class="p">}</span>
</pre></div></div><p>And I’m not even using ALL the features!</p><div class="alert alert-info alert-white rounded"><div class="icon"><i class="fa fa-info-circle"></i></div><strong>By the way...</strong><p>I wasn’t very clear on this, but as <a href="https://www.reddit.com/r/rust/comments/1gdd0md/trimming_down_a_rust_binary_in_half/lu1l7eg/">hubbamybubba</a> specified, you can select features from a crate import in your Cargo.toml.</p></div><h1 id="picking-a-sane-alternative-to-clap">Picking a sane alternative to Clap</h1><p>Am I in the mood to look up for a new crate, and completely rewrite the input part of my code? Not really, but I’ve got a blog to write!</p><p>Let’s do a quick Google, and thankfully, among the first results is a <a href="https://github.com/rosetta-rs/argparse-rosetta-rs">recap of various arg-parsing libraries</a>:</p><ul><li><strong>null</strong> Obviously, I’m not in the mood to parse them by name.<li><strong><a href="https://github.com/google/argh">argh</a></strong> Looks very similar to Clap.<li><strong><a href="https://github.com/pacak/bpaf">bpaf</a></strong> If I wanted a DSL, I’d be using Ruby.</ul><div class="alert alert-warning alert-white rounded"><div class="icon"><i class="fa fa-warning"></i></div><strong>That's not a DSL!</strong><p>As <a href="https://www.reddit.com/r/rust/comments/1gdd0md/trimming_down_a_rust_binary_in_half/lu35rsm/">manpacket</a> corrected me, bpaf’s API is not a <a href="https://en.wikipedia.org/wiki/Domain-specific_language">DSL (Domain-Specific Language)</a>, but a <a href="https://en.wikipedia.org/wiki/Fluent_interface">Fluent Interface</a>.</p></div><ul><li><strong><a href="https://docs.rs/gumdrop/latest/gumdrop/">gumdrop</a></strong> That looks like A LOT of code.<li><strong><a href="https://github.com/blyxxyz/lexopt">lexop</a></strong> I kinda like this one’s approach!<li><strong><a href="https://github.com/RazrFalcon/pico-args">pico-args</a></strong> No examples, that’s too simple for my taste.<li><strong><a href="https://github.com/matklad/xflags">xflags</a></strong> Looks a bit too complicated.</ul><p>I can easily start with <strong>Argh</strong> then, it looks similar enough that I could maybe just drop it in and have it work.</p><div class="alert alert-warning alert-white rounded"><div class="icon"><i class="fa fa-warning"></i></div><strong>Attention!</strong><p>While many other libraries are just as small, I chose <strong>argh</strong> because it was simpler to adapt my code, but it comes with (depending on your setup) a huge caveat: <a href="https://www.reddit.com/r/rust/comments/1gdd0md/trimming_down_a_rust_binary_in_half/lu1cq23/">it’s got issues with invalid UTF-8</a>.</p></div><h1 id="the-beauty-of-rust-unit-tests">The beauty of Rust unit tests</h1><p>As a developper, I’ve written a lot of untested code.</p><p>With <a href="https://github.com/joshleaves/advent-rb">advent-rb</a>, I used unit tests for each exercice, using the examples as inputs and result values in <a href="https://github.com/rspec">RSpec tests</a>, which was very fun.</p><p>With <a href="https://github.com/joshleaves/advent-rs">advent-rs</a>, and Rust in general, I discovered a new paradigm. While all the languages I ever used made it “easy” to forget to add another file with the correct name, asked me to remember a complicated syntax, made me think of ways to cheat on my code to access private members from another file,… Rust just made away with that: testing a function is done from the same file where it’s defined, and I don’t even have to care about visibility.</p><div class="content-img" style="width: 75%"> <img src="/assets/images/2024-10-27/allthere-720x405.jpg" title=" It's all there! " /><div class="img-alt"><p>It’s all there!</p></div></div><p>The only thing not handled out-of-the-box is testing a binary (well, that’s not really a unit test), but since everything else was so easy, it felt okay to take some time to write a test specifically for that in <code class="language-plaintext highlighter-rouge">tests/cli.rs</code>.</p><p>In that case, rewriting is very easy:</p><ul><li>remove <code class="language-plaintext highlighter-rouge">clap</code> from dependencies.<li>add <code class="language-plaintext highlighter-rouge">argh</code> to dependencies.<li>rewrite my <code class="language-plaintext highlighter-rouge">#[derive()]</code><li>some syntax differences</ul><p>Quite frankly, the <a href="https://github.com/joshleaves/advent-rs/commit/e821b287eb567627977087b49663717023d224ce">whole diff</a> is nothing short of hilarious given how simple it is.</p><p>But the best part?</p><p>I can just run <code class="language-plaintext highlighter-rouge">cargo test</code> and my tests will tell me everything works the same!</p><h1 id="cleaning-up">Cleaning up</h1><p>Let’s check up on that bloat one more time, shall we?</p><div class="language-bash highlighter-rouge"><div class="highlight">class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre><td class="rouge-code"><pre><span class="nv">$ </span>cargo bloat <span class="nt">--release</span> <span class="nt">--crates</span>
    Finished <span class="sb">`</span>release<span class="sb">`</span> profile <span class="o">[</span>optimized] target<span class="o">(</span>s<span class="o">)</span> <span class="k">in </span>0.01s
    Analyzing target/release/advent-rs

 File  .text     Size Crate
32.8%  49.3% 360.3KiB std
30.3%  45.5% 332.6KiB advent_rs
 1.6%   2.4%  17.2KiB itertools
 1.0%   1.5%  11.2KiB <span class="o">[</span>Unknown]
 0.9%   1.4%  10.0KiB md5
 0.6%   0.8%   6.2KiB argh
 0.5%   0.7%   5.0KiB digest
66.5% 100.0% 730.3KiB .text section size, the file size is 1.1MiB
</pre></div></div><p>As for the file size:</p><div class="language-bash highlighter-rouge"><div class="highlight">class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre><td class="rouge-code"><pre><span class="nv">$ </span><span class="nb">ls</span> <span class="nt">-lah</span>  target/release/advent-rs
<span class="nt">-rwxr-xr-x</span>@ 1 red  staff   898K Oct 27 11:02 target/release/advent-rs
</pre></div></div><p>Woah, not bad, that shaved off 300kb more, and we reduced our total binary size by almost 1MB.</p><h1 id="going-just-a-bit-further">Going just a bit further</h1><p>Once again, I am standing on the shoulders of giants, and while I am quite happy with the trimming I did, I found way more experimentations I could perform on <a href="https://github.com/johnthagen/min-sized-rust">johnthagen’s min-sized-rust repository</a>.</p><p>However, those solutions either require unstable features (we don’t do <code class="language-plaintext highlighter-rouge">nightly</code> in this house), or stripping off Rust’s standard library, which is a whole other can of worms…</p><hr /><div class="footnotes" role="doc-endnotes"><ol><li id="fn:compilation"><p>Blame NodeJS and Ruby! <a href="#fnref:compilation" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a></p><li id="fn:size"><p>As its <a href="https://linux.die.net/man/1/size">man page</a> will tell you, <code class="language-plaintext highlighter-rouge">size</code> prints the “sections” in a binary, helping you visualise the separation between code and data for instance. It’s not very needed here, but it’s a useful command to know about. <a href="#fnref:size" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a></p><li id="fn:speed"><p>This project was also built to compete with <a href="https://github.com/joshleaves/advent-rb">advent-rb</a>, the same project written in Ruby, to marvel at the speed of execution I was missing from compiled languages. <a href="#fnref:speed" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a> <a href="#fnref:speed:1" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;<sup>2</sup></a></p><li id="fn:linking"><p>Linking is the second part of building a project: in layman’s terms, it will bind all code parts together into a single executable. <a href="#fnref:linking" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a></p><li id="fn:fastermd5"><p>Part two of <a href="https://adventofcode.com/2016/day/14">Year 2016: Day 14</a> requires for a string to be hashed on itself 2016 times. Since the input must be a string, and the output is a buffer of hexadecimal data, my code needs to re-translate that hex buffer to a string 2016 times… <a href="#fnref:fastermd5" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a> <a href="#fnref:fastermd5:1" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;<sup>2</sup></a></p><li id="fn:cargo-bloat"><p>The <code class="language-plaintext highlighter-rouge">cargo-bloat</code> command itself will actually bloat your code. I haven’t checked out how, but I’m 99% sure it’s keeping in some symbols in so it can return the name of the fonction taking space. In my case, it’s adding around 200kb. <a href="#fnref:cargo-bloat" class="reversefootnote" role="doc-backlink">&amp;#8617;&amp;#xfe0e;</a></p></ol></div>]]></content> <author> <name>Arnaud 'red' Rouyer</name> </author> <summary>In which we learn to slim down Rust binaries.</summary> </entry> </feed>
