<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Rust Tutorial on</title><link>/tags/rust-tutorial/</link><description>Recent content in Rust Tutorial on</description><generator>Hugo</generator><language>en</language><lastBuildDate>Wed, 05 Nov 2025 11:46:00 +0000</lastBuildDate><atom:link href="/tags/rust-tutorial/index.xml" rel="self" type="application/rss+xml"/><item><title>Lesson 10: When Not to Use Rust — Honest trade-offs</title><link>/post/rust/rust-prod-when-not-rust/</link><pubDate>Wed, 05 Nov 2025 11:46:00 +0000</pubDate><guid>/post/rust/rust-prod-when-not-rust/</guid><description>&lt;p&gt;I like Rust. I&amp;rsquo;ve written 9 lessons about using it in production. I think it&amp;rsquo;s one of the most well-designed languages of the last twenty years. And I&amp;rsquo;m about to spend an entire article telling you when you shouldn&amp;rsquo;t use it.&lt;/p&gt;
&lt;p&gt;Because the most dangerous engineers aren&amp;rsquo;t the ones who don&amp;rsquo;t know Rust — they&amp;rsquo;re the ones who think Rust is always the answer. I&amp;rsquo;ve been that engineer. I once argued for rewriting a Flask API endpoint in Rust because &amp;ldquo;the response time was too high.&amp;rdquo; The response time was 200ms, and 180ms of that was a database query. Rust would have saved us maybe 5ms of JSON serialization. My team lead asked me to go take a walk.&lt;/p&gt;</description></item><item><title>Lesson 9: War Stories — Lessons from real Rust deployments</title><link>/post/rust/rust-prod-war-stories/</link><pubDate>Sun, 02 Nov 2025 15:08:00 +0000</pubDate><guid>/post/rust/rust-prod-war-stories/</guid><description>&lt;p&gt;Every language looks great in blog posts. Production is where the truth comes out. I&amp;rsquo;ve been running Rust services in production for a few years now, and while I&amp;rsquo;m convinced it&amp;rsquo;s the right tool for certain problems, I&amp;rsquo;ve also hit situations where Rust did something I didn&amp;rsquo;t expect, or where its strengths became weaknesses in surprising ways.&lt;/p&gt;
&lt;p&gt;These are real stories. Some names and details are changed, but the bugs and the lessons are exactly as they happened.&lt;/p&gt;</description></item><item><title>Lesson 8: Migrating Services from Go/Python/Java to Rust — When and how</title><link>/post/rust/rust-prod-migration-from-go/</link><pubDate>Thu, 30 Oct 2025 09:33:00 +0000</pubDate><guid>/post/rust/rust-prod-migration-from-go/</guid><description>&lt;p&gt;I&amp;rsquo;ve been involved in three Rust migrations. One from Python, one from Go, one from Java. Two were successes. One was a disaster that got cancelled six months in after burning a quarter of the team&amp;rsquo;s roadmap capacity.&lt;/p&gt;
&lt;p&gt;The failed one wasn&amp;rsquo;t a technical failure — the Rust code was fine. It failed because we rewrote the wrong service, at the wrong time, for the wrong reasons. &amp;ldquo;Rust is faster&amp;rdquo; was the entire justification. Nobody had measured whether speed was actually the bottleneck.&lt;/p&gt;</description></item><item><title>Lesson 7: Feature Flags at the Type Level — Compile-time feature control</title><link>/post/rust/rust-prod-feature-flags/</link><pubDate>Tue, 28 Oct 2025 13:19:00 +0000</pubDate><guid>/post/rust/rust-prod-feature-flags/</guid><description>&lt;p&gt;We had a feature that was ready for staging but absolutely not ready for production. In my previous Go gig, we&amp;rsquo;d have used a runtime feature flag service — LaunchDarkly or similar. Evaluate a boolean at request time, show the new code path to internal testers, hide it from everyone else.&lt;/p&gt;
&lt;p&gt;In Rust, we had another option. We could decide &lt;em&gt;at compile time&lt;/em&gt; whether the feature existed in the binary at all. Not a runtime check. Not a boolean. The code literally wasn&amp;rsquo;t in the production binary. You couldn&amp;rsquo;t accidentally enable it. You couldn&amp;rsquo;t exploit it. It didn&amp;rsquo;t exist.&lt;/p&gt;</description></item><item><title>Lesson 6: API Versioning and Backwards Compatibility — Don't break your users</title><link>/post/rust/rust-prod-backwards-compat/</link><pubDate>Sun, 26 Oct 2025 10:55:00 +0000</pubDate><guid>/post/rust/rust-prod-backwards-compat/</guid><description>&lt;p&gt;I once shipped a &amp;ldquo;minor&amp;rdquo; API change on a Friday. Renamed a JSON field from &lt;code&gt;user_name&lt;/code&gt; to &lt;code&gt;username&lt;/code&gt;. Seemed harmless — we were cleaning up inconsistencies. By Monday morning, we had 14 support tickets from integration partners whose parsers broke. One partner had hardcoded the field name into a system that processed payroll. People didn&amp;rsquo;t get paid because I renamed a JSON field.&lt;/p&gt;
&lt;p&gt;That was the last time I treated backwards compatibility as optional.&lt;/p&gt;</description></item><item><title>Lesson 5: Multi-Crate Workspace Architecture — Scaling your codebase</title><link>/post/rust/rust-prod-multi-crate/</link><pubDate>Thu, 23 Oct 2025 16:42:00 +0000</pubDate><guid>/post/rust/rust-prod-multi-crate/</guid><description>&lt;p&gt;Our compile times hit 8 minutes. Not from scratch — &lt;em&gt;incremental&lt;/em&gt;. Change one line in the domain model and wait 8 minutes to see if it worked. Three engineers were actively avoiding making changes to shared code because the feedback loop was unbearable.&lt;/p&gt;
&lt;p&gt;The problem was obvious: everything lived in one crate. The domain model, the HTTP handlers, the database layer, the gRPC server, the background workers — all sharing one &lt;code&gt;Cargo.toml&lt;/code&gt; with 47 dependencies. Touch anything and the whole thing recompiles.&lt;/p&gt;</description></item><item><title>Lesson 10: Interpreter Pattern — DSLs and parsing</title><link>/post/rust/rust-dp-interpreter/</link><pubDate>Wed, 22 Oct 2025 12:00:00 +0000</pubDate><guid>/post/rust/rust-dp-interpreter/</guid><description>&lt;p&gt;A few years ago I needed to let non-technical users define filtering rules for a data pipeline. The options were: embed Lua, use a YAML config with increasingly awkward syntax, or write a small domain-specific language. I picked the DSL. It took two days in Rust, and the result was a type-safe, sandboxed expression evaluator that couldn&amp;rsquo;t crash the host program no matter what users typed. Try getting that guarantee with dynamic code execution in Python.&lt;/p&gt;</description></item><item><title>Lesson 4: CQRS and Event Sourcing — Separating reads from writes</title><link>/post/rust/rust-prod-cqrs/</link><pubDate>Tue, 21 Oct 2025 08:27:00 +0000</pubDate><guid>/post/rust/rust-prod-cqrs/</guid><description>&lt;p&gt;We had this inventory service that was doing fine until it wasn&amp;rsquo;t. Reads were simple — &amp;ldquo;how many units of product X are available?&amp;rdquo; Writes were complex — reservations, adjustments, transfers between warehouses, reconciliation with physical counts. Both read and write operations hit the same database table, the same data model, and the same set of queries that were getting increasingly gnarly.&lt;/p&gt;
&lt;p&gt;Then we hit Black Friday. Read traffic spiked 40x. The complex write queries were locking rows that the read queries needed. We couldn&amp;rsquo;t scale reads without scaling writes. We couldn&amp;rsquo;t optimize the read path without breaking the write path&amp;rsquo;s invariants.&lt;/p&gt;</description></item><item><title>Lesson 9: Entity Component System — Data-oriented design</title><link>/post/rust/rust-dp-ecs/</link><pubDate>Sun, 19 Oct 2025 15:30:00 +0000</pubDate><guid>/post/rust/rust-dp-ecs/</guid><description>&lt;p&gt;I spent years thinking about game objects the OOP way. A &lt;code&gt;Player&lt;/code&gt; extends &lt;code&gt;Character&lt;/code&gt; extends &lt;code&gt;Entity&lt;/code&gt;. A &lt;code&gt;Goblin&lt;/code&gt; extends &lt;code&gt;Enemy&lt;/code&gt; extends &lt;code&gt;Character&lt;/code&gt; extends &lt;code&gt;Entity&lt;/code&gt;. Then someone asks &amp;ldquo;what if a goblin can be mind-controlled and act like a player?&amp;rdquo; and your inheritance hierarchy collapses.&lt;/p&gt;
&lt;p&gt;ECS — Entity Component System — is the answer that the game development world converged on, and it&amp;rsquo;s fundamentally a Rust-shaped idea. Data and behavior are separated. Composition replaces inheritance. Cache-friendly memory layouts replace pointer-chasing object graphs. Rust&amp;rsquo;s ownership model maps onto ECS so naturally that Bevy — the most popular Rust game engine — is built entirely around it.&lt;/p&gt;</description></item><item><title>Lesson 3: Hexagonal Architecture in Rust — Ports, adapters, and boundaries</title><link>/post/rust/rust-prod-hexagonal/</link><pubDate>Sun, 19 Oct 2025 11:05:00 +0000</pubDate><guid>/post/rust/rust-prod-hexagonal/</guid><description>&lt;p&gt;About a year ago, I had to swap out our payment provider. In Go, that would&amp;rsquo;ve been a two-week project — chasing down every place we called Stripe&amp;rsquo;s SDK, updating structs, fixing test mocks. In our Rust service, it took a day and a half. The reason wasn&amp;rsquo;t Rust itself. It was how we&amp;rsquo;d structured the code.&lt;/p&gt;
&lt;p&gt;Hexagonal architecture (sometimes called &amp;ldquo;ports and adapters&amp;rdquo;) is one of those patterns that sounds academic until you actually need to replace a database, swap a message broker, or test your business logic without spinning up Docker containers. In Rust, traits make it feel natural rather than ceremonial.&lt;/p&gt;</description></item><item><title>Lesson 2: Domain Modeling with Rust's Type System — Making impossible states impossible</title><link>/post/rust/rust-prod-domain-modeling/</link><pubDate>Fri, 17 Oct 2025 14:38:00 +0000</pubDate><guid>/post/rust/rust-prod-domain-modeling/</guid><description>&lt;p&gt;We shipped a bug to production that cost us about three hours of incident response and a very uncomfortable Slack thread. The root cause? Someone passed a &lt;code&gt;user_id&lt;/code&gt; where an &lt;code&gt;order_id&lt;/code&gt; was expected. Both were &lt;code&gt;String&lt;/code&gt;. Both were UUIDs. The compiler had no way to tell them apart. The function signature said &lt;code&gt;fn cancel_order(order_id: String, user_id: String)&lt;/code&gt;, and someone called it with the arguments flipped.&lt;/p&gt;
&lt;p&gt;This is the kind of bug that makes you rethink everything. Not because it&amp;rsquo;s complex — because it&amp;rsquo;s &lt;em&gt;stupid&lt;/em&gt;. And stupid bugs that slip through a strong type system mean the type system wasn&amp;rsquo;t being used right.&lt;/p&gt;</description></item><item><title>Lesson 8: Middleware / Chain of Responsibility — Tower-style</title><link>/post/rust/rust-dp-middleware/</link><pubDate>Thu, 16 Oct 2025 07:15:00 +0000</pubDate><guid>/post/rust/rust-dp-middleware/</guid><description>&lt;p&gt;If you&amp;rsquo;ve built anything with Express, Koa, or ASP.NET, you know middleware. A request comes in, passes through a chain of handlers — logging, auth, rate limiting, CORS — and eventually reaches your application logic. Each handler can modify the request, short-circuit the chain, or pass it along.&lt;/p&gt;
&lt;p&gt;The Chain of Responsibility pattern from the GoF book is basically the same thing. The difference is branding.&lt;/p&gt;
&lt;p&gt;What makes this pattern interesting in Rust is &lt;code&gt;tower&lt;/code&gt; — the crate that defines how middleware works across the entire Rust async ecosystem. Axum, Tonic, Hyper — they all use Tower&amp;rsquo;s &lt;code&gt;Service&lt;/code&gt; trait. Understanding it unlocks the middleware patterns in all of these frameworks.&lt;/p&gt;</description></item><item><title>Lesson 1: Structuring a Large Rust Application — Beyond hello world</title><link>/post/rust/rust-prod-architecture/</link><pubDate>Wed, 15 Oct 2025 09:14:00 +0000</pubDate><guid>/post/rust/rust-prod-architecture/</guid><description>&lt;p&gt;The moment I knew our Rust project structure was broken was when a junior engineer asked me where to put a new endpoint. I opened the repo, stared at the &lt;code&gt;src/&lt;/code&gt; directory, and realized I couldn&amp;rsquo;t confidently answer. We had 40,000 lines of Rust spread across files with names like &lt;code&gt;utils.rs&lt;/code&gt;, &lt;code&gt;helpers.rs&lt;/code&gt;, &lt;code&gt;types.rs&lt;/code&gt;, and the ever-popular &lt;code&gt;misc.rs&lt;/code&gt;. Everything compiled. Nothing made sense.&lt;/p&gt;
&lt;p&gt;Most Rust tutorials stop at &amp;ldquo;put your code in &lt;code&gt;main.rs&lt;/code&gt; and maybe &lt;code&gt;lib.rs&lt;/code&gt;.&amp;rdquo; That works for a CLI tool or a weekend project. It completely falls apart when you&amp;rsquo;ve got a team of eight engineers building a platform with multiple services, shared domain logic, and infrastructure that&amp;rsquo;s evolving every sprint.&lt;/p&gt;</description></item><item><title>Lesson 7: Repository Pattern — Abstracting storage</title><link>/post/rust/rust-dp-repository/</link><pubDate>Mon, 13 Oct 2025 13:45:00 +0000</pubDate><guid>/post/rust/rust-dp-repository/</guid><description>&lt;p&gt;Every backend developer eventually writes the same code: a function that takes a database connection, runs a query, maps the rows to a struct, and returns it. Then you write another one. And another. Pretty soon your business logic is tangled up with SQL strings and connection pool handles, and testing anything requires a running database.&lt;/p&gt;
&lt;p&gt;The Repository pattern fixes this. It&amp;rsquo;s old — Martin Fowler wrote about it in 2002 — but the way Rust implements it is genuinely different from what you&amp;rsquo;d do in Java or C#. Rust&amp;rsquo;s trait system, combined with generics and lifetimes, gives you a repository abstraction that&amp;rsquo;s zero-cost in production and trivially mockable in tests.&lt;/p&gt;</description></item><item><title>Lesson 6: Factory Patterns — When constructors aren't enough</title><link>/post/rust/rust-dp-factory/</link><pubDate>Sat, 11 Oct 2025 10:00:00 +0000</pubDate><guid>/post/rust/rust-dp-factory/</guid><description>&lt;p&gt;Here&amp;rsquo;s a hot take: most &amp;ldquo;Factory pattern&amp;rdquo; usage in Java and C# exists purely to work around limitations of constructors. Constructors can&amp;rsquo;t have descriptive names. They can&amp;rsquo;t return a different subtype. They can&amp;rsquo;t fail gracefully. So you wrap them in a static method and call it a Factory.&lt;/p&gt;
&lt;p&gt;Rust doesn&amp;rsquo;t have constructors at all. Every struct is constructed directly, and associated functions already do what Factory Method does in OOP. So do you even need the Factory pattern in Rust?&lt;/p&gt;</description></item><item><title>Lesson 5: Decorator Pattern — Wrapping with trait composition</title><link>/post/rust/rust-dp-decorator/</link><pubDate>Thu, 09 Oct 2025 16:30:00 +0000</pubDate><guid>/post/rust/rust-dp-decorator/</guid><description>&lt;p&gt;I remember the moment Decorator clicked for me. I was reading the source for Java&amp;rsquo;s I/O library — &lt;code&gt;BufferedInputStream&lt;/code&gt; wrapping &lt;code&gt;FileInputStream&lt;/code&gt; wrapping &lt;code&gt;InputStream&lt;/code&gt;. Three layers deep, each adding behavior without subclassing. Elegant. Then I tried to write the same thing in Rust and learned that &amp;ldquo;wrapping a thing while preserving its interface&amp;rdquo; is a fundamentally different exercise when you don&amp;rsquo;t have inheritance.&lt;/p&gt;
&lt;p&gt;The good news? Rust&amp;rsquo;s version is often &lt;em&gt;better&lt;/em&gt; than the OOP original, because composition is the default and the compiler enforces the contracts.&lt;/p&gt;</description></item><item><title>Lesson 4: Command Pattern — Closures as commands</title><link>/post/rust/rust-dp-command/</link><pubDate>Tue, 07 Oct 2025 09:20:00 +0000</pubDate><guid>/post/rust/rust-dp-command/</guid><description>&lt;p&gt;The Command pattern has always struck me as one of those patterns that&amp;rsquo;s really just &amp;ldquo;wrap a function call in an object.&amp;rdquo; In Java, you create a &lt;code&gt;Command&lt;/code&gt; interface with an &lt;code&gt;execute()&lt;/code&gt; method, make a class for each command, and pass them around. It takes about 40 lines of boilerplate to do what a lambda does in one.&lt;/p&gt;
&lt;p&gt;In Rust, closures &lt;em&gt;are&lt;/em&gt; the Command pattern. But when you need undo, history, or serialization, you still need the structured version — and Rust&amp;rsquo;s ownership model makes the undo/redo part surprisingly elegant.&lt;/p&gt;</description></item><item><title>Lesson 3: Observer Pattern — Channels and callbacks in Rust</title><link>/post/rust/rust-dp-observer/</link><pubDate>Sun, 05 Oct 2025 11:45:00 +0000</pubDate><guid>/post/rust/rust-dp-observer/</guid><description>&lt;p&gt;The Observer pattern is where Rust&amp;rsquo;s ownership model gets &lt;em&gt;really&lt;/em&gt; opinionated. In C# or Java, Observer is simple — maintain a list of listeners, call a method on each when something happens. But &amp;ldquo;a list of mutable references to objects that can be called at any time&amp;rdquo; is basically everything Rust&amp;rsquo;s borrow checker exists to prevent. So how do you do event-driven programming in Rust? You have more options than you&amp;rsquo;d think, and some of them are better than what OOP languages offer.&lt;/p&gt;</description></item><item><title>Lesson 2: Strategy Pattern — Trait objects and generics</title><link>/post/rust/rust-dp-strategy/</link><pubDate>Fri, 03 Oct 2025 14:15:00 +0000</pubDate><guid>/post/rust/rust-dp-strategy/</guid><description>&lt;p&gt;In my first real Go project, I wrote an interface for a payment processor. Two implementations — Stripe and PayPal. Simple polymorphism. When I tried the same thing in Rust, the compiler hit me with a wall of errors about &lt;code&gt;dyn&lt;/code&gt;, &lt;code&gt;Box&lt;/code&gt;, object safety, and sized types. It took me a full afternoon to understand what was happening. The Strategy pattern — which is trivial in most OOP languages — forced me to actually understand Rust&amp;rsquo;s type system. And I came out the other side a better programmer for it.&lt;/p&gt;</description></item><item><title>Lesson 8: Runtime Comparison — Tokio vs async-std vs smol vs glommio</title><link>/post/rust/rust-runtime-comparison/</link><pubDate>Thu, 02 Oct 2025 16:55:31 +0000</pubDate><guid>/post/rust/rust-runtime-comparison/</guid><description>&lt;p&gt;Every few months, someone asks me &amp;ldquo;which async runtime should I use?&amp;rdquo; and every time my answer is frustrating: &amp;ldquo;it depends.&amp;rdquo; But after spending the last seven lessons understanding how runtimes work from the inside, we can finally have a real conversation about what the differences actually are, why they exist, and when each one is the right choice.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve used all four of these runtimes in production. Tokio for most things, smol for a lightweight embedded project, glommio for a storage engine prototype, and async-std briefly before migrating away from it. Let me share what I&amp;rsquo;ve learned — not from reading documentation, but from debugging real problems at 2 AM.&lt;/p&gt;</description></item><item><title>Lesson 1: Builder Pattern — Typestate builders and compile-time validation</title><link>/post/rust/rust-dp-builder-advanced/</link><pubDate>Wed, 01 Oct 2025 08:30:00 +0000</pubDate><guid>/post/rust/rust-dp-builder-advanced/</guid><description>&lt;p&gt;I once spent three days debugging a production outage caused by a builder that silently accepted a missing &lt;code&gt;host&lt;/code&gt; field and defaulted to &lt;code&gt;localhost&lt;/code&gt;. In Java. The builder compiled fine, the tests passed — because they ran against localhost — and the deployment connected to nothing. That was the day I stopped trusting optional fields in builders.&lt;/p&gt;
&lt;p&gt;Rust&amp;rsquo;s type system lets you make that entire category of bug impossible. Not at runtime. Not with validation methods. At &lt;em&gt;compile time&lt;/em&gt;.&lt;/p&gt;</description></item><item><title>Lesson 7: Designing a Custom Async Runtime — When Tokio isn't enough</title><link>/post/rust/rust-runtime-custom-runtime/</link><pubDate>Tue, 30 Sep 2025 11:20:06 +0000</pubDate><guid>/post/rust/rust-runtime-custom-runtime/</guid><description>&lt;p&gt;A colleague once asked me, &amp;ldquo;Why would anyone build a custom async runtime when Tokio exists?&amp;rdquo; Fair question. Tokio is battle-tested, well-maintained, and fast. For 95% of use cases, it&amp;rsquo;s the right answer. But I&amp;rsquo;ve now been in three situations where it wasn&amp;rsquo;t — and each one taught me something about what a runtime actually does.&lt;/p&gt;
&lt;p&gt;The first was a latency-sensitive trading system where work-stealing&amp;rsquo;s cache invalidation was unacceptable. The second was an embedded system with no allocator. The third was a specialized database engine where we needed precise control over I/O scheduling. Each time, understanding how to build a runtime from the ground up saved the project.&lt;/p&gt;</description></item><item><title>Lesson 6: Work-Stealing Schedulers — Balancing load across cores</title><link>/post/rust/rust-runtime-work-stealing/</link><pubDate>Sat, 27 Sep 2025 13:45:50 +0000</pubDate><guid>/post/rust/rust-runtime-work-stealing/</guid><description>&lt;p&gt;I once spent two days debugging a performance issue where our Tokio application was using four cores but only one was doing any work. Three threads were idle, one was pegged at 100%. The problem wasn&amp;rsquo;t Tokio&amp;rsquo;s scheduler — it was ours. We&amp;rsquo;d accidentally created a pattern where all tasks were spawned from and waking on the same thread, and nothing triggered work-stealing because the tasks completed too quickly.&lt;/p&gt;
&lt;p&gt;That experience taught me that you can&amp;rsquo;t treat the scheduler as a black box. If you understand how work-stealing works, you can design your task topology to take advantage of it. If you don&amp;rsquo;t, you&amp;rsquo;ll write code that accidentally defeats it.&lt;/p&gt;</description></item><item><title>Lesson 5: The Reactor Pattern — How Tokio actually works</title><link>/post/rust/rust-runtime-reactor-pattern/</link><pubDate>Wed, 24 Sep 2025 09:27:13 +0000</pubDate><guid>/post/rust/rust-runtime-reactor-pattern/</guid><description>&lt;p&gt;I read the Tokio source code on a Sunday afternoon, expecting to find something inscrutable. Layers of unsafe code, impenetrable abstractions, the kind of thing that makes you question your career choices. What I actually found was a clean, well-documented reactor implementation that I could follow. Not easily — but followably. The architecture is elegant once you see how the pieces connect.&lt;/p&gt;
&lt;p&gt;This lesson is about that architecture. Not Tokio&amp;rsquo;s API — you already know how to use &lt;code&gt;tokio::spawn&lt;/code&gt; and &lt;code&gt;TcpStream&lt;/code&gt;. This is about what happens &lt;em&gt;underneath&lt;/em&gt; when you call those functions. The reactor pattern, the I/O driver, the timer wheel, and how they all feed into the executor.&lt;/p&gt;</description></item><item><title>Lesson 10: Dependent Type Tricks — Encoding constraints in types</title><link>/post/rust/rust-type-dependent-types/</link><pubDate>Mon, 22 Sep 2025 15:50:00 +0000</pubDate><guid>/post/rust/rust-type-dependent-types/</guid><description>&lt;p&gt;There&amp;rsquo;s a running joke in the Rust community: &amp;ldquo;Rust has a dependent type system, it just doesn&amp;rsquo;t know it.&amp;rdquo; And like most good jokes, there&amp;rsquo;s truth in it. Rust doesn&amp;rsquo;t have &lt;em&gt;real&lt;/em&gt; dependent types like Idris or Agda, where types can depend on arbitrary runtime values. But with const generics, sealed constructors, and some creative type engineering, you can get surprisingly close.&lt;/p&gt;
&lt;p&gt;This final lesson is about pushing Rust&amp;rsquo;s type system to its absolute limits — encoding constraints that most people assume you need a dependently-typed language for.&lt;/p&gt;</description></item><item><title>Lesson 4: epoll/kqueue — Platform event loops</title><link>/post/rust/rust-runtime-epoll-kqueue/</link><pubDate>Sun, 21 Sep 2025 17:08:45 +0000</pubDate><guid>/post/rust/rust-runtime-epoll-kqueue/</guid><description>&lt;p&gt;Before I understood event loops, I thought async I/O was some kind of kernel magic. You register interest in a socket, and somehow the OS tells you when data arrives — without blocking a thread. How? I imagined complex kernel subsystems doing heavy lifting behind the scenes.&lt;/p&gt;
&lt;p&gt;Turns out, the mechanism is almost embarrassingly simple. The kernel maintains a list of file descriptors you care about. When something happens on one of them, it flips a bit. You ask &amp;ldquo;what happened?&amp;rdquo;, it tells you. That&amp;rsquo;s it. The entire async I/O ecosystem — Tokio, Node.js, nginx, everything — is built on this one primitive.&lt;/p&gt;</description></item><item><title>Lesson 9: Proof Witnesses — Types as proofs</title><link>/post/rust/rust-type-proof-witnesses/</link><pubDate>Fri, 19 Sep 2025 13:22:00 +0000</pubDate><guid>/post/rust/rust-type-proof-witnesses/</guid><description>&lt;p&gt;The moment this clicked for me was when I was reviewing a crate that had a function signature like &lt;code&gt;fn process(data: &amp;amp;[u8], _proof: NonEmpty&amp;lt;'_&amp;gt;)&lt;/code&gt;. That second argument carried &lt;em&gt;no data&lt;/em&gt;. It was zero-sized. But you could only construct it by proving — through code that the compiler checked — that the slice was non-empty. The proof existed at compile time. At runtime, it was nothing.&lt;/p&gt;
&lt;p&gt;Types as proofs. Once you see it, you can&amp;rsquo;t unsee it.&lt;/p&gt;</description></item><item><title>Lesson 3: io_uring — Zero-copy async I/O on Linux</title><link>/post/rust/rust-runtime-io-uring/</link><pubDate>Fri, 19 Sep 2025 10:33:08 +0000</pubDate><guid>/post/rust/rust-runtime-io-uring/</guid><description>&lt;p&gt;I ran a benchmark last year that genuinely surprised me. A simple TCP echo server using &lt;code&gt;io_uring&lt;/code&gt; was handling 40% more connections per second than the same server on &lt;code&gt;epoll&lt;/code&gt;, with measurably lower tail latencies. Not 5% — forty percent. On the same hardware, same kernel version, same application logic. That&amp;rsquo;s when I stopped treating &lt;code&gt;io_uring&lt;/code&gt; as a curiosity and started treating it as the future of Linux I/O.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve been building async runtimes on &lt;code&gt;epoll&lt;/code&gt; (or even &lt;code&gt;kqueue&lt;/code&gt; on macOS), &lt;code&gt;io_uring&lt;/code&gt; changes the game completely. Let me show you why.&lt;/p&gt;</description></item><item><title>Lesson 2: Building a Minimal Executor — Your own async runtime</title><link>/post/rust/rust-runtime-executor/</link><pubDate>Wed, 17 Sep 2025 14:12:37 +0000</pubDate><guid>/post/rust/rust-runtime-executor/</guid><description>&lt;p&gt;The moment I built my first executor from scratch, async Rust stopped being scary. Not because executors are simple — they&amp;rsquo;re not — but because once you see the machinery, every &amp;ldquo;mysterious&amp;rdquo; behavior has an obvious explanation. Futures hanging? The waker isn&amp;rsquo;t being called. Tasks not making progress? The executor&amp;rsquo;s run loop has a bug. Performance terrible? You&amp;rsquo;re probably polling too aggressively or not enough.&lt;/p&gt;
&lt;p&gt;So let&amp;rsquo;s build one. A real, working executor. Not a production-quality one (that&amp;rsquo;s Tokio&amp;rsquo;s job), but one that actually runs futures to completion, handles multiple tasks, and demonstrates every concept from the previous lesson.&lt;/p&gt;</description></item><item><title>Lesson 8: Sealed Traits — Closing extension points</title><link>/post/rust/rust-type-sealed-traits/</link><pubDate>Tue, 16 Sep 2025 09:38:00 +0000</pubDate><guid>/post/rust/rust-type-sealed-traits/</guid><description>&lt;p&gt;I was designing a public API for a parser library when I realized I had a problem. I wanted users to &lt;em&gt;use&lt;/em&gt; my trait — call its methods, pass it as a bound — but I did &lt;em&gt;not&lt;/em&gt; want them implementing it for their own types. Every new implementation would need to maintain invariants that I couldn&amp;rsquo;t enforce through the trait interface alone. If someone implemented it wrong, they&amp;rsquo;d get subtly broken behavior with no good error message.&lt;/p&gt;</description></item><item><title>Lesson 1: Future Internals — Poll, Waker, Context</title><link>/post/rust/rust-runtime-future-internals/</link><pubDate>Mon, 15 Sep 2025 08:45:22 +0000</pubDate><guid>/post/rust/rust-runtime-future-internals/</guid><description>&lt;p&gt;I thought I understood Rust futures until I tried to implement one without &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;. Not a toy future that immediately returns &lt;code&gt;Ready&lt;/code&gt;. A real future — one that yields, gets woken up, and resumes where it left off. That exercise broke every mental model I had and rebuilt it from scratch.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; syntax is one of Rust&amp;rsquo;s great lies. It looks simple. It &lt;em&gt;feels&lt;/em&gt; like you&amp;rsquo;re writing sequential code. Under the hood, the compiler is generating state machines, threading waker references through call graphs, and constructing self-referential structs that would make most C++ programmers nervous. Let&amp;rsquo;s rip the lid off.&lt;/p&gt;</description></item><item><title>Lesson 7: Type-Level Programming — Computing with types</title><link>/post/rust/rust-type-type-level-integers/</link><pubDate>Sat, 13 Sep 2025 11:15:00 +0000</pubDate><guid>/post/rust/rust-type-type-level-integers/</guid><description>&lt;p&gt;There&amp;rsquo;s a moment in every Rust programmer&amp;rsquo;s journey where they look at &lt;code&gt;typenum&lt;/code&gt; or some const-generics trick and think: &amp;ldquo;Wait, we&amp;rsquo;re doing &lt;em&gt;math&lt;/em&gt; at compile time? With the &lt;em&gt;type checker&lt;/em&gt;?&amp;rdquo; Yes. Yes we are. And it&amp;rsquo;s not a curiosity — it&amp;rsquo;s the foundation for things like fixed-size matrices, compile-time dimension checking, and provably correct buffer sizes.&lt;/p&gt;
&lt;p&gt;I first encountered type-level integers when I needed a matrix library that could guarantee at compile time that you couldn&amp;rsquo;t multiply a 3x4 matrix by a 2x5 matrix. The dimensions had to match, and I wanted the compiler — not a runtime assertion — to enforce it.&lt;/p&gt;</description></item><item><title>Lesson 6: Variance — Covariance, contravariance, invariance</title><link>/post/rust/rust-type-variance/</link><pubDate>Thu, 11 Sep 2025 16:42:00 +0000</pubDate><guid>/post/rust/rust-type-variance/</guid><description>&lt;p&gt;Variance is the topic that made me realize I didn&amp;rsquo;t actually understand Rust&amp;rsquo;s type system as well as I thought I did. I&amp;rsquo;d been writing Rust for over a year, had shipped production code, even written some unsafe blocks — and then I hit a lifetime error that I could not explain. The borrow checker was rejecting code that looked perfectly fine. Turns out, variance was the reason.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve ever had a lifetime error that made no sense, variance might be the missing piece.&lt;/p&gt;</description></item><item><title>Lesson 5: Existential Types — impl Trait in depth</title><link>/post/rust/rust-type-existential/</link><pubDate>Tue, 09 Sep 2025 07:28:00 +0000</pubDate><guid>/post/rust/rust-type-existential/</guid><description>&lt;p&gt;I used &lt;code&gt;impl Trait&lt;/code&gt; for months thinking it was just syntactic sugar for generics. &amp;ldquo;It&amp;rsquo;s the same as a type parameter, right? Just shorter?&amp;rdquo; No. It&amp;rsquo;s fundamentally different, and understanding &lt;em&gt;how&lt;/em&gt; it&amp;rsquo;s different unlocks patterns that are genuinely impossible with plain generics.&lt;/p&gt;
&lt;p&gt;Let me show you.&lt;/p&gt;
&lt;h2 id="two-positions-two-meanings"&gt;Two Positions, Two Meanings&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;impl Trait&lt;/code&gt; means completely different things depending on where you use it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Argument position: &amp;#34;I accept any type that implements Iterator&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;count_items&lt;/span&gt;(iter: &lt;span style="color:#a6e22e"&gt;impl&lt;/span&gt; Iterator&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Item &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;i32&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;) -&amp;gt; &lt;span style="color:#66d9ef"&gt;usize&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; iter.count()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Return position: &amp;#34;I return some specific type that implements Iterator,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// but I&amp;#39;m not telling you which one&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;make_numbers&lt;/span&gt;() -&amp;gt; &lt;span style="color:#a6e22e"&gt;impl&lt;/span&gt; Iterator&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Item &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;i32&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&lt;span style="color:#f92672"&gt;..&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;).filter(&lt;span style="color:#f92672"&gt;|&lt;/span&gt;x&lt;span style="color:#f92672"&gt;|&lt;/span&gt; x &lt;span style="color:#f92672"&gt;%&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In argument position, the &lt;em&gt;caller&lt;/em&gt; chooses the concrete type. In return position, the &lt;em&gt;function&lt;/em&gt; chooses — and the caller can&amp;rsquo;t see what it picked. These are fundamentally different concepts from type theory.&lt;/p&gt;</description></item><item><title>Lesson 4: Simulating Higher-Kinded Types in Rust — The workarounds</title><link>/post/rust/rust-type-higher-kinded/</link><pubDate>Sun, 07 Sep 2025 19:03:00 +0000</pubDate><guid>/post/rust/rust-type-higher-kinded/</guid><description>&lt;p&gt;Every time someone on Reddit says &amp;ldquo;Rust can&amp;rsquo;t do higher-kinded types,&amp;rdquo; a part of me wants to respond with a 200-line code block that proves them&amp;hellip; well, partially wrong. Rust doesn&amp;rsquo;t have &lt;em&gt;native&lt;/em&gt; HKTs, that&amp;rsquo;s true. But the workarounds are surprisingly expressive, and with GATs (generic associated types) now stable, we can get &lt;em&gt;most&lt;/em&gt; of what you&amp;rsquo;d want from HKTs in practice.&lt;/p&gt;
&lt;p&gt;Let me walk you through the problem, why it matters, and the patterns that let you work around it.&lt;/p&gt;</description></item><item><title>Lesson 3: Session Types — Protocol safety at compile time</title><link>/post/rust/rust-type-session-types/</link><pubDate>Fri, 05 Sep 2025 10:45:00 +0000</pubDate><guid>/post/rust/rust-type-session-types/</guid><description>&lt;p&gt;I once spent three days debugging a distributed system where two services were sending messages in the wrong order. Service A expected a handshake acknowledgment before data, but Service B had been refactored to send data first. Both services compiled, both passed their unit tests, and both exploded in production. The protocol contract existed only in a Google Doc that nobody had updated.&lt;/p&gt;
&lt;p&gt;Session types fix this. They encode the &lt;em&gt;entire communication protocol&lt;/em&gt; in the type system — who sends what, in what order, and when. If you violate the protocol, your code doesn&amp;rsquo;t compile. Period.&lt;/p&gt;</description></item><item><title>Lesson 2: Advanced Typestate — Multi-state machines at compile time</title><link>/post/rust/rust-type-typestate-advanced/</link><pubDate>Wed, 03 Sep 2025 14:17:00 +0000</pubDate><guid>/post/rust/rust-type-typestate-advanced/</guid><description>&lt;p&gt;The first time I implemented a connection pool that &lt;em&gt;couldn&amp;rsquo;t&lt;/em&gt; be misused — not through discipline or documentation, but because the compiler physically rejected invalid state transitions — I felt like I&amp;rsquo;d discovered a cheat code. Not a runtime check. Not an assertion. A straight-up compiler error if you tried to read from a connection you hadn&amp;rsquo;t authenticated yet.&lt;/p&gt;
&lt;p&gt;This is the typestate pattern taken to its logical extreme. In Lesson 1 we saw the basics with &lt;code&gt;PhantomData&lt;/code&gt; and builder states. Now we&amp;rsquo;re going to encode full multi-state machines where every transition is checked at compile time. No runtime overhead. No state field to match on. Just the type system doing its job.&lt;/p&gt;</description></item><item><title>Lesson 1: Zero-Sized Types — PhantomData, () as design tools</title><link>/post/rust/rust-type-zero-sized/</link><pubDate>Mon, 01 Sep 2025 08:32:00 +0000</pubDate><guid>/post/rust/rust-type-zero-sized/</guid><description>&lt;p&gt;I remember staring at a struct definition in a library I was reading — it had a field of type &lt;code&gt;PhantomData&amp;lt;T&amp;gt;&lt;/code&gt; and I thought, &amp;ldquo;this does literally nothing.&amp;rdquo; The field takes up zero bytes. It has no runtime representation. Why on earth would you put a field in a struct that doesn&amp;rsquo;t exist at runtime?&lt;/p&gt;
&lt;p&gt;Turns out, zero-sized types are one of the most powerful design tools in Rust. They let you embed meaning into the type system without paying a single byte of overhead. Once you get this, you&amp;rsquo;ll start seeing ZSTs everywhere — and you&amp;rsquo;ll start using them yourself.&lt;/p&gt;</description></item><item><title>Lesson 8: ML Data Pipelines — polars and processing at speed</title><link>/post/rust/rust-ai-ml-pipelines/</link><pubDate>Sat, 30 Aug 2025 14:15:00 +0000</pubDate><guid>/post/rust/rust-ai-ml-pipelines/</guid><description>&lt;p&gt;Last quarter I inherited a Python data pipeline that prepared training data for our recommendation model. It processed 50 million rows. Took 3 hours. Used 64GB of RAM. Everyone accepted this as normal — &amp;ldquo;big data is slow.&amp;rdquo; I rewrote it in Rust with polars. Same data. 4 minutes. 6GB of RAM. My teammates thought I was lying until they ran it themselves.&lt;/p&gt;
&lt;p&gt;Polars isn&amp;rsquo;t just &amp;ldquo;pandas but faster.&amp;rdquo; It&amp;rsquo;s a fundamentally different approach to DataFrame operations — lazy evaluation, query optimization, true parallelism, and a Rust-native API that makes data pipeline code genuinely pleasant to write.&lt;/p&gt;</description></item><item><title>Lesson 7: On-Device Inference — ONNX Runtime and candle</title><link>/post/rust/rust-ai-onnx-inference/</link><pubDate>Tue, 26 Aug 2025 07:49:00 +0000</pubDate><guid>/post/rust/rust-ai-onnx-inference/</guid><description>&lt;p&gt;I run a sentiment analysis model on every support ticket that comes in. At first I used the OpenAI API — about 2 cents per ticket. Sounds cheap until you do the math: 10,000 tickets a day, $200/day, $6,000/month. For a model that classifies text into &amp;ldquo;positive,&amp;rdquo; &amp;ldquo;negative,&amp;rdquo; and &amp;ldquo;neutral.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Switched to a local ONNX model running on a $50/month VM. Same accuracy. Latency dropped from 300ms to 8ms. Cost dropped to roughly zero. Not every task needs GPT-4 — and Rust is arguably the best language for running models locally because you get C++ performance without the C++ pain.&lt;/p&gt;</description></item><item><title>Lesson 6: Building MCP Servers in Rust — Model Context Protocol</title><link>/post/rust/rust-ai-mcp-servers/</link><pubDate>Fri, 22 Aug 2025 10:23:00 +0000</pubDate><guid>/post/rust/rust-ai-mcp-servers/</guid><description>&lt;p&gt;The first time I heard about MCP, I dismissed it as yet another protocol nobody would adopt. Then Claude Desktop shipped with MCP support, then Cursor, then Windsurf, then half the AI tools I use daily. Turns out when Anthropic publishes a spec and immediately supports it in their flagship products, adoption happens fast.&lt;/p&gt;
&lt;p&gt;MCP — Model Context Protocol — is a standardized way for AI models to discover and use tools, access data sources, and interact with external systems. Think of it as USB for AI: a universal interface so models don&amp;rsquo;t need custom integrations for every data source. And Rust is a fantastic language for building MCP servers because they need to be fast, reliable, and run for a long time without leaking memory.&lt;/p&gt;</description></item><item><title>Lesson 5: Agent Architectures in Rust — ReAct, planning, and loops</title><link>/post/rust/rust-ai-agent-architectures/</link><pubDate>Wed, 20 Aug 2025 13:08:00 +0000</pubDate><guid>/post/rust/rust-ai-agent-architectures/</guid><description>&lt;p&gt;I built my first &amp;ldquo;AI agent&amp;rdquo; by stuffing a system prompt into a while loop and hoping for the best. It worked — sometimes. Other times it&amp;rsquo;d get stuck in infinite loops, burn through $50 of API credits hallucinating tool calls that didn&amp;rsquo;t exist, or confidently produce completely wrong answers after three rounds of &amp;ldquo;reasoning.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The problem wasn&amp;rsquo;t the LLM. The problem was me treating agent design as an afterthought. Good agents need structure — clear state machines, well-defined stopping conditions, and guardrails that prevent runaway behavior. This is where Rust&amp;rsquo;s type system pays massive dividends, because you can encode these constraints at the type level.&lt;/p&gt;</description></item><item><title>Lesson 8: Reproducible Builds — Same source, same binary</title><link>/post/rust/rust-build-reproducible/</link><pubDate>Mon, 18 Aug 2025 15:25:00 +0000</pubDate><guid>/post/rust/rust-build-reproducible/</guid><description>&lt;p&gt;A security auditor once asked me to prove that the binary running in production was actually built from the source code we claimed. I confidently ran &lt;code&gt;cargo build --release&lt;/code&gt;, compared the hash of the output with the deployed binary, and&amp;hellip; they were different. Same source, same compiler, same machine, different binary. That&amp;rsquo;s when I learned that reproducible builds aren&amp;rsquo;t automatic — even in Rust.&lt;/p&gt;
&lt;h2 id="why-reproducible-builds-matter"&gt;Why Reproducible Builds Matter&lt;/h2&gt;
&lt;p&gt;A reproducible build means: given the same source code, same dependencies, same compiler, and same configuration, you get a bit-for-bit identical binary every time, on any machine.&lt;/p&gt;</description></item><item><title>Lesson 4: Embeddings and Vector Search — Semantic search in Rust</title><link>/post/rust/rust-ai-embeddings/</link><pubDate>Mon, 18 Aug 2025 08:55:00 +0000</pubDate><guid>/post/rust/rust-ai-embeddings/</guid><description>&lt;p&gt;I spent a week building a keyword search system for internal documentation. Regex patterns, stemming, tf-idf scoring — the whole nine yards. Then someone searched &amp;ldquo;how do I deploy&amp;rdquo; and got zero results because every doc said &amp;ldquo;deployment process&amp;rdquo; instead of &amp;ldquo;deploy.&amp;rdquo; That&amp;rsquo;s when I switched to embeddings.&lt;/p&gt;
&lt;p&gt;Embeddings map text into high-dimensional vectors where semantically similar content lives close together. &amp;ldquo;Deploy&amp;rdquo; and &amp;ldquo;deployment process&amp;rdquo; end up near each other in vector space even though they share almost no characters. It&amp;rsquo;s a fundamentally different approach to search, and once you&amp;rsquo;ve used it, keyword search feels like the dark ages.&lt;/p&gt;</description></item><item><title>Lesson 3: Tool Calling / Function Calling Patterns — Agents need tools</title><link>/post/rust/rust-ai-tool-calling/</link><pubDate>Sat, 16 Aug 2025 16:42:00 +0000</pubDate><guid>/post/rust/rust-ai-tool-calling/</guid><description>&lt;p&gt;Here&amp;rsquo;s something that took me embarrassingly long to internalize: LLMs don&amp;rsquo;t &lt;em&gt;do&lt;/em&gt; things. They generate text that &lt;em&gt;describes&lt;/em&gt; doing things. The tool calling protocol is just the model saying &amp;ldquo;hey, I&amp;rsquo;d like you to call this function with these arguments&amp;rdquo; — and then your code actually does it.&lt;/p&gt;
&lt;p&gt;This distinction matters because the entire tool calling system is essentially a serialization contract. The model generates JSON conforming to a schema you provided, you execute the function, and you send the result back. Get the schema wrong, and the model hallucinates arguments. Get the execution wrong, and you&amp;rsquo;ve got a broken agent. Get the result format wrong, and the model can&amp;rsquo;t make sense of what happened.&lt;/p&gt;</description></item><item><title>Lesson 7: Linking Strategies — Static, dynamic, LTO</title><link>/post/rust/rust-build-linking/</link><pubDate>Fri, 15 Aug 2025 10:40:00 +0000</pubDate><guid>/post/rust/rust-build-linking/</guid><description>&lt;p&gt;I deployed a Rust service to a minimal Docker container once — Alpine Linux, nothing installed except the binary. It crashed immediately with &amp;ldquo;not a dynamic executable.&amp;rdquo; Turns out my binary was dynamically linked against glibc, but Alpine uses musl. I&amp;rsquo;d never thought about linking before that day. Now it&amp;rsquo;s one of the first things I configure on any new project.&lt;/p&gt;
&lt;h2 id="what-linking-actually-is"&gt;What Linking Actually Is&lt;/h2&gt;
&lt;p&gt;When you write &lt;code&gt;cargo build&lt;/code&gt;, the compiler doesn&amp;rsquo;t produce a binary directly. It produces object files — chunks of machine code for each compilation unit. The &lt;em&gt;linker&lt;/em&gt; takes all those object files, plus any libraries you depend on, and stitches them together into a single executable.&lt;/p&gt;</description></item><item><title>Lesson 2: Streaming LLM Responses — SSE and WebSockets</title><link>/post/rust/rust-ai-streaming/</link><pubDate>Thu, 14 Aug 2025 11:37:00 +0000</pubDate><guid>/post/rust/rust-ai-streaming/</guid><description>&lt;p&gt;The first time I demoed an LLM-powered feature to stakeholders, I made the rookie mistake of using non-streaming responses. The CEO asked a question, hit enter, and stared at a blank screen for eight seconds. &amp;ldquo;Is it broken?&amp;rdquo; No — it was thinking. But by the time the response appeared, she&amp;rsquo;d already mentally moved on to the next agenda item.&lt;/p&gt;
&lt;p&gt;Streaming changes everything. Users see tokens appearing in real-time, which feels responsive even when the total generation time is identical. But implementing streaming in Rust? It&amp;rsquo;s one of those things that&amp;rsquo;s surprisingly nuanced once you get past the happy path.&lt;/p&gt;</description></item><item><title>Lesson 6: Monorepo Management with Workspaces — Scaling Rust projects</title><link>/post/rust/rust-build-monorepo/</link><pubDate>Tue, 12 Aug 2025 13:10:00 +0000</pubDate><guid>/post/rust/rust-build-monorepo/</guid><description>&lt;p&gt;Our Rust project started as a single crate. Then we split the API handlers from the domain logic. Then we extracted shared types. Then someone added a CLI tool. Then a worker service. Before we knew it, we had nine crates and &lt;code&gt;cargo build&lt;/code&gt; was doing weird things because three of them depended on different versions of &lt;code&gt;serde&lt;/code&gt;. That&amp;rsquo;s when we sat down and properly set up a workspace.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve now managed Rust workspaces ranging from 5 crates to over 40. The patterns I&amp;rsquo;m going to share come from real mistakes — dependency hell, circular imports, CI builds that took 45 minutes, the works.&lt;/p&gt;</description></item><item><title>Lesson 1: Building LLM API Clients in Rust — Type-safe AI calls</title><link>/post/rust/rust-ai-llm-clients/</link><pubDate>Tue, 12 Aug 2025 09:14:00 +0000</pubDate><guid>/post/rust/rust-ai-llm-clients/</guid><description>&lt;p&gt;Last month I watched a coworker&amp;rsquo;s Python script silently swallow a malformed response from the OpenAI API. The &lt;code&gt;choices&lt;/code&gt; field came back empty, the code plowed ahead with &lt;code&gt;choices[0]&lt;/code&gt;, and the whole pipeline crashed at 2 AM. Nobody got paged because the error handler was also broken. Classic.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the moment I decided to rebuild our LLM integration layer in Rust. Not because I&amp;rsquo;m some Rust evangelist who thinks Python is evil — I use Python daily. But when you&amp;rsquo;re making API calls that cost real money and feed into production systems, maybe you want a type system that actually catches things before runtime.&lt;/p&gt;</description></item><item><title>Lesson 5: Code Generation — proc macros, build.rs, xtask</title><link>/post/rust/rust-build-code-generation/</link><pubDate>Sun, 10 Aug 2025 08:55:00 +0000</pubDate><guid>/post/rust/rust-build-code-generation/</guid><description>&lt;p&gt;I once inherited a codebase where someone had written a Python script that generated 4,000 lines of Rust from a YAML spec. The script ran outside of Cargo, the generated file was checked into git, and nobody remembered to re-run it when the spec changed. By the time I found it, the generated code and the spec had diverged in twelve places. That experience shaped how I think about code generation in Rust — it needs to be integrated into the build, not bolted on the side.&lt;/p&gt;</description></item><item><title>Lesson 12: Production Systems Software — Databases, runtimes, proxies</title><link>/post/rust/rust-sys-production-systems/</link><pubDate>Fri, 08 Aug 2025 09:33:27 +0000</pubDate><guid>/post/rust/rust-sys-production-systems/</guid><description>&lt;p&gt;I&amp;rsquo;ve been building systems software professionally for a while now, and here&amp;rsquo;s what I&amp;rsquo;ve noticed: the skills we&amp;rsquo;ve covered in this course — &lt;code&gt;no_std&lt;/code&gt; programming, memory-mapped I/O, custom allocators, interrupt handlers, network protocols — they all converge when you build production systems software. A database engine is just a file system on top of a storage engine with a query processor. A network proxy is packet parsing plus connection management. A language runtime is memory management plus a scheduler.&lt;/p&gt;</description></item><item><title>Lesson 4: Custom Lints with clippy and dylint — Your team's rules</title><link>/post/rust/rust-build-custom-lints/</link><pubDate>Thu, 07 Aug 2025 16:20:00 +0000</pubDate><guid>/post/rust/rust-build-custom-lints/</guid><description>&lt;p&gt;We had this rule on my old team: never use &lt;code&gt;.unwrap()&lt;/code&gt; on database query results. We wrote it in our contributing guide. We mentioned it in code reviews. We added it to the onboarding doc. And yet, every single sprint, someone would push an &lt;code&gt;.unwrap()&lt;/code&gt; on a &lt;code&gt;sqlx::Result&lt;/code&gt; that would blow up in production at 2 AM. That&amp;rsquo;s when I decided to make the compiler enforce our rules instead of relying on humans to remember them.&lt;/p&gt;</description></item><item><title>Lesson 3: Conditional Compilation — cfg, features, target</title><link>/post/rust/rust-build-conditional/</link><pubDate>Tue, 05 Aug 2025 11:45:00 +0000</pubDate><guid>/post/rust/rust-build-conditional/</guid><description>&lt;p&gt;A few months back I was debugging a test failure that only happened on our Linux CI server, never on my Mac. Turns out someone had written platform-specific file path handling without proper &lt;code&gt;cfg&lt;/code&gt; guards — the code compiled fine on both platforms but silently did the wrong thing on Linux. That&amp;rsquo;s when I really internalized why conditional compilation needs to be treated as a first-class skill, not something you google when you need it.&lt;/p&gt;</description></item><item><title>Lesson 11: Building a Minimal Hypervisor — Virtualization in Rust</title><link>/post/rust/rust-sys-hypervisor/</link><pubDate>Mon, 04 Aug 2025 15:42:18 +0000</pubDate><guid>/post/rust/rust-sys-hypervisor/</guid><description>&lt;p&gt;The first time I watched a virtual machine boot — not using VirtualBox, but running inside a hypervisor &lt;em&gt;I&amp;rsquo;d written&lt;/em&gt; — I had the same feeling as when my bootloader printed its first character. Except this time, I wasn&amp;rsquo;t just running code on bare metal. I was creating a &lt;em&gt;fake machine&lt;/em&gt; that thought it was running on bare metal.&lt;/p&gt;
&lt;p&gt;Virtualization is where systems programming hits its ceiling of complexity. You&amp;rsquo;re manipulating the CPU&amp;rsquo;s hardware virtualization extensions to create isolated execution environments. It&amp;rsquo;s also where Rust&amp;rsquo;s safety guarantees become most valuable — because a bug in a hypervisor doesn&amp;rsquo;t just crash your program, it potentially compromises every virtual machine running on the host.&lt;/p&gt;</description></item><item><title>Lesson 2: build.rs — Code generation at compile time</title><link>/post/rust/rust-build-build-scripts/</link><pubDate>Sun, 03 Aug 2025 14:30:00 +0000</pubDate><guid>/post/rust/rust-build-build-scripts/</guid><description>&lt;p&gt;The first time I needed &lt;code&gt;build.rs&lt;/code&gt;, I was wrapping a C library that had about 200 constants defined in a header file. I could&amp;rsquo;ve copied them all by hand into Rust &lt;code&gt;const&lt;/code&gt; declarations. Instead, I wrote a build script that parsed the header and generated the constants automatically. Took 30 minutes to write the build script, and it saved me from maintaining a manual mapping that would&amp;rsquo;ve drifted out of sync within a month.&lt;/p&gt;</description></item><item><title>Lesson 10: Writing a Bootloader — The first code that runs</title><link>/post/rust/rust-sys-bootloader/</link><pubDate>Fri, 01 Aug 2025 22:10:44 +0000</pubDate><guid>/post/rust/rust-sys-bootloader/</guid><description>&lt;p&gt;There&amp;rsquo;s something almost spiritual about writing a bootloader. Your code is the &lt;em&gt;first thing that runs&lt;/em&gt; on a machine. Before the OS. Before any drivers. Before the memory manager. Before anything. The CPU comes out of reset, fetches an instruction from a known address, and that instruction is yours.&lt;/p&gt;
&lt;p&gt;I spent a weekend writing one. By Sunday night, I had four characters on screen — &lt;code&gt;BOOT&lt;/code&gt; — rendered by writing directly to VGA memory. And it felt like I&amp;rsquo;d conquered the world.&lt;/p&gt;</description></item><item><title>Lesson 1: Cargo Deep Dive — Workspaces, features, profiles</title><link>/post/rust/rust-build-cargo-deep-dive/</link><pubDate>Fri, 01 Aug 2025 09:15:00 +0000</pubDate><guid>/post/rust/rust-build-cargo-deep-dive/</guid><description>&lt;p&gt;I&amp;rsquo;d been writing Rust for about a year before I realized I was only using maybe 20% of what Cargo actually offers. &lt;code&gt;cargo build&lt;/code&gt;, &lt;code&gt;cargo run&lt;/code&gt;, &lt;code&gt;cargo test&lt;/code&gt; — that was my entire workflow. Then I joined a team managing a Rust monorepo with 30+ crates, custom build profiles, and feature flags controlling everything from database backends to telemetry. Suddenly my surface-level Cargo knowledge wasn&amp;rsquo;t cutting it.&lt;/p&gt;
&lt;h2 id="cargo-is-not-just-a-build-tool"&gt;Cargo Is Not Just a Build Tool&lt;/h2&gt;
&lt;p&gt;Most people coming from other languages think of Cargo as &amp;ldquo;npm for Rust&amp;rdquo; or &amp;ldquo;Maven for Rust.&amp;rdquo; That undersells it massively. Cargo is a build system, package manager, test runner, benchmark runner, documentation generator, and project convention enforcer — all rolled into one binary. And unlike most build tools, it&amp;rsquo;s actually pleasant to use.&lt;/p&gt;</description></item><item><title>Lesson 9: Interrupt Handlers and Real-Time Constraints — When timing matters</title><link>/post/rust/rust-sys-interrupt-handlers/</link><pubDate>Wed, 30 Jul 2025 06:55:22 +0000</pubDate><guid>/post/rust/rust-sys-interrupt-handlers/</guid><description>&lt;p&gt;I once spent three days debugging a motor controller that would randomly twitch. The code was correct. The hardware was fine. The interrupt handler was well-written. But every few thousand cycles, a timer interrupt would preempt the motor control interrupt at exactly the wrong moment, corrupting a shared variable. The fix was two lines of code — disable interrupts around the critical section — but finding it cost me a weekend.&lt;/p&gt;</description></item><item><title>Lesson 8: Designing Custom Allocators — Beyond the global allocator</title><link>/post/rust/rust-sys-allocator-design/</link><pubDate>Sun, 27 Jul 2025 13:19:56 +0000</pubDate><guid>/post/rust/rust-sys-allocator-design/</guid><description>&lt;p&gt;Here&amp;rsquo;s a dirty secret of systems programming: &lt;code&gt;malloc&lt;/code&gt; is not magic. It&amp;rsquo;s just code. Code that somebody wrote, code that makes tradeoffs, and code you can replace when those tradeoffs don&amp;rsquo;t match your workload.&lt;/p&gt;
&lt;p&gt;I spent two years writing performance-sensitive Rust before I realized that the global allocator was my bottleneck. Not CPU. Not I/O. Memory allocation — thousands of tiny allocations per request, each one hitting a lock, each one fragmenting the heap a little more. Switching to a bump allocator for request-scoped data cut latency by 40%.&lt;/p&gt;</description></item><item><title>Lesson 7: Implementing Network Protocols — TCP from scratch</title><link>/post/rust/rust-sys-network-stack/</link><pubDate>Thu, 24 Jul 2025 10:31:48 +0000</pubDate><guid>/post/rust/rust-sys-network-stack/</guid><description>&lt;p&gt;I thought I understood TCP until I tried to implement it. Turns out, &amp;ldquo;client connects to server, data flows&amp;rdquo; is about 5% of the story. The other 95% is state machines, retransmission timers, congestion windows, and edge cases that would make your head spin.&lt;/p&gt;
&lt;p&gt;But here&amp;rsquo;s the good news: implementing even a simplified TCP teaches you more about networking than any textbook. And Rust&amp;rsquo;s type system is actually perfect for modeling protocol state machines — states become types, invalid transitions become compile errors.&lt;/p&gt;</description></item><item><title>Lesson 6: Building a File System — From blocks to files</title><link>/post/rust/rust-sys-file-systems/</link><pubDate>Tue, 22 Jul 2025 16:08:32 +0000</pubDate><guid>/post/rust/rust-sys-file-systems/</guid><description>&lt;p&gt;The moment a file system clicked for me was when I stopped thinking of files as &amp;ldquo;things on disk&amp;rdquo; and started thinking of them as &amp;ldquo;names mapped to byte ranges scattered across a block device.&amp;rdquo; That sounds more complicated, but it&amp;rsquo;s actually simpler — because now there&amp;rsquo;s no magic. Just data structures.&lt;/p&gt;
&lt;p&gt;Every file system, from FAT16 to ZFS, answers the same fundamental questions: Where does this file&amp;rsquo;s data live on disk? How do I find a file by name? What metadata (size, permissions, timestamps) does each file have? Let&amp;rsquo;s answer all of these by building one from scratch.&lt;/p&gt;</description></item><item><title>Lesson 5: OS Concepts in Rust — Processes, threads, signals</title><link>/post/rust/rust-sys-os-concepts/</link><pubDate>Sat, 19 Jul 2025 07:45:19 +0000</pubDate><guid>/post/rust/rust-sys-os-concepts/</guid><description>&lt;p&gt;I used to think I understood processes. Then I tried to implement &lt;code&gt;fork()&lt;/code&gt; semantics in Rust and realized I&amp;rsquo;d been cargo-culting UNIX concepts for years without actually understanding what was happening underneath.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the thing — Rust forces you to think about OS primitives more carefully than C ever did. The ownership model doesn&amp;rsquo;t just prevent memory bugs; it makes you confront questions like &amp;ldquo;who owns a file descriptor?&amp;rdquo; and &amp;ldquo;what happens to shared memory after fork?&amp;rdquo; that C lets you handwave past.&lt;/p&gt;</description></item><item><title>Lesson 8: The Component Model — Composable WASM modules</title><link>/post/rust/rust-wasm-component-model/</link><pubDate>Fri, 18 Jul 2025 10:41:09 +0000</pubDate><guid>/post/rust/rust-wasm-component-model/</guid><description>&lt;p&gt;Here&amp;rsquo;s a scenario that actually happened to me: I had a data validation library written in Rust, a business logic layer in Go, and a reporting module that a client had written in Python. Three languages, three teams, three deployment stories. Traditionally, this means three services talking over HTTP with serialization overhead, network latency, and a distributed systems headache.&lt;/p&gt;
&lt;p&gt;With the Component Model, I compiled all three to WASM components, composed them into a single module, and ran the whole pipeline in one process. No network calls, no serialization, no containers. Function calls across language boundaries, at native speed.&lt;/p&gt;</description></item><item><title>Lesson 4: Writing Linux Kernel Modules in Rust — Rust in the kernel</title><link>/post/rust/rust-sys-kernel-modules/</link><pubDate>Thu, 17 Jul 2025 11:23:45 +0000</pubDate><guid>/post/rust/rust-sys-kernel-modules/</guid><description>&lt;p&gt;In December 2022, Rust officially merged into the Linux kernel source tree. Not as an experiment. Not as a sidecar. As a first-class language for writing kernel code. Linus Torvalds signed off on it.&lt;/p&gt;
&lt;p&gt;I remember reading the mailing list thread and thinking: &amp;ldquo;This is either going to be the most important thing to happen to systems programming in twenty years, or the most spectacular failure.&amp;rdquo; Three years in, it&amp;rsquo;s looking a lot like the former.&lt;/p&gt;</description></item><item><title>Lesson 7: WASI — WebAssembly beyond the browser</title><link>/post/rust/rust-wasm-wasi/</link><pubDate>Tue, 15 Jul 2025 19:22:41 +0000</pubDate><guid>/post/rust/rust-wasm-wasi/</guid><description>&lt;p&gt;About a year ago, I deployed a Rust function as a Cloudflare Worker using WASM. Cold start: 0.5ms. Compare that to a Lambda function in any other language — 50-500ms on a cold start. That&amp;rsquo;s when I realized WASI isn&amp;rsquo;t some academic curiosity. It&amp;rsquo;s the future of server-side compute.&lt;/p&gt;
&lt;p&gt;Solomon Hykes — the guy who created Docker — tweeted this back in 2019: &amp;ldquo;If WASM+WASI existed in 2008, we wouldn&amp;rsquo;t have needed to create Docker.&amp;rdquo; He wasn&amp;rsquo;t being hyperbolic. WASI gives you true sandboxing, near-native performance, cross-platform portability, and sub-millisecond startup. It&amp;rsquo;s what containers promised, but at a fundamentally lower level.&lt;/p&gt;</description></item><item><title>Lesson 3: Memory-Mapped I/O — Talking to hardware</title><link>/post/rust/rust-sys-memory-mapped-io/</link><pubDate>Mon, 14 Jul 2025 19:52:07 +0000</pubDate><guid>/post/rust/rust-sys-memory-mapped-io/</guid><description>&lt;p&gt;There&amp;rsquo;s a moment in every systems programmer&amp;rsquo;s life when they realize that writing to memory address &lt;code&gt;0x4002_0818&lt;/code&gt; doesn&amp;rsquo;t store a value — it turns on an LED. That address isn&amp;rsquo;t RAM. It&amp;rsquo;s a hardware register. And the CPU doesn&amp;rsquo;t know the difference.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s memory-mapped I/O in a nutshell, and it&amp;rsquo;s how virtually all hardware communication works on modern processors. Understanding it properly is the difference between code that happens to work and code that&amp;rsquo;s &lt;em&gt;correct&lt;/em&gt;.&lt;/p&gt;</description></item><item><title>Lesson 6: Multi-Threaded WASM — SharedArrayBuffer and atomics</title><link>/post/rust/rust-wasm-threads/</link><pubDate>Sun, 13 Jul 2025 07:55:18 +0000</pubDate><guid>/post/rust/rust-wasm-threads/</guid><description>&lt;p&gt;I got a 4.2x speedup on a real-time audio processing pipeline by adding threads to my WASM module. Four threads, 4.2x faster — nearly linear scaling. That almost never happens in practice, but WASM threading hits a sweet spot: the workloads that justify WASM in the first place (heavy computation, large data) are exactly the workloads that parallelize well.&lt;/p&gt;
&lt;p&gt;The bad news? Getting threads working in WASM is more involved than &lt;code&gt;std::thread::spawn&lt;/code&gt;. There are browser security requirements, Web Worker coordination, shared memory semantics, and a whole build pipeline to figure out. Let me walk you through all of it.&lt;/p&gt;</description></item><item><title>Lesson 2: Embedded Rust — Microcontrollers and bare metal</title><link>/post/rust/rust-sys-embedded-intro/</link><pubDate>Sat, 12 Jul 2025 14:37:51 +0000</pubDate><guid>/post/rust/rust-sys-embedded-intro/</guid><description>&lt;p&gt;I bricked my first development board within forty-five minutes of getting it out of the box. Wrote some C code, forgot to configure the clock properly, flashed it, and the thing just&amp;hellip; stopped responding. No debugger connection. No serial output. A $15 paperweight.&lt;/p&gt;
&lt;p&gt;That experience — the raw, unforgiving nature of hardware programming — is exactly why Rust matters in embedded. Not because it prevents you from writing to the wrong register (it can&amp;rsquo;t, really), but because it gives you tools to &lt;em&gt;structure&lt;/em&gt; hardware access so mistakes become harder to make.&lt;/p&gt;</description></item><item><title>Lesson 5: WASM Performance — When it beats JavaScript</title><link>/post/rust/rust-wasm-performance/</link><pubDate>Thu, 10 Jul 2025 16:08:51 +0000</pubDate><guid>/post/rust/rust-wasm-performance/</guid><description>&lt;p&gt;&amp;ldquo;WASM is faster than JavaScript.&amp;rdquo; I&amp;rsquo;ve heard this so many times, and it drives me nuts — not because it&amp;rsquo;s wrong, but because it&amp;rsquo;s incomplete. WASM &lt;em&gt;can&lt;/em&gt; be faster than JavaScript. It can also be slower. The difference depends on what you&amp;rsquo;re doing, how you&amp;rsquo;re crossing the JS↔WASM boundary, and whether you&amp;rsquo;ve hit the specific scenarios where WASM&amp;rsquo;s architecture actually gives you an advantage.&lt;/p&gt;
&lt;p&gt;I ran benchmarks for months to figure out where the real boundaries are. Let me show you the data.&lt;/p&gt;</description></item><item><title>Lesson 1: no_std — Rust without the standard library</title><link>/post/rust/rust-sys-no-std/</link><pubDate>Thu, 10 Jul 2025 08:14:33 +0000</pubDate><guid>/post/rust/rust-sys-no-std/</guid><description>&lt;p&gt;The first time I tried to compile a Rust program with &lt;code&gt;#![no_std]&lt;/code&gt;, I felt like someone had pulled the floor out from under me. No &lt;code&gt;println!&lt;/code&gt;. No &lt;code&gt;String&lt;/code&gt;. No &lt;code&gt;Vec&lt;/code&gt;. No &lt;code&gt;HashMap&lt;/code&gt;. Half the stuff I relied on daily — just &lt;em&gt;gone&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s exactly the point.&lt;/p&gt;
&lt;h2 id="why-would-anyone-do-this"&gt;Why Would Anyone Do This?&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the thing most Rust tutorials won&amp;rsquo;t tell you up front: the standard library is &lt;em&gt;enormous&lt;/em&gt;. It pulls in heap allocation, threading, file I/O, networking, and a whole OS-level runtime. That&amp;rsquo;s great for application development. It&amp;rsquo;s a non-starter for:&lt;/p&gt;</description></item><item><title>Lesson 4: Leptos, Yew, Dioxus — Full-stack Rust</title><link>/post/rust/rust-wasm-web-frameworks/</link><pubDate>Mon, 07 Jul 2025 09:17:33 +0000</pubDate><guid>/post/rust/rust-wasm-web-frameworks/</guid><description>&lt;p&gt;After building that todo list with raw &lt;code&gt;web-sys&lt;/code&gt; in Lesson 3, I think we can all agree: manually managing DOM nodes, closures wrapped in &lt;code&gt;Rc&amp;lt;RefCell&amp;lt;Option&amp;lt;Closure&amp;lt;dyn FnMut()&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt;, and string-based style attributes isn&amp;rsquo;t how anyone wants to build a real application. That&amp;rsquo;s where Rust frontend frameworks come in. And we&amp;rsquo;ve got three serious contenders — each with a different philosophy about how to build web UIs.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve built side projects with all three. Let me tell you what actually matters when choosing between them.&lt;/p&gt;</description></item><item><title>Lesson 3: Manipulating the DOM from Rust — Web without JavaScript</title><link>/post/rust/rust-wasm-dom/</link><pubDate>Sat, 05 Jul 2025 11:33:47 +0000</pubDate><guid>/post/rust/rust-wasm-dom/</guid><description>&lt;p&gt;There&amp;rsquo;s something deeply satisfying about writing &lt;code&gt;document.create_element(&amp;quot;div&amp;quot;)&lt;/code&gt; in Rust and watching it actually work in a browser. It&amp;rsquo;s also, if I&amp;rsquo;m being honest, kind of painful — because &lt;code&gt;web-sys&lt;/code&gt; wraps every single Web API call in &lt;code&gt;Result&lt;/code&gt; types, and you end up with &lt;code&gt;.unwrap()&lt;/code&gt; chains that would make any Rustacean cringe. But once you build the right abstractions on top, it becomes surprisingly pleasant. Let me show you how I got there.&lt;/p&gt;</description></item><item><title>Lesson 10: Soundness — The ultimate safety guarantee</title><link>/post/rust/rust-unsafe-soundness/</link><pubDate>Fri, 04 Jul 2025 16:50:00 +0000</pubDate><guid>/post/rust/rust-unsafe-soundness/</guid><description>&lt;p&gt;A few months ago, someone filed a soundness bug against a crate I maintained. The report was elegant — three lines of safe code that triggered a use-after-free through my API. No &lt;code&gt;unsafe&lt;/code&gt; in the caller&amp;rsquo;s code. The bug was in my &lt;code&gt;unsafe&lt;/code&gt; implementation. I fixed it within the hour, cut a patch release, and filed a CVE advisory. That&amp;rsquo;s the social contract of soundness in Rust: if safe code can cause undefined behavior, the bug is &lt;em&gt;always&lt;/em&gt; in the library.&lt;/p&gt;</description></item><item><title>Lesson 2: wasm-bindgen — Bridging Rust and JavaScript</title><link>/post/rust/rust-wasm-bindgen/</link><pubDate>Thu, 03 Jul 2025 14:12:05 +0000</pubDate><guid>/post/rust/rust-wasm-bindgen/</guid><description>&lt;p&gt;The first time I looked at what &lt;code&gt;#[wasm_bindgen]&lt;/code&gt; actually generates, I was equal parts impressed and horrified. Impressed because it seamlessly bridges two fundamentally different type systems. Horrified because the generated code is a labyrinth of pointer arithmetic, descriptor tables, and heap management. But here&amp;rsquo;s the thing — you don&amp;rsquo;t need to understand every line of generated code. You do need to understand the &lt;em&gt;model&lt;/em&gt;, because when things go wrong (and they will), the model is what helps you debug.&lt;/p&gt;</description></item><item><title>Lesson 9: napi-rs — Rust extensions for Node.js</title><link>/post/rust/rust-unsafe-ffi-node/</link><pubDate>Tue, 01 Jul 2025 13:26:00 +0000</pubDate><guid>/post/rust/rust-unsafe-ffi-node/</guid><description>&lt;p&gt;We had a Node.js microservice that validated JWTs. Under load, the &lt;code&gt;jsonwebtoken&lt;/code&gt; npm package was burning 40% of CPU on RSA signature verification. I wrote the verification in Rust with napi-rs, dropped it in as a replacement, and CPU usage fell to 8%. The JavaScript API didn&amp;rsquo;t change at all — same function name, same arguments, same return type. Just 5x faster.&lt;/p&gt;
&lt;p&gt;napi-rs is to Node.js what PyO3 is to Python. You write Rust, export it as a native Node addon, and call it from JavaScript like any other module. The framework handles all the N-API complexity, type marshaling, and async integration.&lt;/p&gt;</description></item><item><title>Lesson 1: Rust to WebAssembly — Why and how</title><link>/post/rust/rust-wasm-intro/</link><pubDate>Tue, 01 Jul 2025 08:45:22 +0000</pubDate><guid>/post/rust/rust-wasm-intro/</guid><description>&lt;p&gt;I was optimizing a client-side image processing pipeline last year — think heavy convolutions, histogram equalization, color space conversions. The JavaScript implementation was doing about 12 frames per second. I rewrote the core loops in Rust, compiled to WebAssembly, and hit 55 fps. Same browser. Same machine. That&amp;rsquo;s the moment WebAssembly stopped being a curiosity and became a tool I actually reach for.&lt;/p&gt;
&lt;p&gt;But let me be real: getting there wasn&amp;rsquo;t a straight line. The tooling has rough edges, the mental model is different from writing server-side Rust, and half the blog posts out there show you how to add two numbers in WASM and call it a tutorial. That&amp;rsquo;s not what we&amp;rsquo;re doing here.&lt;/p&gt;</description></item><item><title>Lesson 8: PyO3 — Rust extensions for Python</title><link>/post/rust/rust-unsafe-ffi-python/</link><pubDate>Sat, 28 Jun 2025 08:14:00 +0000</pubDate><guid>/post/rust/rust-unsafe-ffi-python/</guid><description>&lt;p&gt;I had a Python service that processed 2 million JSON records daily. Profiling showed 80% of the time was spent in one function — a custom similarity scoring algorithm. I rewrote that single function in Rust with PyO3. Same API, same tests, same deployment. Processing time dropped from 47 minutes to 90 seconds. The Python team didn&amp;rsquo;t have to learn Rust, didn&amp;rsquo;t have to change their imports, didn&amp;rsquo;t even notice — they just saw their pipeline get 30x faster.&lt;/p&gt;</description></item><item><title>Lesson 7: Exposing Rust to C — cdylib and cbindgen</title><link>/post/rust/rust-unsafe-ffi-rust-from-c/</link><pubDate>Wed, 25 Jun 2025 15:40:00 +0000</pubDate><guid>/post/rust/rust-unsafe-ffi-rust-from-c/</guid><description>&lt;p&gt;A team I was advising had a massive C codebase — about 400,000 lines of networking code. They wanted to rewrite their TLS handling in Rust but couldn&amp;rsquo;t justify a full rewrite. The solution: build Rust as a shared library, expose a C-compatible API, and link it into the existing build. Took a week to get the first version working. The memory safety bugs in that module dropped to zero.&lt;/p&gt;</description></item><item><title>Lesson 6: FFI — Calling C from Rust</title><link>/post/rust/rust-unsafe-ffi-c/</link><pubDate>Mon, 23 Jun 2025 11:55:00 +0000</pubDate><guid>/post/rust/rust-unsafe-ffi-c/</guid><description>&lt;p&gt;My first real FFI project was binding to SQLite. I thought &amp;ldquo;how hard can it be — it&amp;rsquo;s just calling C functions.&amp;rdquo; Three days later I was debugging a segfault caused by a string lifetime issue where Rust freed a &lt;code&gt;CString&lt;/code&gt; while SQLite was still reading from the pointer. That experience taught me more about unsafe Rust than any tutorial ever could.&lt;/p&gt;
&lt;p&gt;Calling C from Rust is the most common FFI scenario. Every operating system API is C. Most high-performance libraries — OpenSSL, zlib, SQLite, libcurl — are C. If you&amp;rsquo;re writing systems software in Rust, you&amp;rsquo;ll need this skill.&lt;/p&gt;</description></item><item><title>Lesson 5: Building Safe Abstractions Over Unsafe Code — The encapsulation pattern</title><link>/post/rust/rust-unsafe-safe-abstractions/</link><pubDate>Fri, 20 Jun 2025 09:22:00 +0000</pubDate><guid>/post/rust/rust-unsafe-safe-abstractions/</guid><description>&lt;p&gt;The standard library&amp;rsquo;s &lt;code&gt;Vec&amp;lt;T&amp;gt;&lt;/code&gt; contains over 50 &lt;code&gt;unsafe&lt;/code&gt; blocks. &lt;code&gt;HashMap&lt;/code&gt; has even more. Yet you use both every day without thinking about safety — because their public APIs are entirely safe. The &lt;code&gt;unsafe&lt;/code&gt; is invisible, encapsulated behind type system boundaries that make misuse impossible.&lt;/p&gt;
&lt;p&gt;This is the most important pattern in Rust: unsafe internals, safe surface. Master it, and you can build anything.&lt;/p&gt;
&lt;h2 id="the-core-principle"&gt;The Core Principle&lt;/h2&gt;
&lt;p&gt;An &lt;code&gt;unsafe&lt;/code&gt; block means &amp;ldquo;I&amp;rsquo;ve verified the invariants.&amp;rdquo; A safe API means &amp;ldquo;the type system prevents invariant violations.&amp;rdquo; The goal is to push all the verification into the implementation so that &lt;em&gt;users&lt;/em&gt; of your code can&amp;rsquo;t break the invariants no matter what they do.&lt;/p&gt;</description></item><item><title>Lesson 4: transmute — Type punning and its dangers</title><link>/post/rust/rust-unsafe-transmute/</link><pubDate>Wed, 18 Jun 2025 17:08:00 +0000</pubDate><guid>/post/rust/rust-unsafe-transmute/</guid><description>&lt;p&gt;I once watched a senior engineer &lt;code&gt;transmute&lt;/code&gt; a &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt; into a &lt;code&gt;Vec&amp;lt;u32&amp;gt;&lt;/code&gt; and couldn&amp;rsquo;t figure out why it segfaulted on ARM but worked fine on x86. Spoiler: alignment. The allocator returned 1-byte-aligned memory for the &lt;code&gt;u8&lt;/code&gt; vec, and &lt;code&gt;u32&lt;/code&gt; needs 4-byte alignment. On x86 you pay a performance penalty; on ARM you get a bus error.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;transmute&lt;/code&gt; is Rust&amp;rsquo;s most powerful unsafe tool. It reinterprets the bits of one type as another type. No conversion, no transformation — just &amp;ldquo;these bytes are now a different type.&amp;rdquo; That power makes it incredibly useful and incredibly dangerous.&lt;/p&gt;</description></item><item><title>Lesson 8: Monolith-First — Modular monoliths in Rust</title><link>/post/rust/rust-micro-monolith-first/</link><pubDate>Tue, 17 Jun 2025 13:19:00 +0000</pubDate><guid>/post/rust/rust-micro-monolith-first/</guid><description>&lt;p&gt;I&amp;rsquo;m going to tell you something that might sound weird after seven lessons about microservices patterns: don&amp;rsquo;t start with microservices. Start with a monolith. A well-structured, modular monolith that&amp;rsquo;s &lt;em&gt;designed&lt;/em&gt; to be split later.&lt;/p&gt;
&lt;p&gt;This isn&amp;rsquo;t contrarianism for its own sake. I&amp;rsquo;ve seen three teams build microservices from day one. All three regretted it. One team spent more time debugging distributed system issues than building features. Another had seven services that each handled about 50 requests per day — the infrastructure cost was absurd. The third discovered six months in that they&amp;rsquo;d drawn their service boundaries wrong and had to do a painful re-architecture.&lt;/p&gt;</description></item><item><title>Lesson 3: Dereferencing Raw Pointers Safely — The patterns that work</title><link>/post/rust/rust-unsafe-deref/</link><pubDate>Mon, 16 Jun 2025 10:45:00 +0000</pubDate><guid>/post/rust/rust-unsafe-deref/</guid><description>&lt;p&gt;A colleague once showed me a bug that took them three days to find. Their &lt;code&gt;unsafe&lt;/code&gt; code dereferenced a pointer that was valid when created but dangling by the time it was used — a classic lifetime mismatch. The fix was two lines. The debugging was seventy-two hours. That ratio is why this lesson exists.&lt;/p&gt;
&lt;p&gt;Dereferencing raw pointers is the most common &lt;code&gt;unsafe&lt;/code&gt; operation you&amp;rsquo;ll encounter, and getting it right means following specific patterns. Not guidelines — patterns. Repeatable, auditable approaches that make your &lt;code&gt;unsafe&lt;/code&gt; code reviewable.&lt;/p&gt;</description></item><item><title>Lesson 2: Raw Pointers — *const T, *mut T and when you need them</title><link>/post/rust/rust-unsafe-raw-pointers/</link><pubDate>Sat, 14 Jun 2025 14:17:00 +0000</pubDate><guid>/post/rust/rust-unsafe-raw-pointers/</guid><description>&lt;p&gt;The first time I used raw pointers in Rust, I was porting a ring buffer from C. I&amp;rsquo;d written ring buffers in C a dozen times — head pointer, tail pointer, wrap around, done. In Rust, the borrow checker wanted nothing to do with my two mutable pointers into the same buffer. That&amp;rsquo;s when I learned what raw pointers are actually for.&lt;/p&gt;
&lt;h2 id="references-vs-raw-pointers"&gt;References vs Raw Pointers&lt;/h2&gt;
&lt;p&gt;Rust references (&lt;code&gt;&amp;amp;T&lt;/code&gt; and &lt;code&gt;&amp;amp;mut T&lt;/code&gt;) come with guarantees enforced by the compiler:&lt;/p&gt;</description></item><item><title>Lesson 7: Testing Microservices — Contract tests and integration</title><link>/post/rust/rust-micro-testing/</link><pubDate>Sat, 14 Jun 2025 07:33:00 +0000</pubDate><guid>/post/rust/rust-micro-testing/</guid><description>&lt;p&gt;I deployed a change to the order service that renamed a field from &lt;code&gt;total_amount&lt;/code&gt; to &lt;code&gt;total_cents&lt;/code&gt;. Made perfect sense — cents avoid floating-point nonsense. All unit tests passed. Integration tests passed. Staging looked fine.&lt;/p&gt;
&lt;p&gt;Production broke instantly. The payment service was still expecting &lt;code&gt;total_amount&lt;/code&gt;. It deserialized the response, got &lt;code&gt;None&lt;/code&gt; for the amount, and started processing $0 charges. We caught it in four minutes, but four minutes of free orders adds up.&lt;/p&gt;</description></item><item><title>Lesson 1: What unsafe Actually Means — The contract you're signing</title><link>/post/rust/rust-unsafe-what-it-means/</link><pubDate>Thu, 12 Jun 2025 08:32:00 +0000</pubDate><guid>/post/rust/rust-unsafe-what-it-means/</guid><description>&lt;p&gt;I spent my first six months writing Rust thinking &lt;code&gt;unsafe&lt;/code&gt; meant &amp;ldquo;this code is dangerous and you should feel bad.&amp;rdquo; That misunderstanding cost me weeks — I&amp;rsquo;d bend over backwards to avoid it, writing convoluted safe wrappers around problems that genuinely needed a raw pointer or two. Once I actually read the Rustonomicon and understood what &lt;code&gt;unsafe&lt;/code&gt; &lt;em&gt;really&lt;/em&gt; means, everything clicked.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s clear this up properly.&lt;/p&gt;
&lt;h2 id="unsafe-is-not-what-you-think"&gt;unsafe Is Not What You Think&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the biggest misconception in the Rust ecosystem: &lt;code&gt;unsafe&lt;/code&gt; does not mean &amp;ldquo;this code is broken&amp;rdquo; or &amp;ldquo;this code does bad things.&amp;rdquo; It means &lt;strong&gt;&amp;ldquo;I, the programmer, am upholding invariants that the compiler cannot verify.&amp;rdquo;&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Lesson 6: Distributed Tracing Across Services — Following requests</title><link>/post/rust/rust-micro-tracing/</link><pubDate>Wed, 11 Jun 2025 10:45:00 +0000</pubDate><guid>/post/rust/rust-micro-tracing/</guid><description>&lt;p&gt;&amp;ldquo;It&amp;rsquo;s slow&amp;rdquo; is the most useless bug report in a microservices world. Slow where? The API gateway? The order service? The database query inside the payment service? The message queue between inventory and shipping? When a single user action touches five services and three databases, &amp;ldquo;it&amp;rsquo;s slow&amp;rdquo; could mean anything.&lt;/p&gt;
&lt;p&gt;I spent an entire afternoon once trying to track down a latency spike. Added timing logs to every service. Correlated timestamps across hosts. Manually stitched together the request flow from six different log streams. Found the culprit: a DNS resolution that was taking 800ms because of a misconfigured resolver — in a service I didn&amp;rsquo;t even know was involved.&lt;/p&gt;</description></item><item><title>Lesson 5: Service Mesh Integration — Istio, Linkerd, and Rust</title><link>/post/rust/rust-micro-service-mesh/</link><pubDate>Mon, 09 Jun 2025 16:21:00 +0000</pubDate><guid>/post/rust/rust-micro-service-mesh/</guid><description>&lt;p&gt;I&amp;rsquo;ll be honest — when someone first pitched &amp;ldquo;service mesh&amp;rdquo; to me, I thought it was over-engineered marketing. You&amp;rsquo;re telling me I need a sidecar proxy bolted onto every pod, a control plane to manage those proxies, and custom CRDs to configure traffic routing&amp;hellip; just to do what a load balancer and some retry logic could handle?&lt;/p&gt;
&lt;p&gt;Then I ran a fleet of 20+ services in production. mTLS between everything? Doing that in application code is painful. Per-route retry policies? Circuit breaking with consistent configuration? Gradual traffic shifting for canary deploys? At that scale, doing it all in app code means doing it differently in every service, with different bugs in each implementation.&lt;/p&gt;</description></item><item><title>Lesson 4: Saga Pattern — Distributed transactions without 2PC</title><link>/post/rust/rust-micro-saga/</link><pubDate>Sat, 07 Jun 2025 08:48:00 +0000</pubDate><guid>/post/rust/rust-micro-saga/</guid><description>&lt;p&gt;Here&amp;rsquo;s a scenario that&amp;rsquo;ll ruin your week. A customer places an order. Your order service saves it. Your payment service charges their card. Your inventory service reserves the items. Your shipping service schedules a pickup. Then the shipping service discovers the item is oversized and can&amp;rsquo;t be shipped to that address.&lt;/p&gt;
&lt;p&gt;Now what? The card&amp;rsquo;s been charged. The inventory&amp;rsquo;s been reserved. The order exists. You need to undo three things across three services, each with their own database, each with their own failure modes. Welcome to distributed transactions.&lt;/p&gt;</description></item><item><title>Lesson 3: Event-Driven Architecture in Rust — Decoupled systems</title><link>/post/rust/rust-micro-event-driven/</link><pubDate>Thu, 05 Jun 2025 11:02:00 +0000</pubDate><guid>/post/rust/rust-micro-event-driven/</guid><description>&lt;p&gt;The worst production incident I ever dealt with was a cascading failure triggered by a single slow database query. Service A called Service B synchronously, which called Service C, which ran a query that usually took 2ms but on that particular Tuesday took 45 seconds because of a missing index on a new column. Service A&amp;rsquo;s thread pool exhausted, its health check failed, Kubernetes restarted it, and for twenty minutes the entire checkout flow was down — because of a read query in a recommendation engine.&lt;/p&gt;</description></item><item><title>Lesson 10: Distributed System Patterns — Consensus, CRDTs, and consistency</title><link>/post/rust/rust-net-distributed-patterns/</link><pubDate>Wed, 04 Jun 2025 11:08:00 +0000</pubDate><guid>/post/rust/rust-net-distributed-patterns/</guid><description>&lt;p&gt;I once watched a team spend six months building a &amp;ldquo;distributed database&amp;rdquo; that was really just PostgreSQL with a cron job that copied rows between data centers. It worked until it didn&amp;rsquo;t — conflicting writes, lost updates, and an incident where the same order was fulfilled twice from different warehouses. They learned the hard way that distributed systems aren&amp;rsquo;t just &amp;ldquo;run it on multiple machines.&amp;rdquo; They&amp;rsquo;re a fundamentally different programming model with different guarantees, different failure modes, and different mental models.&lt;/p&gt;</description></item><item><title>Lesson 2: gRPC Microservices with tonic — Production-grade RPC</title><link>/post/rust/rust-micro-grpc-services/</link><pubDate>Tue, 03 Jun 2025 14:37:00 +0000</pubDate><guid>/post/rust/rust-micro-grpc-services/</guid><description>&lt;p&gt;The first time I used gRPC in production was on a Go project. The experience was fine — &lt;code&gt;protoc&lt;/code&gt; generated stubs, you implemented an interface, done. Then I tried &lt;code&gt;tonic&lt;/code&gt; in Rust, and I realized what gRPC was &lt;em&gt;supposed&lt;/em&gt; to feel like. Type-safe request/response types generated at compile time, streaming that works with Rust&amp;rsquo;s async model, and interceptors built on the same Tower middleware stack as Axum. It&amp;rsquo;s gRPC done right.&lt;/p&gt;</description></item><item><title>Lesson 9: Message Queues — NATS, Kafka, RabbitMQ from Rust</title><link>/post/rust/rust-net-message-queues/</link><pubDate>Sun, 01 Jun 2025 15:25:00 +0000</pubDate><guid>/post/rust/rust-net-message-queues/</guid><description>&lt;p&gt;The moment I stopped thinking of services as calling each other and started thinking of them as reacting to events, my architecture got dramatically simpler. Instead of service A calling service B calling service C in a synchronous chain that&amp;rsquo;s as fragile as it sounds, service A publishes an event. Services B and C subscribe and react independently. A doesn&amp;rsquo;t even know they exist. B can be down for maintenance without affecting A. C can be added next month without changing A&amp;rsquo;s code. Message queues are the backbone of this pattern.&lt;/p&gt;</description></item><item><title>Lesson 1: Service Boundaries and API Contracts — Where to draw the lines</title><link>/post/rust/rust-micro-service-design/</link><pubDate>Sun, 01 Jun 2025 09:14:00 +0000</pubDate><guid>/post/rust/rust-micro-service-design/</guid><description>&lt;p&gt;I once joined a team that had 47 microservices for what was essentially a CRUD app with a payment flow. Forty-seven. Each one had its own database, its own deployment pipeline, its own on-call rotation. When a customer placed an order, the request bounced through eleven services before a confirmation email went out. Latency was terrible, debugging was a nightmare, and nobody could explain why the &amp;ldquo;UserPreferences&amp;rdquo; service existed separately from the &amp;ldquo;UserProfile&amp;rdquo; service.&lt;/p&gt;</description></item><item><title>Lesson 8: Circuit Breakers in Rust — Failing fast</title><link>/post/rust/rust-net-circuit-breaker/</link><pubDate>Thu, 29 May 2025 07:35:00 +0000</pubDate><guid>/post/rust/rust-net-circuit-breaker/</guid><description>&lt;p&gt;Picture this: your payment service depends on a fraud detection API that&amp;rsquo;s completely down. Every request to it takes 30 seconds to timeout. Your payment service has 200 requests queued up, each holding a thread and a database connection while waiting for fraud detection to respond. Within minutes, you&amp;rsquo;re out of connections, the payment service itself starts failing, and now the checkout service that depends on payments starts failing too. One dead service has cascaded into a full outage.&lt;/p&gt;</description></item><item><title>Lesson 7: Retry Strategies and Exponential Backoff — Resilient clients</title><link>/post/rust/rust-net-retries/</link><pubDate>Mon, 26 May 2025 10:40:00 +0000</pubDate><guid>/post/rust/rust-net-retries/</guid><description>&lt;p&gt;Here&amp;rsquo;s a scenario that&amp;rsquo;s burned me more than once: a downstream service has a brief hiccup — maybe a pod is restarting, maybe there&amp;rsquo;s a momentary network partition — and instead of gracefully retrying, my service immediately returns a 500 to every caller. The hiccup lasts 3 seconds. My P99 latency graph spikes. On-call gets paged. Everyone&amp;rsquo;s unhappy. The fix? A retry loop with exponential backoff. Three lines of logic that would&amp;rsquo;ve made the entire incident invisible.&lt;/p&gt;</description></item><item><title>Lesson 6: TLS — rustls and native TLS</title><link>/post/rust/rust-net-tls/</link><pubDate>Fri, 23 May 2025 19:10:00 +0000</pubDate><guid>/post/rust/rust-net-tls/</guid><description>&lt;p&gt;I&amp;rsquo;ll never forget the 3am page that turned out to be an expired TLS certificate. Our automated renewal had been silently failing for two weeks, nobody noticed because the cert was still valid, and then at 2:47am on a Sunday it expired and every client started getting connection errors. We had monitoring for CPU, memory, disk, latency, error rates — but not for certificate expiry. That was the day I decided to actually understand TLS instead of just copy-pasting cert paths into config files.&lt;/p&gt;</description></item><item><title>Lesson 5: DNS Resolution and Custom Resolvers — Understanding name resolution</title><link>/post/rust/rust-net-dns/</link><pubDate>Wed, 21 May 2025 13:55:00 +0000</pubDate><guid>/post/rust/rust-net-dns/</guid><description>&lt;p&gt;A few months back, our entire staging environment went down for an hour. Not because any service crashed — because someone changed a DNS record and forgot that our Kubernetes ingress had a 5-minute TTL cache while the CDN had a 24-hour cache. Half our traffic was going to the old IP, half to the new one. Debugging it took forever because &lt;code&gt;dig&lt;/code&gt; on my laptop showed the correct answer, but the services inside the cluster were seeing stale records.&lt;/p&gt;</description></item><item><title>Lesson 8: Security Fuzzing — Finding vulnerabilities before attackers do</title><link>/post/rust/rust-sec-fuzzing-security/</link><pubDate>Sun, 18 May 2025 13:29:00 +0000</pubDate><guid>/post/rust/rust-sec-fuzzing-security/</guid><description>&lt;p&gt;I found a panic in a production parser by accident last year. A user in Japan sent a request with a multi-byte UTF-8 character right at a boundary where our code was slicing a string by byte index. It worked fine for ASCII. It worked fine for most Unicode. But this particular combination of character and position triggered an index-out-of-bounds panic that crashed the request handler.&lt;/p&gt;
&lt;p&gt;I fixed the bug in ten minutes. What bothered me was that we&amp;rsquo;d had unit tests, integration tests, and even some property-based tests — and none of them caught it. The input space was too large. The edge case was too specific. A human writing test cases would never think to put a 3-byte UTF-8 character at exactly that offset.&lt;/p&gt;</description></item><item><title>Lesson 4: WebSocket Servers and Clients — Real-time communication</title><link>/post/rust/rust-net-websockets/</link><pubDate>Sun, 18 May 2025 08:20:00 +0000</pubDate><guid>/post/rust/rust-net-websockets/</guid><description>&lt;p&gt;I built my first WebSocket server to power a live dashboard that showed deployment status across our fleet. The alternative was polling every 2 seconds — 500 browser tabs hitting the API, each getting back the same &amp;ldquo;nothing changed&amp;rdquo; response 99% of the time. WebSockets turned that from 250 requests/second of wasted work into a handful of persistent connections that only sent data when something actually happened.&lt;/p&gt;
&lt;h2 id="http-vs-websockets--when-do-you-need-them"&gt;HTTP vs WebSockets — When Do You Need Them?&lt;/h2&gt;
&lt;p&gt;HTTP is request-response. Client asks, server answers. Great for most things. But some use cases fundamentally don&amp;rsquo;t fit that model:&lt;/p&gt;</description></item><item><title>Lesson 3: gRPC with tonic — High-performance RPC</title><link>/post/rust/rust-net-grpc/</link><pubDate>Fri, 16 May 2025 11:30:00 +0000</pubDate><guid>/post/rust/rust-net-grpc/</guid><description>&lt;p&gt;The first time I used gRPC in production, I was skeptical. We already had REST APIs that worked fine — why add protobuf compilation, code generation, and an entirely new protocol? Then our team grew to four services in three languages, and the answer became painfully obvious. Every REST endpoint had slightly different JSON field naming, different error formats, and documentation that was always a version behind. gRPC eliminated all of that overnight.&lt;/p&gt;</description></item><item><title>Lesson 7: Sandboxing and Privilege Dropping — Least privilege</title><link>/post/rust/rust-sec-sandboxing/</link><pubDate>Thu, 15 May 2025 07:41:00 +0000</pubDate><guid>/post/rust/rust-sec-sandboxing/</guid><description>&lt;p&gt;Here&amp;rsquo;s a pattern I&amp;rsquo;ve seen too many times: a Rust web service runs as root in a Docker container because &amp;ldquo;it needs to bind port 443.&amp;rdquo; The service handles user uploads, parses JSON, processes images, and talks to a database — all with root privileges. If any part of that pipeline has a vulnerability, the attacker gets root on the container. And if the container isn&amp;rsquo;t properly isolated, they might get the host too.&lt;/p&gt;</description></item><item><title>Lesson 2: HTTP Clients — reqwest and hyper</title><link>/post/rust/rust-net-http-client/</link><pubDate>Wed, 14 May 2025 16:45:00 +0000</pubDate><guid>/post/rust/rust-net-http-client/</guid><description>&lt;p&gt;I once spent three hours debugging a production issue that turned out to be an HTTP client with no timeout configured. Three hours. The client was happily waiting forever for a response from a service that had crashed, holding a database connection open the entire time. That experience permanently changed how I think about HTTP clients — they&amp;rsquo;re not just &amp;ldquo;make a request, get a response.&amp;rdquo; They&amp;rsquo;re complex state machines with connection pools, redirect policies, timeout hierarchies, and a dozen other knobs that matter when things go wrong.&lt;/p&gt;</description></item><item><title>Lesson 6: Supply Chain Security — Lockfiles, vendoring, and trust</title><link>/post/rust/rust-sec-supply-chain/</link><pubDate>Mon, 12 May 2025 10:08:00 +0000</pubDate><guid>/post/rust/rust-sec-supply-chain/</guid><description>&lt;p&gt;The xz backdoor was a wake-up call for the entire industry, but honestly, supply chain attacks had been happening for years before that — just more quietly. Typosquatting on npm, malicious PyPI packages, compromised maintainer accounts. The question isn&amp;rsquo;t whether Rust&amp;rsquo;s ecosystem is vulnerable to supply chain attacks. It is. The question is what you&amp;rsquo;re doing about it.&lt;/p&gt;
&lt;p&gt;I spent a week last year hardening our build pipeline after we realized that a &lt;code&gt;cargo build&lt;/code&gt; on our CI server was pulling fresh crate downloads from the internet with no verification beyond what Cargo does by default. If crates.io got compromised, or if our DNS got hijacked, we&amp;rsquo;d be compiling and shipping attacker code with zero friction.&lt;/p&gt;</description></item><item><title>Lesson 1: Building a TCP Server from Scratch — Raw sockets</title><link>/post/rust/rust-net-tcp-server/</link><pubDate>Mon, 12 May 2025 09:14:00 +0000</pubDate><guid>/post/rust/rust-net-tcp-server/</guid><description>&lt;p&gt;Last month I was debugging a flaky microservice at work and realized I couldn&amp;rsquo;t explain what was actually happening between &lt;code&gt;bind()&lt;/code&gt; and the first byte arriving. I&amp;rsquo;d been using high-level frameworks for years — Actix, Axum, you name it — but I&amp;rsquo;d never actually built a TCP server from raw sockets in Rust. That bothered me. So I spent a weekend doing exactly that, and honestly, it changed how I think about every networked service I write.&lt;/p&gt;</description></item><item><title>Lesson 5: Dependency Auditing — cargo-audit and cargo-deny</title><link>/post/rust/rust-sec-dependency-audit/</link><pubDate>Fri, 09 May 2025 14:55:00 +0000</pubDate><guid>/post/rust/rust-sec-dependency-audit/</guid><description>&lt;p&gt;You know what keeps me up at night? Not my own code — I can review that. It&amp;rsquo;s the 200+ transitive dependencies in my &lt;code&gt;Cargo.lock&lt;/code&gt; that I&amp;rsquo;ve never read a single line of. Every one of those crates runs with the same permissions as my code. If any of them has a vulnerability, it&amp;rsquo;s my vulnerability.&lt;/p&gt;
&lt;p&gt;This isn&amp;rsquo;t theoretical. In 2024, the &lt;code&gt;xz&lt;/code&gt; backdoor showed that even core infrastructure maintained by a single person can be compromised. Rust&amp;rsquo;s ecosystem isn&amp;rsquo;t immune. We&amp;rsquo;ve had actual advisories for real crates — buffer overflows in parsing libraries, unsound &lt;code&gt;unsafe&lt;/code&gt; code in popular crates, logic bugs in crypto implementations.&lt;/p&gt;</description></item><item><title>Lesson 8: Release Profiles and Build Optimization — Shipping fast binaries</title><link>/post/rust/rust-deploy-release-profiles/</link><pubDate>Thu, 08 May 2025 13:50:00 +0000</pubDate><guid>/post/rust/rust-deploy-release-profiles/</guid><description>&lt;p&gt;I was benchmarking two builds of the same service — one with default release settings, one with a tuned profile. Same code. Same hardware. The tuned build was 22% faster on our hot path and 40% smaller. I didn&amp;rsquo;t change a single line of Rust. Just &lt;code&gt;Cargo.toml&lt;/code&gt; settings.&lt;/p&gt;
&lt;p&gt;Most Rust developers know about &lt;code&gt;cargo build --release&lt;/code&gt;. Fewer know that &lt;code&gt;--release&lt;/code&gt; is just a starting point — there&amp;rsquo;s a whole set of knobs in the release profile that trade compile time for runtime performance, or binary size, or debuggability. Let me walk you through every one that matters.&lt;/p&gt;</description></item><item><title>Lesson 4: Secret Management — zeroize and secure memory</title><link>/post/rust/rust-sec-secrets/</link><pubDate>Wed, 07 May 2025 08:23:00 +0000</pubDate><guid>/post/rust/rust-sec-secrets/</guid><description>&lt;p&gt;A while back I was debugging a crash in production and pulled a core dump from the server. Sitting right there in the heap, in plain text, was a database connection string with credentials. The service had loaded the secret from Vault on startup, stored it in a regular &lt;code&gt;String&lt;/code&gt;, and that &lt;code&gt;String&lt;/code&gt; stayed in memory for the entire process lifetime. When it crashed, the secret got written to disk in the core dump.&lt;/p&gt;</description></item><item><title>Lesson 3: Cryptography — ring, RustCrypto, and sodiumoxide</title><link>/post/rust/rust-sec-crypto/</link><pubDate>Mon, 05 May 2025 11:47:00 +0000</pubDate><guid>/post/rust/rust-sec-crypto/</guid><description>&lt;p&gt;I&amp;rsquo;m going to say something controversial: most developers should never write cryptographic code. Not because they&amp;rsquo;re not smart enough — because the field is absurdly hostile to even tiny mistakes. A single branch in your constant-time comparison function leaks timing information. A reused nonce in AES-GCM completely destroys confidentiality. An ECDSA implementation with a biased random number generator leaks your private key after enough signatures.&lt;/p&gt;
&lt;p&gt;But you still need to &lt;em&gt;use&lt;/em&gt; cryptography. Every production system needs hashing, encryption, signatures, or key derivation at some point. The trick is picking the right library, using it correctly, and understanding just enough of the theory to avoid the common footguns.&lt;/p&gt;</description></item><item><title>Lesson 7: Configuration — Environment, files, feature flags</title><link>/post/rust/rust-deploy-config/</link><pubDate>Mon, 05 May 2025 10:15:00 +0000</pubDate><guid>/post/rust/rust-deploy-config/</guid><description>&lt;p&gt;I once shipped a service to production with the staging database URL hardcoded. Not in an environment variable — literally in the source code, in a &lt;code&gt;const&lt;/code&gt;. It ran for two hours writing production data to the staging database before anyone noticed. The fix was easy. The data migration to clean up the mess took three days.&lt;/p&gt;
&lt;p&gt;Configuration is one of those things that seems trivial until it bites you. And in Rust, we have the type system to make configuration bulletproof — but only if we structure things right. Let me walk you through the approach I&amp;rsquo;ve converged on after making every possible configuration mistake.&lt;/p&gt;</description></item><item><title>Lesson 2: Input Validation and Sanitization — Trust nothing</title><link>/post/rust/rust-sec-input-validation/</link><pubDate>Sat, 03 May 2025 16:12:00 +0000</pubDate><guid>/post/rust/rust-sec-input-validation/</guid><description>&lt;p&gt;A few months ago, I was reviewing a PR where someone had written a REST API handler that took a user-supplied filename, appended it to a base path, and opened the file. The code compiled perfectly. Clippy was happy. Tests passed. And it was a textbook path traversal vulnerability — &lt;code&gt;../../etc/passwd&lt;/code&gt; would work just fine.&lt;/p&gt;
&lt;p&gt;Rust&amp;rsquo;s type system protects you from memory corruption. It doesn&amp;rsquo;t protect you from trusting user input. That&amp;rsquo;s still on you. And honestly? It&amp;rsquo;s where most production vulnerabilities in Rust code are going to come from.&lt;/p&gt;</description></item><item><title>Lesson 6: Graceful Shutdown — Draining connections cleanly</title><link>/post/rust/rust-deploy-graceful-shutdown/</link><pubDate>Fri, 02 May 2025 19:45:00 +0000</pubDate><guid>/post/rust/rust-deploy-graceful-shutdown/</guid><description>&lt;p&gt;I deployed a new version of a payment service once, and for about 3 seconds during the rollout, a handful of transactions just&amp;hellip; vanished. They weren&amp;rsquo;t in the database. They weren&amp;rsquo;t in the error logs. The old pods received the requests, started processing them, and then Kubernetes killed the pods before they finished. SIGKILL doesn&amp;rsquo;t ask nicely — it just terminates the process. Those transactions were gone.&lt;/p&gt;
&lt;p&gt;Graceful shutdown is the solution. When your service receives SIGTERM (Kubernetes&amp;rsquo;s way of saying &amp;ldquo;please stop&amp;rdquo;), it should stop accepting new requests, finish processing in-flight requests, flush any buffered data, close connections cleanly, and &lt;em&gt;then&lt;/em&gt; exit. Get this right and you get zero-downtime deployments. Get it wrong and you get data loss.&lt;/p&gt;</description></item><item><title>Lesson 1: Memory Safety — What Rust gives you for free</title><link>/post/rust/rust-sec-memory-safety/</link><pubDate>Thu, 01 May 2025 09:34:00 +0000</pubDate><guid>/post/rust/rust-sec-memory-safety/</guid><description>&lt;p&gt;Last year I inherited a C++ service that had been &amp;ldquo;battle-tested&amp;rdquo; in production for three years. Within a week of digging through crash dumps, I found two use-after-free bugs, a buffer overread that leaked heap data into API responses, and a data race in the connection pool that only triggered under load. Three years. Battle-tested. Right.&lt;/p&gt;
&lt;p&gt;That experience is what finally pushed me from &amp;ldquo;Rust is interesting&amp;rdquo; to &amp;ldquo;Rust is non-negotiable for anything touching the network.&amp;rdquo; The memory safety guarantees aren&amp;rsquo;t just a nice-to-have — they&amp;rsquo;re the single biggest security win you get by choosing Rust.&lt;/p&gt;</description></item><item><title>Lesson 5: Health Checks and Readiness Probes — Production liveness</title><link>/post/rust/rust-deploy-health-checks/</link><pubDate>Wed, 30 Apr 2025 14:05:00 +0000</pubDate><guid>/post/rust/rust-deploy-health-checks/</guid><description>&lt;p&gt;A service I maintained once passed all its health checks while silently dropping 30% of incoming requests. The health endpoint returned 200 OK every time Kubernetes asked. The database connection pool was exhausted, the service couldn&amp;rsquo;t process anything, but that little &lt;code&gt;/health&lt;/code&gt; endpoint — which didn&amp;rsquo;t touch the database — happily reported everything was fine.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s when I learned the difference between a health check that checks health and one that just says &amp;ldquo;the process is running.&amp;rdquo; They&amp;rsquo;re very different things, and getting this wrong means your orchestrator keeps sending traffic to a broken instance instead of replacing it.&lt;/p&gt;</description></item><item><title>Lesson 4: Observability — tracing, metrics, OpenTelemetry</title><link>/post/rust/rust-deploy-observability/</link><pubDate>Sun, 27 Apr 2025 08:22:00 +0000</pubDate><guid>/post/rust/rust-deploy-observability/</guid><description>&lt;p&gt;I once spent six hours debugging a production issue where requests were randomly timing out. No errors in the logs. CPU and memory looked fine. Response times were normal — except for the 2% that took 30 seconds. Without distributed tracing, I was blind. I ended up bisecting the problem by adding &lt;code&gt;println!&lt;/code&gt; statements, deploying them one at a time, and watching CloudWatch. It was miserable.&lt;/p&gt;
&lt;p&gt;That experience permanently changed how I build services. Observability isn&amp;rsquo;t something you bolt on when things break. It&amp;rsquo;s something you build in from day one, or you pay for it later — with your time, your sleep, and your sanity.&lt;/p&gt;</description></item><item><title>Lesson 3: CI/CD for Rust — GitHub Actions, caching, cargo-nextest</title><link>/post/rust/rust-deploy-ci/</link><pubDate>Thu, 24 Apr 2025 11:30:00 +0000</pubDate><guid>/post/rust/rust-deploy-ci/</guid><description>&lt;p&gt;My first Rust CI pipeline took 45 minutes. Forty-five. Every push triggered a full dependency build, tests ran sequentially, and clippy ran as a separate job that also built everything from scratch. I was burning through GitHub Actions minutes like they were free — which they are for open source, but my patience certainly wasn&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;Getting Rust CI right is mostly about caching. The compilation model means a clean build downloads and compiles hundreds of crates, and without caching, every single CI run pays that cost. Let me show you the pipeline I&amp;rsquo;ve landed on after iterating through dozens of projects.&lt;/p&gt;</description></item><item><title>Lesson 10: Using unsafe to Escape the Borrow Checker — The wrong reason</title><link>/post/rust/rust-anti-unsafe-escape-hatch/</link><pubDate>Thu, 24 Apr 2025 09:30:00 +0000</pubDate><guid>/post/rust/rust-anti-unsafe-escape-hatch/</guid><description>&lt;p&gt;I found this in a production codebase:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_or_insert&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;mut&lt;/span&gt; self, key: &lt;span style="color:#66d9ef"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;str&lt;/span&gt;) -&amp;gt; &lt;span style="color:#66d9ef"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;mut&lt;/span&gt; Value {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;!&lt;/span&gt;self.map.contains_key(key) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self.map.insert(key.to_string(), Value::default());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// &amp;#34;The borrow checker is being stupid, we know the key exists&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;unsafe&lt;/span&gt; { &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;mut&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;(self.map.get_mut(key).unwrap() &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;mut&lt;/span&gt; Value) }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The comment says it all. The developer hit a borrow checker error — a legitimate one about borrowing &lt;code&gt;self.map&lt;/code&gt; mutably twice — and instead of restructuring the code, they cast through a raw pointer to silence the compiler. This is undefined behavior. The compiler is allowed to assume the mutable references don&amp;rsquo;t alias, and it &lt;em&gt;will&lt;/em&gt; optimize based on that assumption. When it does, your program does something you didn&amp;rsquo;t write.&lt;/p&gt;</description></item><item><title>Lesson 2: Static Linking with musl — Single binary deploys</title><link>/post/rust/rust-deploy-static-linking/</link><pubDate>Tue, 22 Apr 2025 16:40:00 +0000</pubDate><guid>/post/rust/rust-deploy-static-linking/</guid><description>&lt;p&gt;Last year I had to deploy a Rust service to a hardened environment — no package manager, no shared libraries, no internet access. Just a bare Linux kernel and my binary. If my binary depended on libc, libssl, or anything else in &lt;code&gt;/usr/lib&lt;/code&gt;, it simply wouldn&amp;rsquo;t start. That constraint forced me to learn static linking properly, and it turned out to be one of the best deployment patterns I&amp;rsquo;ve ever used.&lt;/p&gt;</description></item><item><title>Lesson 9: Macro Abuse — When a function would do</title><link>/post/rust/rust-anti-macro-abuse/</link><pubDate>Mon, 21 Apr 2025 17:08:00 +0000</pubDate><guid>/post/rust/rust-anti-macro-abuse/</guid><description>&lt;p&gt;I spent an entire afternoon debugging a test failure that turned out to be caused by a macro expanding variable names in a way I didn&amp;rsquo;t expect. The macro was called &lt;code&gt;make_handler!&lt;/code&gt; and it generated HTTP handler functions from a declarative DSL that someone on the team had invented. The &amp;ldquo;DSL&amp;rdquo; saved maybe ten lines of boilerplate per handler. The macro definition was 200 lines of nested &lt;code&gt;macro_rules!&lt;/code&gt; with five recursion levels, three &lt;code&gt;tt&lt;/code&gt; munchers, and hygiene workarounds that I&amp;rsquo;m still not convinced were correct. When a new developer asked how to add a query parameter to a handler, nobody could explain it without first teaching them how the macro worked.&lt;/p&gt;</description></item><item><title>Lesson 1: Docker for Rust — Multi-stage builds, minimal images</title><link>/post/rust/rust-deploy-docker/</link><pubDate>Sun, 20 Apr 2025 09:15:00 +0000</pubDate><guid>/post/rust/rust-deploy-docker/</guid><description>&lt;p&gt;The first time I shipped a Rust service in Docker, my image was 2.1 GB. Two. Point. One. Gigabytes. For a binary that was 8 MB. I&amp;rsquo;d used &lt;code&gt;rust:latest&lt;/code&gt; as my base, ran &lt;code&gt;cargo build --release&lt;/code&gt; inside it, and called it a day. The image had GCC, LLVM, every system library known to humanity, and my tiny HTTP server somewhere in the corner.&lt;/p&gt;
&lt;p&gt;That was the day I learned about multi-stage builds. And honestly, getting Docker right for Rust is one of those things that separates &amp;ldquo;I deployed it&amp;rdquo; from &amp;ldquo;I deployed it well.&amp;rdquo;&lt;/p&gt;</description></item><item><title>Lesson 8: Premature Optimization — Profile before you optimize</title><link>/post/rust/rust-anti-premature-optimization/</link><pubDate>Sat, 19 Apr 2025 13:42:00 +0000</pubDate><guid>/post/rust/rust-anti-premature-optimization/</guid><description>&lt;p&gt;A teammate once spent three days replacing every &lt;code&gt;String&lt;/code&gt; in our data model with a custom arena-allocated string type. The rationale: &amp;ldquo;String allocations are slow, and we&amp;rsquo;re processing a lot of data.&amp;rdquo; Sounds reasonable, right? After the rewrite, I ran the benchmarks. The improvement was within noise — less than 1%. The actual bottleneck was network I/O to an external API, which accounted for 94% of the request latency. Those three days of intricate unsafe string manipulation? Completely wasted. And the code was now harder to read, harder to maintain, and had a subtle use-after-free bug that we found six weeks later.&lt;/p&gt;</description></item><item><title>Lesson 7: Arc&lt;Mutex&lt;T&gt;&gt; as Default — Reach for channels first</title><link>/post/rust/rust-anti-arc-mutex-default/</link><pubDate>Wed, 16 Apr 2025 20:15:00 +0000</pubDate><guid>/post/rust/rust-anti-arc-mutex-default/</guid><description>&lt;p&gt;There&amp;rsquo;s a specific moment in every Rust developer&amp;rsquo;s journey where they discover &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt; and start putting everything in it. I&amp;rsquo;ve been that developer. I had a web service that needed to share a cache between request handlers, and my first instinct was &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;HashMap&amp;lt;String, CachedItem&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt;. It worked. Then traffic went up, contention went up, tail latency went up, and I spent a weekend profiling lock contention that shouldn&amp;rsquo;t have existed in the first place — because most of my &amp;ldquo;shared mutable state&amp;rdquo; could have been restructured as message passing.&lt;/p&gt;</description></item><item><title>Lesson 6: Trait Bloat — Interface segregation in Rust</title><link>/post/rust/rust-anti-trait-bloat/</link><pubDate>Tue, 15 Apr 2025 09:55:00 +0000</pubDate><guid>/post/rust/rust-anti-trait-bloat/</guid><description>&lt;p&gt;I worked on a project that had a &lt;code&gt;Storage&lt;/code&gt; trait with twenty-three methods. Twenty-three. It handled reading, writing, deleting, listing, searching, watching for changes, managing permissions, computing checksums, and streaming large files. Every backend — S3, local filesystem, in-memory for tests — had to implement all twenty-three methods. The in-memory test backend had fourteen methods that just returned &lt;code&gt;unimplemented!()&lt;/code&gt;. The local filesystem backend panicked on the permissions methods because POSIX permissions don&amp;rsquo;t map to the trait&amp;rsquo;s model. And every time someone added a new method to the trait, every backend had to be updated — even the ones where the new method made no sense.&lt;/p&gt;</description></item><item><title>Lesson 5: Over-Genericizing — Not everything needs &lt;T&gt;</title><link>/post/rust/rust-anti-over-generic/</link><pubDate>Sun, 13 Apr 2025 11:28:00 +0000</pubDate><guid>/post/rust/rust-anti-over-generic/</guid><description>&lt;p&gt;I reviewed a library last year where the author had made literally everything generic. The HTTP client was generic over the transport, the serializer, the deserializer, the error type, the retry policy, the timeout strategy, and the logger. Using it required spelling out a type signature that looked like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; client: &lt;span style="color:#a6e22e"&gt;HttpClient&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; TcpTransport&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;TlsConfig&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; JsonSerializer&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;PrettyPrint&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; JsonDeserializer&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;StrictMode&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; AppError,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ExponentialBackoff&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;SystemClock&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; FixedTimeout,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; SlogLogger&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;JsonFormat&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; HttpClient::new(&lt;span style="color:#75715e"&gt;/* ... */&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The kicker? The library was an internal tool used by exactly one team. There was one transport, one serializer, one error type. Every type parameter had exactly one implementation. The author had written an extensible framework for a problem that didn&amp;rsquo;t need extending.&lt;/p&gt;</description></item><item><title>Lesson 12: Zero-Copy Parsing — bytes, nom, winnow</title><link>/post/rust/rust-perf-zero-copy/</link><pubDate>Sat, 12 Apr 2025 11:30:00 +0000</pubDate><guid>/post/rust/rust-perf-zero-copy/</guid><description>&lt;p&gt;I once had to parse 2GB of log files per hour on a machine with 4GB of RAM. The naive approach — read line, split into fields, store as &lt;code&gt;String&lt;/code&gt; — peaked at 6GB memory usage and fell over. The data was being duplicated everywhere: once in the read buffer, once in each split &lt;code&gt;String&lt;/code&gt;, once in the output struct. Three copies of every byte.&lt;/p&gt;
&lt;p&gt;The zero-copy version peaked at 2.1GB — basically the file size plus a thin layer of parsed references. Same output, same correctness, one-third the memory, twice the throughput. Zero-copy parsing is one of the most powerful techniques in Rust&amp;rsquo;s performance toolkit, and the ownership system makes it uniquely natural here.&lt;/p&gt;</description></item><item><title>Lesson 4: God Structs — When types do too much</title><link>/post/rust/rust-anti-god-struct/</link><pubDate>Fri, 11 Apr 2025 16:10:00 +0000</pubDate><guid>/post/rust/rust-anti-god-struct/</guid><description>&lt;p&gt;I once opened a file called &lt;code&gt;app.rs&lt;/code&gt; and found a struct with forty-two fields. Forty-two. It held the database connection, the HTTP client, the cache handle, the logger, the config, the metrics collector, the rate limiter, the auth provider, the feature flags, the email sender, the template engine, and about thirty other things I&amp;rsquo;ve blocked from memory. Every function in the codebase took &lt;code&gt;&amp;amp;self&lt;/code&gt; on this monster. Need to send an email? You need the god struct. Parse a config value? God struct. Log a message? Believe it or not, god struct.&lt;/p&gt;</description></item><item><title>Lesson 3: Stringly Typed APIs — Use enums, not strings</title><link>/post/rust/rust-anti-stringly-typed/</link><pubDate>Wed, 09 Apr 2025 08:47:00 +0000</pubDate><guid>/post/rust/rust-anti-stringly-typed/</guid><description>&lt;p&gt;I inherited a Rust codebase once where the entire state machine was driven by string comparisons. The order status could be &lt;code&gt;&amp;quot;pending&amp;quot;&lt;/code&gt;, &lt;code&gt;&amp;quot;processing&amp;quot;&lt;/code&gt;, &lt;code&gt;&amp;quot;shipped&amp;quot;&lt;/code&gt;, &lt;code&gt;&amp;quot;delivered&amp;quot;&lt;/code&gt;, or &lt;code&gt;&amp;quot;cancelled&amp;quot;&lt;/code&gt;. Except sometimes it was &lt;code&gt;&amp;quot;Pending&amp;quot;&lt;/code&gt; with a capital P. And there was one code path that set it to &lt;code&gt;&amp;quot;canceled&amp;quot;&lt;/code&gt; — one L, American spelling. And another that used &lt;code&gt;&amp;quot;CANCELLED&amp;quot;&lt;/code&gt;. The bug lived in production for weeks because nobody could figure out why some orders were getting stuck in a phantom state that didn&amp;rsquo;t match any of the &lt;code&gt;if status == &amp;quot;cancelled&amp;quot;&lt;/code&gt; checks scattered across thirty files.&lt;/p&gt;</description></item><item><title>Lesson 2: unwrap() in Production — Time bombs waiting to explode</title><link>/post/rust/rust-anti-unwrap-abuse/</link><pubDate>Mon, 07 Apr 2025 14:35:00 +0000</pubDate><guid>/post/rust/rust-anti-unwrap-abuse/</guid><description>&lt;p&gt;A service I was responsible for went down at 2 AM on a Saturday because of a single &lt;code&gt;.unwrap()&lt;/code&gt; call on line 847 of a file nobody had touched in months. The function parsed a config value that was supposed to always be present. Somebody changed the config format in a different repo, the value became optional, and that &lt;code&gt;.unwrap()&lt;/code&gt; detonated like a landmine — &lt;code&gt;thread 'main' panicked at 'called Option::unwrap() on a None value'&lt;/code&gt;. Down. Dead. Pager screaming.&lt;/p&gt;</description></item><item><title>Lesson 11: Binary Size Reduction — Smaller deployments</title><link>/post/rust/rust-perf-binary-size/</link><pubDate>Mon, 07 Apr 2025 13:17:00 +0000</pubDate><guid>/post/rust/rust-perf-binary-size/</guid><description>&lt;p&gt;I shipped a &amp;ldquo;hello world&amp;rdquo; Rust binary to a team once and they came back confused: &amp;ldquo;Why is this 4 megabytes?&amp;rdquo; Fair question. A C hello-world is 16KB. A Go hello-world is about 2MB. A default Rust hello-world with standard linking is 3-4MB.&lt;/p&gt;
&lt;p&gt;That 4MB isn&amp;rsquo;t wasted — it&amp;rsquo;s the Rust standard library, panic handling, formatting machinery, and debug symbols. But when you&amp;rsquo;re building container images, deploying to embedded devices, or targeting WebAssembly, every megabyte counts. Here&amp;rsquo;s how to cut Rust binaries down to size.&lt;/p&gt;</description></item><item><title>Lesson 1: .clone() Everywhere — Hiding ownership problems</title><link>/post/rust/rust-anti-clone-everywhere/</link><pubDate>Sat, 05 Apr 2025 10:22:00 +0000</pubDate><guid>/post/rust/rust-anti-clone-everywhere/</guid><description>&lt;p&gt;I was reviewing a pull request last year from a developer who&amp;rsquo;d been writing Rust for about three months. The code compiled. The tests passed. Everything looked fine — until I ran &lt;code&gt;grep -c '\.clone()' src/&lt;/code&gt; and got back a number that made me physically uncomfortable. Forty-seven clones across six files. The codebase was a data pipeline processing millions of events per hour, and this person had turned every ownership error into a &lt;code&gt;.clone()&lt;/code&gt; call until the compiler stopped yelling.&lt;/p&gt;</description></item><item><title>Lesson 10: Compile Time Optimization — Strategies that actually work</title><link>/post/rust/rust-perf-compile-times/</link><pubDate>Thu, 03 Apr 2025 08:55:00 +0000</pubDate><guid>/post/rust/rust-perf-compile-times/</guid><description>&lt;p&gt;My main Rust project at work took 4 minutes and 38 seconds for a clean build. That was two years ago. Today it takes 52 seconds. Same codebase — more code, actually. Same hardware. The difference is about a dozen targeted changes, none of which involved rewriting application code.&lt;/p&gt;
&lt;p&gt;Rust&amp;rsquo;s compile times are its most legitimate criticism. But &amp;ldquo;Rust is slow to compile&amp;rdquo; is the starting point, not the conclusion. Most projects can cut their build times by 50-80% with the right techniques. Let me show you what actually moves the needle.&lt;/p&gt;</description></item><item><title>Lesson 9: Inlining — #[inline] and LTO</title><link>/post/rust/rust-perf-inlining/</link><pubDate>Mon, 31 Mar 2025 19:05:00 +0000</pubDate><guid>/post/rust/rust-perf-inlining/</guid><description>&lt;p&gt;A few years ago I was profiling a JSON parser and noticed something weird. A tiny function — four lines, no allocations — was showing up as a 15% hot spot. Not because it was slow, but because it was called 8 million times per second and the function call overhead (push registers, set up stack frame, call, pop registers, return) was eating 2 nanoseconds each call. That&amp;rsquo;s 16 milliseconds per second just in function prologues and epilogues.&lt;/p&gt;</description></item><item><title>Lesson 8: Cache-Friendly Data Structures — Data-oriented design</title><link>/post/rust/rust-perf-cache-friendly/</link><pubDate>Sat, 29 Mar 2025 07:40:00 +0000</pubDate><guid>/post/rust/rust-perf-cache-friendly/</guid><description>&lt;p&gt;Here&amp;rsquo;s a number that should change how you think about data structures: reading from L1 cache takes about 1 nanosecond. Reading from main memory takes about 100 nanoseconds. That&amp;rsquo;s a 100x penalty for a cache miss. On a modern CPU running at 4 GHz, a single cache miss stalls the processor for roughly 400 cycles. Four hundred cycles where your CPU is sitting there, doing nothing, waiting for data to arrive from RAM.&lt;/p&gt;</description></item><item><title>Lesson 7: Choosing the Right Collection — It's not always Vec</title><link>/post/rust/rust-perf-collections/</link><pubDate>Thu, 27 Mar 2025 15:22:00 +0000</pubDate><guid>/post/rust/rust-perf-collections/</guid><description>&lt;p&gt;A friend asked me to review their service that was doing &amp;ldquo;thousands of lookups per second&amp;rdquo; against a &lt;code&gt;Vec&lt;/code&gt; of about 50,000 entries. Linear scan every time. They&amp;rsquo;d chosen &lt;code&gt;Vec&lt;/code&gt; because &amp;ldquo;it&amp;rsquo;s the default&amp;rdquo; and hadn&amp;rsquo;t thought about it further. Swapping to a &lt;code&gt;HashMap&lt;/code&gt; took the lookup from 12µs to 40ns. Three hundred times faster. From a one-line change.&lt;/p&gt;
&lt;p&gt;Choosing the right collection is one of the highest-leverage performance decisions you can make, and it requires basically zero cleverness. Just know your access patterns.&lt;/p&gt;</description></item><item><title>Lesson 6: String Performance — SmartString, CompactStr, and when to care</title><link>/post/rust/rust-perf-string-perf/</link><pubDate>Tue, 25 Mar 2025 11:50:00 +0000</pubDate><guid>/post/rust/rust-perf-string-perf/</guid><description>&lt;p&gt;I was building an in-memory index that stored about 2 million tag strings. Most were short — &amp;ldquo;rust&amp;rdquo;, &amp;ldquo;go&amp;rdquo;, &amp;ldquo;api&amp;rdquo;, &amp;ldquo;v2&amp;rdquo; — averaging 6 bytes. But each &lt;code&gt;String&lt;/code&gt; carries 24 bytes of overhead (pointer + length + capacity) plus the heap allocation for the actual data. That&amp;rsquo;s 24 bytes of bookkeeping to store 6 bytes of useful information. Plus 2 million separate allocations hammering the allocator.&lt;/p&gt;
&lt;p&gt;Switching to &lt;code&gt;CompactStr&lt;/code&gt; cut memory usage by 60% and index-building time by 40%. Strings matter more than you think.&lt;/p&gt;</description></item><item><title>Lesson 5: Iterators vs Loops — Performance characteristics</title><link>/post/rust/rust-perf-iterators-vs-loops/</link><pubDate>Sun, 23 Mar 2025 09:12:00 +0000</pubDate><guid>/post/rust/rust-perf-iterators-vs-loops/</guid><description>&lt;p&gt;When I first started writing Rust, I wrote everything as &lt;code&gt;for&lt;/code&gt; loops. Old habits from C. Then someone on my team rewrote one of my loops as an iterator chain and I got annoyed — it looked &amp;ldquo;slower&amp;rdquo; to me. More function calls, closures, chaining. Obviously that&amp;rsquo;s more overhead, right?&lt;/p&gt;
&lt;p&gt;I benchmarked it. Same performance. Down to the nanosecond. I looked at the assembly. Identical. That was the day I stopped assuming and started measuring.&lt;/p&gt;</description></item><item><title>Lesson 10: How rustc Works — From source code to binary</title><link>/post/rust/rust-internals-compiler-pipeline/</link><pubDate>Sat, 22 Mar 2025 11:30:00 +0000</pubDate><guid>/post/rust/rust-internals-compiler-pipeline/</guid><description>&lt;p&gt;A junior on my team once asked why Rust compiles so slowly compared to Go. I gave the standard answer about monomorphization and LLVM, but realized I couldn&amp;rsquo;t actually explain the full pipeline. So I spent a weekend reading the rustc dev guide and poking at compiler internals. What I found was a surprisingly elegant six-stage pipeline, and understanding it changed how I think about Rust&amp;rsquo;s design tradeoffs.&lt;/p&gt;
&lt;h2 id="the-big-picture"&gt;The Big Picture&lt;/h2&gt;
&lt;p&gt;When you run &lt;code&gt;cargo build&lt;/code&gt;, your source code goes through six major stages before becoming a binary:&lt;/p&gt;</description></item><item><title>Lesson 4: Reducing Allocations — Stack, arena, SmallVec</title><link>/post/rust/rust-perf-allocations/</link><pubDate>Fri, 21 Mar 2025 16:30:00 +0000</pubDate><guid>/post/rust/rust-perf-allocations/</guid><description>&lt;p&gt;I profiled a Rust web service once and found it was allocating 47,000 times per request. Forty-seven thousand. Most were tiny — 16-byte strings, 3-element vectors, temporary buffers. Each individual allocation was fast (jemalloc is good), but 47,000 of them at ~30ns each is 1.4ms of pure allocator overhead. Per request. At 10K RPS that&amp;rsquo;s 14 seconds of CPU time per second, just asking the allocator for memory.&lt;/p&gt;
&lt;p&gt;The fix took half a day and cut allocations to about 200 per request. Here&amp;rsquo;s everything I know about reducing allocations in Rust.&lt;/p&gt;</description></item><item><title>Lesson 3: Profiling — perf, flamegraph, samply</title><link>/post/rust/rust-perf-profiling/</link><pubDate>Wed, 19 Mar 2025 10:45:00 +0000</pubDate><guid>/post/rust/rust-perf-profiling/</guid><description>&lt;p&gt;A colleague once asked me to look at a Rust service that was &amp;ldquo;slow.&amp;rdquo; They&amp;rsquo;d already spent a week trying to optimize the JSON parsing layer because &amp;ldquo;parsing is always the bottleneck.&amp;rdquo; I ran a profiler. Sixty-three percent of CPU time was spent in &lt;code&gt;Drop&lt;/code&gt; implementations, deallocating thousands of small strings that were created and immediately discarded. The JSON parsing was 4% of runtime.&lt;/p&gt;
&lt;p&gt;Profiling would&amp;rsquo;ve found that in five minutes. That&amp;rsquo;s why this lesson exists.&lt;/p&gt;</description></item><item><title>Lesson 9: Miri — Your safety net for unsafe Rust</title><link>/post/rust/rust-internals-miri/</link><pubDate>Wed, 19 Mar 2025 08:45:00 +0000</pubDate><guid>/post/rust/rust-internals-miri/</guid><description>&lt;p&gt;I once shipped a Rust library with an unsafe block that worked perfectly on x86, passed all tests, and ran flawlessly in production for months. Then someone compiled it on ARM and it segfaulted immediately. The undefined behavior had been lurking the whole time — x86 just happened to tolerate the misaligned read that ARM wouldn&amp;rsquo;t. If I&amp;rsquo;d run Miri before releasing, I would have caught it in thirty seconds. Lesson learned the hard way.&lt;/p&gt;</description></item><item><title>Lesson 2: Benchmarking with criterion and divan — Statistically rigorous benchmarks</title><link>/post/rust/rust-perf-benchmarking/</link><pubDate>Mon, 17 Mar 2025 14:18:00 +0000</pubDate><guid>/post/rust/rust-perf-benchmarking/</guid><description>&lt;p&gt;Last year I reviewed a PR where someone claimed their new serialization code was &amp;ldquo;2x faster.&amp;rdquo; Their benchmark? &lt;code&gt;std::time::Instant::now()&lt;/code&gt; called once before and once after. Single run. No warmup. No statistical analysis. The &amp;ldquo;2x speedup&amp;rdquo; was thermal throttling on the first run.&lt;/p&gt;
&lt;p&gt;Benchmarking is harder than it looks. Let&amp;rsquo;s do it properly.&lt;/p&gt;
&lt;h2 id="why-naive-benchmarks-lie"&gt;Why Naive Benchmarks Lie&lt;/h2&gt;
&lt;p&gt;Before we get into the tools, let me show you all the ways a naive benchmark can mislead you:&lt;/p&gt;</description></item><item><title>Lesson 8: Custom Allocators — GlobalAlloc, arena allocation, and beyond</title><link>/post/rust/rust-internals-allocator/</link><pubDate>Sun, 16 Mar 2025 13:10:00 +0000</pubDate><guid>/post/rust/rust-internals-allocator/</guid><description>&lt;p&gt;I was building a JSON parser that processed millions of small documents per second. Profiling showed that 40% of the time was spent in &lt;code&gt;malloc&lt;/code&gt; and &lt;code&gt;free&lt;/code&gt; — not parsing, not I/O, just allocating and deallocating tiny strings and vectors. Switched to an arena allocator that bulk-freed everything after each document, and throughput doubled. That experience taught me that the allocator isn&amp;rsquo;t just plumbing — it&amp;rsquo;s a performance lever.&lt;/p&gt;
&lt;h2 id="how-rust-allocates-memory"&gt;How Rust Allocates Memory&lt;/h2&gt;
&lt;p&gt;When you write &lt;code&gt;Box::new(42)&lt;/code&gt; or &lt;code&gt;Vec::with_capacity(100)&lt;/code&gt;, Rust asks the &lt;strong&gt;global allocator&lt;/strong&gt; for memory. By default, this is the system allocator — &lt;code&gt;malloc&lt;/code&gt;/&lt;code&gt;free&lt;/code&gt; on Unix, &lt;code&gt;HeapAlloc&lt;/code&gt;/&lt;code&gt;HeapFree&lt;/code&gt; on Windows. It&amp;rsquo;s a general-purpose allocator designed to handle any allocation pattern reasonably well, but it&amp;rsquo;s not optimized for any specific pattern.&lt;/p&gt;</description></item><item><title>Lesson 1: Performance Philosophy — Measure, don't guess</title><link>/post/rust/rust-perf-philosophy/</link><pubDate>Sat, 15 Mar 2025 08:32:00 +0000</pubDate><guid>/post/rust/rust-perf-philosophy/</guid><description>&lt;p&gt;I once spent three days rewriting a hot loop to avoid a single allocation per iteration. Hand-rolled a custom arena, eliminated two clones, even switched from &lt;code&gt;HashMap&lt;/code&gt; to a hand-tuned open-addressing table. Benchmarked the result: 0.3% improvement. The actual bottleneck? A DNS lookup buried in a library call that I never bothered to profile.&lt;/p&gt;
&lt;p&gt;Three days. Zero meaningful impact. That&amp;rsquo;s the lesson I want to start this entire course with.&lt;/p&gt;</description></item><item><title>Lesson 7: Drop Order — Deterministic destruction and why it matters</title><link>/post/rust/rust-internals-drop-order/</link><pubDate>Thu, 13 Mar 2025 07:20:00 +0000</pubDate><guid>/post/rust/rust-internals-drop-order/</guid><description>&lt;p&gt;I once had a deadlock in a Rust program that only manifested during shutdown. A mutex guard and a database connection were being dropped in the wrong order — the connection&amp;rsquo;s destructor tried to acquire the mutex that was already locked by the guard that hadn&amp;rsquo;t been dropped yet. Rust&amp;rsquo;s drop order is deterministic, but &amp;ldquo;deterministic&amp;rdquo; doesn&amp;rsquo;t mean &amp;ldquo;obvious.&amp;rdquo; Knowing the rules saved me hours of debugging.&lt;/p&gt;
&lt;h2 id="the-drop-trait"&gt;The Drop Trait&lt;/h2&gt;
&lt;p&gt;In Rust, cleanup logic is implemented through the &lt;code&gt;Drop&lt;/code&gt; trait. When a value goes out of scope, the compiler calls &lt;code&gt;drop()&lt;/code&gt; on it automatically. This is RAII — Resource Acquisition Is Initialization — borrowed from C++ but made more reliable by the ownership system.&lt;/p&gt;</description></item><item><title>Lesson 6: repr(C), repr(transparent), repr(packed) — Taking control of memory layout</title><link>/post/rust/rust-internals-repr/</link><pubDate>Tue, 11 Mar 2025 09:55:00 +0000</pubDate><guid>/post/rust/rust-internals-repr/</guid><description>&lt;p&gt;The first time I wrote Rust FFI bindings to a C library, I defined a struct, passed it across the boundary, and got garbage data back. The C side was reading fields at fixed offsets, but Rust had silently reordered my fields for padding efficiency. Took me twenty minutes of staring at hex dumps to realize the layouts didn&amp;rsquo;t match. That&amp;rsquo;s the day I learned about &lt;code&gt;#[repr(C)]&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="default-rust-layout-reprrust"&gt;Default Rust Layout: repr(Rust)&lt;/h2&gt;
&lt;p&gt;By default, Rust structs use &lt;code&gt;repr(Rust)&lt;/code&gt; — the compiler is free to reorder fields, add padding wherever it wants, and generally optimize the layout however it sees fit. The only guarantees are:&lt;/p&gt;</description></item><item><title>Lesson 5: Fat Pointers — &amp;dyn Trait, &amp;[T], and &amp;str under the hood</title><link>/post/rust/rust-internals-fat-pointers/</link><pubDate>Sun, 09 Mar 2025 16:40:00 +0000</pubDate><guid>/post/rust/rust-internals-fat-pointers/</guid><description>&lt;p&gt;I remember being confused why &lt;code&gt;&amp;amp;str&lt;/code&gt; was 16 bytes on a 64-bit system. A pointer is 8 bytes — what&amp;rsquo;s the other 8? That question sent me down a rabbit hole that fundamentally changed how I think about Rust&amp;rsquo;s type system. Turns out, some references carry extra baggage, and that baggage is the entire reason dynamically sized types work.&lt;/p&gt;
&lt;h2 id="thin-pointers-vs-fat-pointers"&gt;Thin Pointers vs Fat Pointers&lt;/h2&gt;
&lt;p&gt;Most references in Rust are &amp;ldquo;thin&amp;rdquo; — a single machine word pointing to the data:&lt;/p&gt;</description></item><item><title>Lesson 4: vtables — How dyn Trait actually works under the hood</title><link>/post/rust/rust-internals-vtable/</link><pubDate>Fri, 07 Mar 2025 11:15:00 +0000</pubDate><guid>/post/rust/rust-internals-vtable/</guid><description>&lt;p&gt;I was profiling a parser once and found that a hot path using &lt;code&gt;dyn Iterator&lt;/code&gt; was 3x slower than the equivalent code using generics. The algorithm was identical. The difference? Dynamic dispatch — every method call went through a vtable lookup instead of being inlined. That day I learned to respect what &lt;code&gt;dyn&lt;/code&gt; really costs. Let&amp;rsquo;s open the hood.&lt;/p&gt;
&lt;h2 id="static-vs-dynamic-dispatch"&gt;Static vs Dynamic Dispatch&lt;/h2&gt;
&lt;p&gt;Rust gives you two ways to call methods on trait objects: static dispatch (generics) and dynamic dispatch (&lt;code&gt;dyn Trait&lt;/code&gt;).&lt;/p&gt;</description></item><item><title>Lesson 3: Box — Heap allocation by choice</title><link>/post/rust/rust-internals-box/</link><pubDate>Wed, 05 Mar 2025 08:30:00 +0000</pubDate><guid>/post/rust/rust-internals-box/</guid><description>&lt;p&gt;When I first started writing Rust, I used &lt;code&gt;Box&lt;/code&gt; everywhere. Came from a Java background where everything lives on the heap, and wrapping things in &lt;code&gt;Box&lt;/code&gt; felt natural. Took me a while to realize I was fighting the language — most of the time, you don&amp;rsquo;t need it. But when you do need &lt;code&gt;Box&lt;/code&gt;, nothing else will do.&lt;/p&gt;
&lt;h2 id="what-box-actually-is"&gt;What Box Actually Is&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Box&amp;lt;T&amp;gt;&lt;/code&gt; is the simplest smart pointer in Rust. It allocates a value of type &lt;code&gt;T&lt;/code&gt; on the heap and gives you ownership of that allocation through a pointer on the stack. When the &lt;code&gt;Box&lt;/code&gt; goes out of scope, it frees the heap memory. That&amp;rsquo;s it. No reference counting, no garbage collection, no magic.&lt;/p&gt;</description></item><item><title>Lesson 12: Macro Anti-Patterns — When not to macro</title><link>/post/rust/rust-macros-anti-patterns/</link><pubDate>Tue, 04 Mar 2025 12:50:00 +0000</pubDate><guid>/post/rust/rust-macros-anti-patterns/</guid><description>&lt;p&gt;I once worked on a codebase where someone had written a macro for everything. Creating structs? Macro. Implementing a two-line function? Macro. Logging? Custom logging macro that wrapped &lt;code&gt;println!&lt;/code&gt; and added a timestamp. The macro definitions file was 800 lines long. The macros had macros inside them. Nobody on the team could modify them without breaking something, and the original author had left six months earlier. That project taught me more about when &lt;em&gt;not&lt;/em&gt; to use macros than any tutorial ever could.&lt;/p&gt;</description></item><item><title>Lesson 2: Stack vs Heap — Where your data actually lives</title><link>/post/rust/rust-internals-stack-heap/</link><pubDate>Mon, 03 Mar 2025 14:45:00 +0000</pubDate><guid>/post/rust/rust-internals-stack-heap/</guid><description>&lt;p&gt;A colleague once asked me why their Rust program was ten times slower than expected. They were allocating a &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt; inside a tight loop — millions of heap allocations per second. Moved the vec outside the loop, pre-allocated with &lt;code&gt;with_capacity&lt;/code&gt;, and the function went from 2 seconds to 80 milliseconds. Stack vs heap isn&amp;rsquo;t an academic distinction. It&amp;rsquo;s the difference between fast code and slow code.&lt;/p&gt;
&lt;h2 id="two-memory-regions-two-personalities"&gt;Two Memory Regions, Two Personalities&lt;/h2&gt;
&lt;p&gt;Your program has (at minimum) two regions of memory to work with: the &lt;strong&gt;stack&lt;/strong&gt; and the &lt;strong&gt;heap&lt;/strong&gt;. They behave differently, perform differently, and serve different purposes. Rust makes the choice between them more explicit than most languages, which is one of the reasons it&amp;rsquo;s fast by default.&lt;/p&gt;</description></item><item><title>Lesson 11: Real-World Macro Patterns — serde, clap, sqlx under the hood</title><link>/post/rust/rust-macros-real-world/</link><pubDate>Sat, 01 Mar 2025 15:40:00 +0000</pubDate><guid>/post/rust/rust-macros-real-world/</guid><description>&lt;p&gt;I used &lt;code&gt;#[derive(Serialize)]&lt;/code&gt; for two years before I actually looked at what it generates. When I finally ran &lt;code&gt;cargo expand&lt;/code&gt; on a struct with five fields, I got 150 lines of serialization code — visitor patterns, generic bounds, field-by-field traversal, error handling. All generated from a single line. Understanding how production crates use macros changed how I think about API design. These aren&amp;rsquo;t academic exercises — they&amp;rsquo;re the patterns behind the most downloaded crates in the ecosystem.&lt;/p&gt;</description></item><item><title>Lesson 1: Memory Layout — Size, alignment, and the padding you never asked for</title><link>/post/rust/rust-internals-memory-layout/</link><pubDate>Sat, 01 Mar 2025 10:22:00 +0000</pubDate><guid>/post/rust/rust-internals-memory-layout/</guid><description>&lt;p&gt;I spent an embarrassing amount of time debugging a networking project where my hand-crafted packet structs were mysteriously three bytes too large. Turns out the compiler was inserting padding I didn&amp;rsquo;t know about. That&amp;rsquo;s when I realized most Rust developers — myself included — treat memory layout as a black box. Let&amp;rsquo;s crack it open.&lt;/p&gt;
&lt;h2 id="why-layout-matters"&gt;Why Layout Matters&lt;/h2&gt;
&lt;p&gt;Every type in Rust has two fundamental properties: &lt;strong&gt;size&lt;/strong&gt; and &lt;strong&gt;alignment&lt;/strong&gt;. The size is how many bytes the value occupies. The alignment is which memory addresses the value is allowed to start at. These two numbers determine everything about how your data sits in memory, how much RAM your program uses, and whether your structs can talk to C code or hardware registers.&lt;/p&gt;</description></item><item><title>Lesson 10: syn and quote — Parsing and generating tokens</title><link>/post/rust/rust-macros-syn-quote/</link><pubDate>Thu, 27 Feb 2025 10:20:00 +0000</pubDate><guid>/post/rust/rust-macros-syn-quote/</guid><description>&lt;p&gt;Every time I write a proc macro without &lt;code&gt;syn&lt;/code&gt; and &lt;code&gt;quote&lt;/code&gt;, I regret it within twenty minutes. Raw &lt;code&gt;TokenStream&lt;/code&gt; manipulation is like writing HTML by concatenating strings — technically possible, practically unbearable. These two crates are the reason Rust&amp;rsquo;s proc macro ecosystem works at all. They handle the two hardest parts — parsing Rust syntax into a usable data structure, and generating valid Rust code from a template — so you can focus on the actual logic of your macro.&lt;/p&gt;</description></item><item><title>Lesson 9: Function-Like Proc Macros — sql!() and friends</title><link>/post/rust/rust-macros-function-like/</link><pubDate>Mon, 24 Feb 2025 18:30:00 +0000</pubDate><guid>/post/rust/rust-macros-function-like/</guid><description>&lt;p&gt;A colleague once asked me why &lt;code&gt;sqlx::query!(&amp;quot;SELECT * FROM users WHERE id = $1&amp;quot;)&lt;/code&gt; can catch SQL errors at compile time. &amp;ldquo;Is it reading the database during compilation?&amp;rdquo; Yes. It literally connects to your database, validates the query, checks the types, and generates type-safe Rust code — all before your program runs. That&amp;rsquo;s a function-like proc macro doing things that feel illegal. And the mechanics behind it are more straightforward than you&amp;rsquo;d think.&lt;/p&gt;</description></item><item><title>Lesson 8: Attribute Macros — #[my_attr] in practice</title><link>/post/rust/rust-macros-attribute/</link><pubDate>Sat, 22 Feb 2025 07:15:00 +0000</pubDate><guid>/post/rust/rust-macros-attribute/</guid><description>&lt;p&gt;I was reviewing a codebase that used Actix Web and kept seeing &lt;code&gt;#[get(&amp;quot;/users&amp;quot;)]&lt;/code&gt; on handler functions. I knew it was a macro, but I didn&amp;rsquo;t understand the mechanics — how does an attribute on a function transform the function? Where does the routing registration happen? When I finally built my own attribute macro, the whole system clicked. Attribute macros aren&amp;rsquo;t magic. They&amp;rsquo;re just functions that receive code and return different code.&lt;/p&gt;</description></item><item><title>Lesson 7: Derive Macros — Custom #[derive()]</title><link>/post/rust/rust-macros-derive/</link><pubDate>Wed, 19 Feb 2025 13:55:00 +0000</pubDate><guid>/post/rust/rust-macros-derive/</guid><description>&lt;p&gt;The first derive macro I shipped to production generated about 200 lines of boilerplate per struct. We had 47 structs. That&amp;rsquo;s 9,400 lines of code I didn&amp;rsquo;t have to write, test, or maintain. Every time someone added a field to a struct, the derive macro picked it up automatically. No manual updates, no forgotten implementations, no &amp;ldquo;oh I changed the struct but forgot to update the builder&amp;rdquo; bugs. Derive macros are the highest-leverage tool in Rust&amp;rsquo;s macro system, and once you build one, you&amp;rsquo;ll find excuses to build more.&lt;/p&gt;</description></item><item><title>Lesson 6: Procedural Macros — The three kinds</title><link>/post/rust/rust-macros-proc-intro/</link><pubDate>Mon, 17 Feb 2025 09:40:00 +0000</pubDate><guid>/post/rust/rust-macros-proc-intro/</guid><description>&lt;p&gt;The moment I realized &lt;code&gt;macro_rules!&lt;/code&gt; couldn&amp;rsquo;t generate new identifier names from captured inputs, I knew I needed something more powerful. I was trying to auto-generate a &lt;code&gt;_builder&lt;/code&gt; suffix for struct names — take &lt;code&gt;Config&lt;/code&gt; and produce &lt;code&gt;ConfigBuilder&lt;/code&gt;. Declarative macros can&amp;rsquo;t do string manipulation on identifiers. Period. That&amp;rsquo;s what pushed me into procedural macros, and honestly, it felt like unlocking a completely different layer of the language.&lt;/p&gt;
&lt;h2 id="what-procedural-macros-actually-are"&gt;What Procedural Macros Actually Are&lt;/h2&gt;
&lt;p&gt;A procedural macro is a Rust function that runs at compile time. It receives a stream of tokens as input, does whatever processing it wants — parsing, analyzing, transforming — and returns a new stream of tokens as output. The compiler then compiles the returned tokens as if they were regular source code.&lt;/p&gt;</description></item><item><title>Lesson 5: Debugging Macros — cargo-expand and trace_macros</title><link>/post/rust/rust-macros-debugging/</link><pubDate>Fri, 14 Feb 2025 11:05:00 +0000</pubDate><guid>/post/rust/rust-macros-debugging/</guid><description>&lt;p&gt;You will write a macro that compiles, runs, and produces the wrong output. You&amp;rsquo;ll stare at the macro definition, convinced it&amp;rsquo;s correct. You&amp;rsquo;ll re-read the pattern matching rules, check the repetition operators, verify the fragment specifiers — everything looks right. And then you&amp;rsquo;ll expand the macro and realize it&amp;rsquo;s generating something completely different from what you imagined. This has happened to me more times than I&amp;rsquo;m willing to admit.&lt;/p&gt;
&lt;p&gt;Macro debugging is a different skill from regular debugging. You can&amp;rsquo;t set breakpoints in macro expansion. You can&amp;rsquo;t step through it. You have to &lt;em&gt;see&lt;/em&gt; the generated code, and then reason backwards from there to figure out where the pattern matching went wrong.&lt;/p&gt;</description></item><item><title>Lesson 20: Production Async Architecture — Connection pools, retries, circuit breakers</title><link>/post/rust/rust-async-production-patterns/</link><pubDate>Wed, 12 Feb 2025 17:09:33 +0000</pubDate><guid>/post/rust/rust-async-production-patterns/</guid><description>&lt;p&gt;This is the lesson that ties everything together. Over the last 19 lessons, we&amp;rsquo;ve built up from mental models to executors, from channels to cancellation safety. Now it&amp;rsquo;s time to put it all into a production-grade architecture.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve run async Rust services handling tens of thousands of requests per second. The patterns in this lesson are the ones that survived contact with real traffic, real failures, and 3 AM pages. Nothing theoretical here — just battle-tested code.&lt;/p&gt;</description></item><item><title>Lesson 4: Macro Hygiene — Scoping and naming pitfalls</title><link>/post/rust/rust-macros-hygiene/</link><pubDate>Wed, 12 Feb 2025 16:10:00 +0000</pubDate><guid>/post/rust/rust-macros-hygiene/</guid><description>&lt;p&gt;I once spent an embarrassing amount of time debugging a macro that worked perfectly in one file and broke in another. Same macro, same input, different behavior. Turned out the expansion was referencing a variable called &lt;code&gt;result&lt;/code&gt; — which happened to shadow a &lt;code&gt;result&lt;/code&gt; variable at the call site. The macro was hygienically correct in isolation but collided with the caller&amp;rsquo;s namespace. That&amp;rsquo;s when I actually understood what &amp;ldquo;macro hygiene&amp;rdquo; means and why Rust only gets it partially right.&lt;/p&gt;</description></item><item><title>Lesson 19: Testing Async Code — Mocking time and I/O</title><link>/post/rust/rust-async-testing/</link><pubDate>Mon, 10 Feb 2025 11:28:56 +0000</pubDate><guid>/post/rust/rust-async-testing/</guid><description>&lt;p&gt;Async code is harder to test than sync code. Not because the logic is more complex, but because time, I/O, and concurrency introduce non-determinism. A test that passes 99 times and fails on the 100th is worse than a test that always fails — at least the always-failing test tells you something.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve developed a set of patterns for testing async Rust that eliminate flakiness. The key insight: mock the things that make tests non-deterministic (time, network, randomness) and let everything else run for real.&lt;/p&gt;</description></item><item><title>Lesson 3: Macro Pattern Matching — Repetition, fragments, and captures</title><link>/post/rust/rust-macros-patterns/</link><pubDate>Mon, 10 Feb 2025 08:30:00 +0000</pubDate><guid>/post/rust/rust-macros-patterns/</guid><description>&lt;p&gt;There&amp;rsquo;s a point when writing declarative macros where the syntax stops feeling like Rust and starts feeling like regex for code. You&amp;rsquo;re stacking repetition operators, nesting captures inside captures, and using &lt;code&gt;tt&lt;/code&gt; munching to parse things the macro system was never designed to parse. It&amp;rsquo;s weird, it&amp;rsquo;s powerful, and once it clicks, you&amp;rsquo;ll wonder why you ever wrote boilerplate by hand.&lt;/p&gt;
&lt;h2 id="repetition-operators-deep-dive"&gt;Repetition Operators Deep Dive&lt;/h2&gt;
&lt;p&gt;We touched on repetition in the last lesson. Now let&amp;rsquo;s get into the mechanics that actually matter when you&amp;rsquo;re building non-trivial macros.&lt;/p&gt;</description></item><item><title>Lesson 18: Tracing Async Code — Context propagation across .await</title><link>/post/rust/rust-async-tracing/</link><pubDate>Sat, 08 Feb 2025 15:43:29 +0000</pubDate><guid>/post/rust/rust-async-tracing/</guid><description>&lt;p&gt;Debugging async code with &lt;code&gt;println!&lt;/code&gt; is like debugging a highway pileup by asking each car individually what happened. You get disconnected fragments — &amp;ldquo;I was going north,&amp;rdquo; &amp;ldquo;I hit something,&amp;rdquo; &amp;ldquo;I heard a crash&amp;rdquo; — but no coherent story. With 500 concurrent tasks all writing to stdout, good luck finding which log line belongs to which request.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;tracing&lt;/code&gt; crate solves this. It gives you structured, contextual logging that follows your request across tasks, &lt;code&gt;.await&lt;/code&gt; points, and even service boundaries. I switched from &lt;code&gt;log&lt;/code&gt; to &lt;code&gt;tracing&lt;/code&gt; three years ago and haven&amp;rsquo;t looked back.&lt;/p&gt;</description></item><item><title>Lesson 2: Declarative Macros — macro_rules! from zero</title><link>/post/rust/rust-macros-declarative/</link><pubDate>Fri, 07 Feb 2025 14:45:00 +0000</pubDate><guid>/post/rust/rust-macros-declarative/</guid><description>&lt;p&gt;The first macro I ever wrote was a disaster. I wanted a shorthand for creating &lt;code&gt;HashMap&lt;/code&gt;s — something like &lt;code&gt;map!{ &amp;quot;a&amp;quot; =&amp;gt; 1, &amp;quot;b&amp;quot; =&amp;gt; 2 }&lt;/code&gt;. The macro compiled. The expansion was garbage. I didn&amp;rsquo;t understand fragment specifiers, I didn&amp;rsquo;t understand repetition, and I definitely didn&amp;rsquo;t understand why the compiler kept telling me &amp;ldquo;unexpected token.&amp;rdquo; Took me a full weekend to get it right. Let me save you that weekend.&lt;/p&gt;</description></item><item><title>Lesson 17: Pin in Async Context — Why futures must be pinned</title><link>/post/rust/rust-async-pinning-revisited/</link><pubDate>Thu, 06 Feb 2025 08:27:44 +0000</pubDate><guid>/post/rust/rust-async-pinning-revisited/</guid><description>&lt;p&gt;I&amp;rsquo;ve been hand-waving around &lt;code&gt;Pin&lt;/code&gt; for 16 lessons. Every time we saw &lt;code&gt;Pin&amp;lt;&amp;amp;mut Self&amp;gt;&lt;/code&gt; in a &lt;code&gt;poll&lt;/code&gt; method, I said &amp;ldquo;don&amp;rsquo;t worry about it.&amp;rdquo; But now it&amp;rsquo;s time to worry about it, because without Pin, async Rust&amp;rsquo;s entire safety model falls apart.&lt;/p&gt;
&lt;p&gt;The good news: the mental model is simpler than it looks. The bad news: the &lt;em&gt;syntax&lt;/em&gt; is still ugly. Let&amp;rsquo;s deal with both.&lt;/p&gt;
&lt;h2 id="the-problem-pin-solves"&gt;The Problem Pin Solves&lt;/h2&gt;
&lt;p&gt;When the compiler turns your &lt;code&gt;async fn&lt;/code&gt; into a state machine, it needs to store local variables across &lt;code&gt;.await&lt;/code&gt; points. Sometimes those variables reference each other:&lt;/p&gt;</description></item><item><title>Lesson 1: Why Macros — When functions aren't enough</title><link>/post/rust/rust-macros-why/</link><pubDate>Wed, 05 Feb 2025 10:22:00 +0000</pubDate><guid>/post/rust/rust-macros-why/</guid><description>&lt;p&gt;I spent three hours once writing nearly identical &lt;code&gt;impl&lt;/code&gt; blocks for sixteen different numeric types. Copy, paste, change &lt;code&gt;i32&lt;/code&gt; to &lt;code&gt;i64&lt;/code&gt;, change &lt;code&gt;i64&lt;/code&gt; to &lt;code&gt;u32&lt;/code&gt;, repeat. Halfway through I started making typos. By the end I had a bug in the &lt;code&gt;u128&lt;/code&gt; variant that took another hour to find. That&amp;rsquo;s the day I actually sat down and learned macros properly.&lt;/p&gt;
&lt;h2 id="the-gap-between-functions-and-macros"&gt;The Gap Between Functions and Macros&lt;/h2&gt;
&lt;p&gt;Functions are great. You take some inputs, do some work, return a result. But functions operate on &lt;em&gt;values&lt;/em&gt;. They can&amp;rsquo;t generate new struct definitions. They can&amp;rsquo;t implement traits for you. They can&amp;rsquo;t vary the number or types of arguments they accept. They can&amp;rsquo;t produce different code depending on what you pass them at compile time.&lt;/p&gt;</description></item><item><title>Lesson 16: How Async Executors Work Under the Hood — Demystifying the runtime</title><link>/post/rust/rust-async-executor-internals/</link><pubDate>Tue, 04 Feb 2025 13:56:18 +0000</pubDate><guid>/post/rust/rust-async-executor-internals/</guid><description>&lt;p&gt;There&amp;rsquo;s a moment in every async Rust developer&amp;rsquo;s journey where the runtime stops being a black box and starts being a machine you understand. For me, it was when I built a toy executor from scratch. Suddenly, &lt;code&gt;Waker&lt;/code&gt;, &lt;code&gt;Context&lt;/code&gt;, &lt;code&gt;poll_ready&lt;/code&gt; — all of it made sense. Not as abstract concepts, but as mechanical parts.&lt;/p&gt;
&lt;p&gt;This lesson won&amp;rsquo;t make you build a production executor. But it will show you how the pieces fit together, and that understanding will change how you write and debug async code.&lt;/p&gt;</description></item><item><title>Lesson 15: Backpressure — Bounded channels and flow control</title><link>/post/rust/rust-async-backpressure/</link><pubDate>Sun, 02 Feb 2025 10:33:45 +0000</pubDate><guid>/post/rust/rust-async-backpressure/</guid><description>&lt;p&gt;The fastest way to kill a production service is to accept work faster than you can process it. I learned this the hard way when a log ingestion pipeline I built consumed 50GB of RAM and crashed because I used unbounded channels everywhere. &amp;ldquo;It works fine in testing&amp;rdquo; — yeah, testing with 100 messages, not 10 million.&lt;/p&gt;
&lt;p&gt;Backpressure is the mechanism by which a system says &amp;ldquo;slow down, I&amp;rsquo;m full.&amp;rdquo; Without it, fast producers overwhelm slow consumers, and your only flow control is the OOM killer.&lt;/p&gt;</description></item><item><title>Lesson 14: Tower — The service middleware pattern</title><link>/post/rust/rust-async-tower/</link><pubDate>Fri, 31 Jan 2025 19:18:37 +0000</pubDate><guid>/post/rust/rust-async-tower/</guid><description>&lt;p&gt;I avoided Tower for months. The trait bounds looked terrifying, the documentation assumed you already knew what you were doing, and I couldn&amp;rsquo;t figure out why I&amp;rsquo;d want it when I could just write functions. Then I needed to add logging, retries, timeouts, and rate limiting to every HTTP handler in a service with 40 endpoints.&lt;/p&gt;
&lt;p&gt;Writing those as middleware that composes? That&amp;rsquo;s Tower&amp;rsquo;s whole thing. And once it clicks, you&amp;rsquo;ll never build a service without it.&lt;/p&gt;</description></item><item><title>Lesson 13: Building HTTP Clients with reqwest — Async HTTP done right</title><link>/post/rust/rust-async-http-clients/</link><pubDate>Thu, 30 Jan 2025 16:42:09 +0000</pubDate><guid>/post/rust/rust-async-http-clients/</guid><description>&lt;p&gt;Almost every backend service I&amp;rsquo;ve built makes HTTP calls to &lt;em&gt;something&lt;/em&gt; — a third-party API, another microservice, a webhook endpoint. And almost every production HTTP bug I&amp;rsquo;ve dealt with comes from one of three things: missing timeouts, not reusing connections, or ignoring response bodies.&lt;/p&gt;
&lt;p&gt;reqwest is the HTTP client for async Rust. It&amp;rsquo;s built on hyper and Tokio, handles connection pooling, TLS, cookies, compression, and all the stuff you don&amp;rsquo;t want to think about. But you still need to use it correctly.&lt;/p&gt;</description></item><item><title>Lesson 12: Async I/O — Files, sockets, DNS</title><link>/post/rust/rust-async-io/</link><pubDate>Tue, 28 Jan 2025 08:55:13 +0000</pubDate><guid>/post/rust/rust-async-io/</guid><description>&lt;p&gt;Here&amp;rsquo;s an uncomfortable truth about async file I/O: on most operating systems, it doesn&amp;rsquo;t really exist. When you call &lt;code&gt;tokio::fs::read_to_string&lt;/code&gt;, Tokio dispatches the operation to a thread pool because Linux&amp;rsquo;s file I/O isn&amp;rsquo;t truly asynchronous (yes, there&amp;rsquo;s io_uring, but Tokio doesn&amp;rsquo;t use it by default). Network I/O, on the other hand, is genuinely async through epoll/kqueue.&lt;/p&gt;
&lt;p&gt;Understanding this distinction matters. It changes how you architect things.&lt;/p&gt;
&lt;h2 id="the-async-io-traits"&gt;The Async I/O Traits&lt;/h2&gt;
&lt;p&gt;Tokio defines two core traits that mirror their std counterparts:&lt;/p&gt;</description></item><item><title>Lesson 11: Timeouts, Deadlines, and Graceful Shutdown — Bounded operations</title><link>/post/rust/rust-async-timeouts/</link><pubDate>Sun, 26 Jan 2025 12:24:51 +0000</pubDate><guid>/post/rust/rust-async-timeouts/</guid><description>&lt;p&gt;Every production outage I&amp;rsquo;ve investigated boils down to one of two things: unbounded retries or missing timeouts. A function that &amp;ldquo;usually takes 50ms&amp;rdquo; eventually takes 30 seconds because the database is overloaded, and suddenly your entire service is frozen because every thread is waiting on that one slow call.&lt;/p&gt;
&lt;p&gt;Timeouts aren&amp;rsquo;t optional in production code. They&amp;rsquo;re as important as error handling. And graceful shutdown — cleanly stopping your service when it&amp;rsquo;s time to deploy — is what separates &amp;ldquo;my service runs in production&amp;rdquo; from &amp;ldquo;my service runs in production &lt;em&gt;well&lt;/em&gt;.&amp;rdquo;&lt;/p&gt;</description></item><item><title>Lesson 10: Cancellation Safety — The silent footgun</title><link>/post/rust/rust-async-cancellation/</link><pubDate>Fri, 24 Jan 2025 09:41:22 +0000</pubDate><guid>/post/rust/rust-async-cancellation/</guid><description>&lt;p&gt;This is the lesson I wish someone had shoved in my face before I wrote my first &lt;code&gt;select!&lt;/code&gt; loop. I lost three days to a bug where messages were disappearing from a queue. No errors. No panics. Just&amp;hellip; gone. Turns out, &lt;code&gt;select!&lt;/code&gt; was cancelling a future that had already read the message from the channel but hadn&amp;rsquo;t finished processing it.&lt;/p&gt;
&lt;p&gt;Cancellation safety is the most under-discussed footgun in async Rust. If you use &lt;code&gt;select!&lt;/code&gt;, you need to understand this.&lt;/p&gt;</description></item><item><title>Lesson 9: Semaphores and Rate Limiting — Bounded async concurrency</title><link>/post/rust/rust-async-semaphores/</link><pubDate>Wed, 22 Jan 2025 15:08:36 +0000</pubDate><guid>/post/rust/rust-async-semaphores/</guid><description>&lt;p&gt;I once crashed a third-party API by spawning 10,000 concurrent requests from an async Rust service. The code was correct — every request completed (eventually). But the API&amp;rsquo;s rate limiter kicked in after 50 concurrent connections, and we got IP-banned for two hours.&lt;/p&gt;
&lt;p&gt;The fix was a single type: &lt;code&gt;tokio::sync::Semaphore&lt;/code&gt;. Five lines of code turned &amp;ldquo;as fast as possible&amp;rdquo; into &amp;ldquo;at most N at a time.&amp;rdquo;&lt;/p&gt;
&lt;h2 id="whats-a-semaphore"&gt;What&amp;rsquo;s a Semaphore?&lt;/h2&gt;
&lt;p&gt;A semaphore is a counter with a maximum value. You acquire a permit before doing work, and release it when you&amp;rsquo;re done. If all permits are taken, acquiring blocks (yields) until one becomes available.&lt;/p&gt;</description></item><item><title>Lesson 8: Async Mutexes — tokio::sync::Mutex vs std</title><link>/post/rust/rust-async-mutex/</link><pubDate>Mon, 20 Jan 2025 07:15:44 +0000</pubDate><guid>/post/rust/rust-async-mutex/</guid><description>&lt;p&gt;&amp;ldquo;Should I use &lt;code&gt;tokio::sync::Mutex&lt;/code&gt; or &lt;code&gt;std::sync::Mutex&lt;/code&gt; in async code?&amp;rdquo; I&amp;rsquo;ve seen this question in every Rust Discord server, every forum, every team Slack. And the answer most people give — &amp;ldquo;always use the async one in async code&amp;rdquo; — is wrong.&lt;/p&gt;
&lt;p&gt;The real answer depends on how long you hold the lock and whether you need to &lt;code&gt;.await&lt;/code&gt; while holding it. Get this wrong and you&amp;rsquo;ll either deadlock your runtime or tank your performance.&lt;/p&gt;</description></item><item><title>Lesson 7: Async Channels — tokio::sync::mpsc</title><link>/post/rust/rust-async-channels/</link><pubDate>Sat, 18 Jan 2025 13:52:28 +0000</pubDate><guid>/post/rust/rust-async-channels/</guid><description>&lt;p&gt;The first real async service I built had a classic architecture: an HTTP handler receives a request, puts work on a queue, a background worker processes it, and the result gets sent back. In Go, this is channels all day. In async Rust, it&amp;rsquo;s &lt;em&gt;also&lt;/em&gt; channels — but you&amp;rsquo;ve got four different kinds to choose from, and picking the wrong one leads to subtle bugs.&lt;/p&gt;
&lt;p&gt;This lesson covers all four of Tokio&amp;rsquo;s channel types and when to reach for each one.&lt;/p&gt;</description></item><item><title>Lesson 6: Streams — Async iterators</title><link>/post/rust/rust-async-streams/</link><pubDate>Thu, 16 Jan 2025 10:31:07 +0000</pubDate><guid>/post/rust/rust-async-streams/</guid><description>&lt;p&gt;Regular iterators give you one value at a time, synchronously. Futures give you one value, asynchronously. Streams give you &lt;em&gt;multiple&lt;/em&gt; values, asynchronously. It&amp;rsquo;s the obvious combination, and once you start using them, you&amp;rsquo;ll wonder how you ever processed sequences of async data without them.&lt;/p&gt;
&lt;p&gt;I first needed streams when building a log aggregator. I had dozens of log sources, each producing lines at their own pace. I needed to merge them, filter them, and process them in real time. Without streams, that code was a tangled mess of channels and select loops. With streams, it was a pipeline.&lt;/p&gt;</description></item><item><title>Lesson 5: tokio::select! — Racing futures</title><link>/post/rust/rust-async-select/</link><pubDate>Tue, 14 Jan 2025 16:19:55 +0000</pubDate><guid>/post/rust/rust-async-select/</guid><description>&lt;p&gt;A couple months ago I was building a WebSocket handler that needed to do three things simultaneously: read from the socket, check a shutdown signal, and send periodic heartbeats. With &lt;code&gt;join!&lt;/code&gt;, I&amp;rsquo;d need all three to complete. But I didn&amp;rsquo;t want them all to complete — I wanted to react to whichever one happened &lt;em&gt;first&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s what &lt;code&gt;select!&lt;/code&gt; does. It races multiple futures and gives you the result of the winner. The losers are dropped.&lt;/p&gt;</description></item><item><title>Lesson 4: Spawning Tasks and JoinHandles — Concurrent work units</title><link>/post/rust/rust-async-spawn-join/</link><pubDate>Sun, 12 Jan 2025 08:44:19 +0000</pubDate><guid>/post/rust/rust-async-spawn-join/</guid><description>&lt;p&gt;I remember the exact moment async Rust &amp;ldquo;clicked&amp;rdquo; for me. I was building an API aggregator that needed to call five different services. My first version awaited them sequentially — 2 seconds total. Then I spawned them as concurrent tasks — 400ms. Same work, 5x faster, and I didn&amp;rsquo;t need to think about threads, locks, or shared state.&lt;/p&gt;
&lt;p&gt;But spawning tasks isn&amp;rsquo;t free, and &lt;code&gt;JoinHandle&lt;/code&gt; has some sharp edges that nobody warned me about. This lesson covers the patterns you&amp;rsquo;ll use every day.&lt;/p&gt;</description></item><item><title>Lesson 3: Tokio — The runtime that powers async Rust</title><link>/post/rust/rust-async-tokio-intro/</link><pubDate>Fri, 10 Jan 2025 11:05:33 +0000</pubDate><guid>/post/rust/rust-async-tokio-intro/</guid><description>&lt;p&gt;Every async Rust tutorial shows you &lt;code&gt;#[tokio::main]&lt;/code&gt; in the first example and then moves on like that&amp;rsquo;s totally self-explanatory. It&amp;rsquo;s not. That macro hides a &lt;em&gt;lot&lt;/em&gt; of important decisions, and if you don&amp;rsquo;t understand what it&amp;rsquo;s doing, you&amp;rsquo;re going to make some costly mistakes.&lt;/p&gt;
&lt;p&gt;I once spent an entire day debugging a deadlock that happened because I was using the single-threaded runtime without realizing it. One &lt;code&gt;#[tokio::main(flavor = &amp;quot;current_thread&amp;quot;)]&lt;/code&gt; vs the default, and my entire application&amp;rsquo;s behavior changed. That shouldn&amp;rsquo;t surprise anyone — but it surprised me.&lt;/p&gt;</description></item><item><title>Lesson 2: The Future Trait — What .await actually does</title><link>/post/rust/rust-async-future-trait/</link><pubDate>Wed, 08 Jan 2025 14:37:42 +0000</pubDate><guid>/post/rust/rust-async-future-trait/</guid><description>&lt;p&gt;After I understood the mental model from lesson 1, my next question was obvious: what happens &lt;em&gt;mechanically&lt;/em&gt; when I write &lt;code&gt;.await&lt;/code&gt;? I could accept &amp;ldquo;it&amp;rsquo;s a state machine&amp;rdquo; as a hand-wave, but I wanted to see the gears turning.&lt;/p&gt;
&lt;p&gt;Turns out, the answer is beautifully simple once you strip away the syntax sugar. The entire async machinery in Rust boils down to one trait, two types, and a contract.&lt;/p&gt;
&lt;h2 id="the-future-trait-for-real-this-time"&gt;The Future Trait, For Real This Time&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;use&lt;/span&gt; std::pin::Pin;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;use&lt;/span&gt; std::task::{Context, Poll};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;pub&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;trait&lt;/span&gt; Future {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;type&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Output&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;poll&lt;/span&gt;(self: &lt;span style="color:#a6e22e"&gt;Pin&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;mut&lt;/span&gt; Self&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;, cx: &lt;span style="color:#66d9ef"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;mut&lt;/span&gt; Context&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&amp;#39;_&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;) -&amp;gt; &lt;span style="color:#a6e22e"&gt;Poll&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Self::Output&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Three things to note:&lt;/p&gt;</description></item><item><title>Lesson 1: Async Mental Model — Futures, not threads</title><link>/post/rust/rust-async-mental-model/</link><pubDate>Mon, 06 Jan 2025 09:22:14 +0000</pubDate><guid>/post/rust/rust-async-mental-model/</guid><description>&lt;p&gt;I spent three weeks writing async Rust code that compiled, ran, and produced correct results — while having absolutely no idea what was actually happening. I was copy-pasting &lt;code&gt;async fn&lt;/code&gt;, slapping &lt;code&gt;.await&lt;/code&gt; on things, and hoping for the best. Sound familiar?&lt;/p&gt;
&lt;p&gt;The problem wasn&amp;rsquo;t syntax. The problem was that I was thinking about async Rust the same way I thought about threads in Go or Java. And that mental model is &lt;em&gt;wrong&lt;/em&gt; for Rust.&lt;/p&gt;</description></item><item><title>Lesson 25: Production Concurrency Architecture — Putting it all together</title><link>/post/rust/rust-conc-production-patterns/</link><pubDate>Sun, 22 Dec 2024 10:00:00 +0000</pubDate><guid>/post/rust/rust-conc-production-patterns/</guid><description>&lt;p&gt;After 24 lessons of building blocks, let&amp;rsquo;s talk about how they compose in real systems. I&amp;rsquo;ve shipped concurrent Rust services handling millions of requests per day, and the architecture patterns that survive production are surprisingly consistent. Not because they&amp;rsquo;re clever — because they&amp;rsquo;re boring. Boring is good when your pager is involved.&lt;/p&gt;
&lt;p&gt;This lesson is the blueprint I wish I&amp;rsquo;d had when I started building concurrent Rust systems for real.&lt;/p&gt;</description></item><item><title>Lesson 24: Testing Concurrent Code — Loom and beyond</title><link>/post/rust/rust-conc-testing/</link><pubDate>Sat, 21 Dec 2024 09:15:00 +0000</pubDate><guid>/post/rust/rust-conc-testing/</guid><description>&lt;p&gt;I spent a full week writing tests for a lock-free queue. Ran them a thousand times — all green. Shipped it. Two days later, a production crash. A race condition that occurred roughly once every 50,000 operations under specific timing. My tests never hit it because standard testing can&amp;rsquo;t explore all possible thread interleavings. That&amp;rsquo;s when I found Loom.&lt;/p&gt;
&lt;p&gt;Testing concurrent code is fundamentally different from testing sequential code. A test that passes doesn&amp;rsquo;t mean the code is correct — it means the code was correct &lt;em&gt;for that particular thread scheduling&lt;/em&gt;. Run the same test with different timing and you might get a different result.&lt;/p&gt;</description></item><item><title>Lesson 23: GPU Computing from Rust — wgpu and compute shaders</title><link>/post/rust/rust-conc-gpu/</link><pubDate>Thu, 19 Dec 2024 11:35:00 +0000</pubDate><guid>/post/rust/rust-conc-gpu/</guid><description>&lt;p&gt;The first time I ran a matrix multiplication on a GPU, my jaw dropped. A computation that took 8 seconds on an 8-core CPU finished in 40 milliseconds on a mid-range GPU. That&amp;rsquo;s a 200x speedup. GPUs have thousands of cores — small, simple cores designed for massively parallel uniform computation.&lt;/p&gt;
&lt;p&gt;Rust&amp;rsquo;s GPU story has gotten remarkably good. &lt;code&gt;wgpu&lt;/code&gt; gives you cross-platform GPU compute that works on Vulkan, Metal, DX12, and even in the browser via WebGPU. No CUDA lock-in.&lt;/p&gt;</description></item><item><title>Lesson 22: SIMD — Explicit vectorization</title><link>/post/rust/rust-conc-simd/</link><pubDate>Tue, 17 Dec 2024 08:20:00 +0000</pubDate><guid>/post/rust/rust-conc-simd/</guid><description>&lt;p&gt;I had an image processing pipeline that took 4.2 seconds per frame on a single core. After rewriting the hot loop with SIMD intrinsics, it dropped to 0.9 seconds. Same core, same algorithm, 4.7x faster. SIMD doesn&amp;rsquo;t add more cores — it makes each core do more work per clock cycle.&lt;/p&gt;
&lt;p&gt;SIMD (Single Instruction, Multiple Data) is the other kind of parallelism. While threads run code on different cores, SIMD runs the same operation on multiple data elements simultaneously within a single core.&lt;/p&gt;</description></item><item><title>Lesson 21: CSP-Style Concurrency — Go channels in Rust</title><link>/post/rust/rust-conc-csp/</link><pubDate>Sun, 15 Dec 2024 10:40:00 +0000</pubDate><guid>/post/rust/rust-conc-csp/</guid><description>&lt;p&gt;Before I wrote Rust full-time, I spent two years writing Go. The goroutine-plus-channel model gets into your brain. You start thinking about problems as independent processes connected by typed pipes. When I switched to Rust, the first thing I looked for was the equivalent of Go&amp;rsquo;s &lt;code&gt;select&lt;/code&gt; statement. Crossbeam has it — and in some ways it&amp;rsquo;s even better.&lt;/p&gt;
&lt;p&gt;CSP (Communicating Sequential Processes) is the formal model behind Go&amp;rsquo;s concurrency. The idea: concurrent processes interact only by passing messages through channels. No shared memory. Each process is sequential internally. Concurrency comes from composition.&lt;/p&gt;</description></item><item><title>Lesson 20: The Actor Model with Rust — Message-passing architectures</title><link>/post/rust/rust-conc-actor-model/</link><pubDate>Fri, 13 Dec 2024 14:20:00 +0000</pubDate><guid>/post/rust/rust-conc-actor-model/</guid><description>&lt;p&gt;The first time I saw Erlang&amp;rsquo;s actor model, I thought it was over-engineered. Every piece of state behind a process, every interaction a message. Then I worked on a system with 40 mutexes, 12 deadlock-prone code paths, and a debugging story that involved printf-ing thread IDs into a file and diffing them. I became an actor model convert that week.&lt;/p&gt;
&lt;p&gt;Rust doesn&amp;rsquo;t have a built-in actor system like Erlang or Akka. But the building blocks — channels, threads, ownership transfer — make building one surprisingly natural.&lt;/p&gt;</description></item><item><title>Lesson 19: Thread-Local Storage — Per-thread state</title><link>/post/rust/rust-conc-thread-local/</link><pubDate>Wed, 11 Dec 2024 09:55:00 +0000</pubDate><guid>/post/rust/rust-conc-thread-local/</guid><description>&lt;p&gt;I was optimizing a JSON serializer that allocated a buffer for every call. Under profiling, those allocations were 30% of the cost. The fix? A thread-local buffer that gets reused across calls on the same thread. No synchronization needed. No contention. Each thread has its own buffer. Throughput doubled.&lt;/p&gt;
&lt;p&gt;Thread-local storage is the ultimate escape hatch from synchronization overhead. If each thread has its own copy, there&amp;rsquo;s nothing to synchronize.&lt;/p&gt;</description></item><item><title>Lesson 18: Debugging Deadlocks and Data Races — Tools and techniques</title><link>/post/rust/rust-conc-deadlocks/</link><pubDate>Mon, 09 Dec 2024 11:30:00 +0000</pubDate><guid>/post/rust/rust-conc-deadlocks/</guid><description>&lt;p&gt;The worst deadlock I ever encountered wasn&amp;rsquo;t between two mutexes. It was between a mutex and a channel. Thread A held a lock and tried to send on a full bounded channel. Thread B was the consumer for that channel but was waiting to acquire the same lock before it could receive. Clean deadlock. Took me four hours to find because I was looking at mutex ordering and the real problem was a channel.&lt;/p&gt;</description></item><item><title>Lesson 17: Lock-Free Data Structures in Rust — Beyond mutexes</title><link>/post/rust/rust-conc-lock-free/</link><pubDate>Sat, 07 Dec 2024 08:50:00 +0000</pubDate><guid>/post/rust/rust-conc-lock-free/</guid><description>&lt;p&gt;A few years ago I was profiling a metrics collection service and found that 60% of the CPU time was spent on mutex contention. Thirty-two threads, one mutex protecting a counter map. The actual &amp;ldquo;work&amp;rdquo; — incrementing counters — took nanoseconds. The locking overhead was three orders of magnitude more expensive than the operation it protected.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s when lock-free data structures become worth the complexity. When the lock is the bottleneck, remove the lock.&lt;/p&gt;</description></item><item><title>Lesson 16: Barriers and Once — Synchronization primitives</title><link>/post/rust/rust-conc-barriers/</link><pubDate>Thu, 05 Dec 2024 10:15:00 +0000</pubDate><guid>/post/rust/rust-conc-barriers/</guid><description>&lt;p&gt;I was building a benchmark suite once — eight threads, each measuring throughput of a different operation. The problem was that threads started at different times depending on OS scheduling. Thread 0 might start 50ms before thread 7, skewing the results. I needed all threads to start their measurement at exactly the same time.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s what barriers do. Everyone waits at the barrier until the last thread arrives, then they all proceed together.&lt;/p&gt;</description></item><item><title>Lesson 15: Condvar — Waiting for conditions</title><link>/post/rust/rust-conc-condvar/</link><pubDate>Tue, 03 Dec 2024 14:40:00 +0000</pubDate><guid>/post/rust/rust-conc-condvar/</guid><description>&lt;p&gt;Early in my career, I wrote a producer-consumer queue using a mutex and a busy-wait loop. The consumer would lock the mutex, check if there&amp;rsquo;s data, unlock, sleep for 10 milliseconds, and repeat. It worked, but it burned CPU doing nothing and had up to 10ms of latency on every message. My tech lead pointed me to condition variables, and the latency dropped to microseconds while CPU usage went to near zero.&lt;/p&gt;</description></item><item><title>Lesson 14: parking_lot — Faster mutexes</title><link>/post/rust/rust-conc-parking-lot/</link><pubDate>Sun, 01 Dec 2024 09:25:00 +0000</pubDate><guid>/post/rust/rust-conc-parking-lot/</guid><description>&lt;p&gt;I switched a high-contention service from &lt;code&gt;std::sync::Mutex&lt;/code&gt; to &lt;code&gt;parking_lot::Mutex&lt;/code&gt; and saw lock acquisition time drop by 30% under load. The API is nearly identical — it was a find-and-replace job. That&amp;rsquo;s the kind of optimization I like: massive payoff, zero complexity cost.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;parking_lot&lt;/code&gt; is one of those crates that probably should have been in the standard library. It provides the same synchronization primitives as std, but faster, smaller, and with more features.&lt;/p&gt;</description></item><item><title>Lesson 13: Fan-Out Fan-In in Rust — Parallel pipelines</title><link>/post/rust/rust-conc-fan-out-fan-in/</link><pubDate>Fri, 29 Nov 2024 12:50:00 +0000</pubDate><guid>/post/rust/rust-conc-fan-out-fan-in/</guid><description>&lt;p&gt;One of the most satisfying architectures I&amp;rsquo;ve built was a real-time analytics pipeline. Events came in through a single ingestion point, fanned out to eight processing workers, then fanned back in to a single aggregator that wrote results to the database. Throughput went from 2,000 events/second to 14,000 with zero data loss.&lt;/p&gt;
&lt;p&gt;Fan-out/fan-in is one of those patterns that looks simple on a whiteboard but has real subtlety in implementation. Getting the channel lifecycle right, handling errors, managing backpressure — that&amp;rsquo;s where things get interesting.&lt;/p&gt;</description></item><item><title>Lesson 12: Worker Pool Patterns — Bounded concurrency</title><link>/post/rust/rust-conc-worker-pools/</link><pubDate>Wed, 27 Nov 2024 08:35:00 +0000</pubDate><guid>/post/rust/rust-conc-worker-pools/</guid><description>&lt;p&gt;I blew up a production database once by spawning unlimited concurrent connections. A batch job that normally processed 100 items suddenly got 50,000. Each item opened a database connection. The connection pool maxed out, then the OS ran out of file descriptors. The database server stopped accepting connections from any service. All because I wrote &lt;code&gt;for item in items { thread::spawn(...) }&lt;/code&gt; without thinking about bounds.&lt;/p&gt;
&lt;p&gt;Worker pools solve this. Fixed number of threads, bounded queue, backpressure when the system is overloaded. After that incident, I never write unbounded concurrency again.&lt;/p&gt;</description></item><item><title>Lesson 11: Crossbeam — Scoped threads and lock-free structures</title><link>/post/rust/rust-conc-crossbeam/</link><pubDate>Mon, 25 Nov 2024 11:05:00 +0000</pubDate><guid>/post/rust/rust-conc-crossbeam/</guid><description>&lt;p&gt;Before Rust 1.63 added &lt;code&gt;thread::scope&lt;/code&gt; to the standard library, crossbeam was the only ergonomic way to spawn threads that could borrow local data. Even now that std has scoped threads, crossbeam remains essential. Its channels are faster than &lt;code&gt;std::sync::mpsc&lt;/code&gt;, it provides lock-free data structures, and its utilities fill gaps the standard library doesn&amp;rsquo;t cover.&lt;/p&gt;
&lt;p&gt;I reach for crossbeam in nearly every concurrent Rust project. Here&amp;rsquo;s why.&lt;/p&gt;
&lt;h2 id="crossbeam-channels"&gt;Crossbeam Channels&lt;/h2&gt;
&lt;p&gt;The biggest win: crossbeam&amp;rsquo;s channels are multi-producer, multi-consumer (MPMC) and significantly faster than &lt;code&gt;std::sync::mpsc&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Lesson 10: Rayon — Data parallelism made easy</title><link>/post/rust/rust-conc-rayon/</link><pubDate>Sat, 23 Nov 2024 15:20:00 +0000</pubDate><guid>/post/rust/rust-conc-rayon/</guid><description>&lt;p&gt;I had a batch processing job — reading 50,000 JSON files, parsing them, running validation, writing results. Single-threaded, it took 12 minutes. I added Rayon, changed &lt;code&gt;.iter()&lt;/code&gt; to &lt;code&gt;.par_iter()&lt;/code&gt;, and it dropped to 90 seconds. Five characters added to my code. That&amp;rsquo;s it.&lt;/p&gt;
&lt;p&gt;Rayon is probably the most impressive crate in the Rust ecosystem for the effort-to-impact ratio. If you have CPU-bound work that operates on collections, Rayon makes parallelism trivial.&lt;/p&gt;</description></item><item><title>Lesson 9: Send and Sync — The traits behind thread safety</title><link>/post/rust/rust-conc-send-sync/</link><pubDate>Thu, 21 Nov 2024 09:40:00 +0000</pubDate><guid>/post/rust/rust-conc-send-sync/</guid><description>&lt;p&gt;There&amp;rsquo;s a moment in every Rust developer&amp;rsquo;s life when they try to send an &lt;code&gt;Rc&amp;lt;RefCell&amp;lt;Vec&amp;lt;String&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt; to another thread and get a wall of compiler errors. They Google the error, see something about &lt;code&gt;Send&lt;/code&gt; and &lt;code&gt;Sync&lt;/code&gt;, patch the type to &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;Vec&amp;lt;String&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt;, and move on without understanding why.&lt;/p&gt;
&lt;p&gt;I did exactly that for six months. Then I needed to write a custom type that crossed thread boundaries, and I had to actually learn what these traits mean. Turns out they&amp;rsquo;re beautifully simple once you see the design.&lt;/p&gt;</description></item><item><title>Lesson 8: Memory Ordering — Relaxed, Acquire, Release, SeqCst</title><link>/post/rust/rust-conc-ordering/</link><pubDate>Tue, 19 Nov 2024 13:15:00 +0000</pubDate><guid>/post/rust/rust-conc-ordering/</guid><description>&lt;p&gt;Memory ordering is the thing that separates people who &lt;em&gt;use&lt;/em&gt; concurrent code from people who &lt;em&gt;write&lt;/em&gt; concurrent primitives. I avoided understanding it for years, using &lt;code&gt;SeqCst&lt;/code&gt; everywhere like a safety blanket. It worked. But it left performance on the table and, more importantly, left me unable to read half the lock-free code I encountered.&lt;/p&gt;
&lt;p&gt;So here&amp;rsquo;s the actual explanation — no hand-waving.&lt;/p&gt;
&lt;h2 id="why-ordering-matters"&gt;Why Ordering Matters&lt;/h2&gt;
&lt;p&gt;Modern CPUs and compilers reorder instructions for performance. Your code says &amp;ldquo;write A, then write B,&amp;rdquo; but the CPU might execute B first if there&amp;rsquo;s no dependency between them. On a single thread, this is invisible — the final result is the same.&lt;/p&gt;</description></item><item><title>Lesson 7: Atomics — Lock-free primitives</title><link>/post/rust/rust-conc-atomics/</link><pubDate>Sun, 17 Nov 2024 07:45:00 +0000</pubDate><guid>/post/rust/rust-conc-atomics/</guid><description>&lt;p&gt;I once replaced a &lt;code&gt;Mutex&amp;lt;u64&amp;gt;&lt;/code&gt; counter in a hot path with an &lt;code&gt;AtomicU64&lt;/code&gt; and saw throughput jump 40%. Not because mutexes are slow — they&amp;rsquo;re fast. But for a single integer being incremented by 32 threads, the overhead of acquiring and releasing a lock millions of times per second adds up to real time.&lt;/p&gt;
&lt;p&gt;Atomics are the foundation of lock-free programming. They let you do thread-safe operations on primitive values without any lock at all.&lt;/p&gt;</description></item><item><title>Lesson 6: Arc&lt;Mutex&lt;T&gt;&gt; — The shared mutable state pattern</title><link>/post/rust/rust-conc-arc-mutex/</link><pubDate>Fri, 15 Nov 2024 10:30:00 +0000</pubDate><guid>/post/rust/rust-conc-arc-mutex/</guid><description>&lt;p&gt;I remember staring at &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;HashMap&amp;lt;String, Vec&amp;lt;u8&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt; in a codebase and thinking &amp;ldquo;this is the ugliest type I&amp;rsquo;ve ever seen.&amp;rdquo; Three months later, after debugging a similar system in Go that had zero type safety around its concurrent map access, I came crawling back to Rust&amp;rsquo;s ugly-but-correct approach.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt; pattern is everywhere in Rust concurrent code. Understanding &lt;em&gt;why&lt;/em&gt; it exists — not just how to type it — is the key.&lt;/p&gt;</description></item><item><title>Lesson 5: Shared State — Mutex, RwLock, and poisoning</title><link>/post/rust/rust-conc-shared-state/</link><pubDate>Wed, 13 Nov 2024 16:55:00 +0000</pubDate><guid>/post/rust/rust-conc-shared-state/</guid><description>&lt;p&gt;The nastiest production bug I ever tracked down involved a Java ConcurrentHashMap that was &amp;ldquo;thread-safe&amp;rdquo; in the API sense but not in the logic sense. Two threads would read a key, both see it&amp;rsquo;s absent, both insert with computed values, and one would silently overwrite the other. The map itself was fine — the &lt;em&gt;access pattern&lt;/em&gt; was broken.&lt;/p&gt;
&lt;p&gt;Rust&amp;rsquo;s Mutex won&amp;rsquo;t save you from logic bugs either. But it will absolutely prevent you from forgetting the lock in the first place.&lt;/p&gt;</description></item><item><title>Lesson 4: Channels — mpsc and beyond</title><link>/post/rust/rust-conc-channels/</link><pubDate>Mon, 11 Nov 2024 09:10:00 +0000</pubDate><guid>/post/rust/rust-conc-channels/</guid><description>&lt;p&gt;The first concurrent system I built that actually worked well was a log aggregation pipeline. Multiple producers writing log lines, one consumer batching and flushing to disk. No shared state, no locks, no races. Just messages flowing through a pipe.&lt;/p&gt;
&lt;p&gt;That experience sold me on message passing. And Rust&amp;rsquo;s channel implementation makes it surprisingly ergonomic.&lt;/p&gt;
&lt;h2 id="the-problem-shared-state-is-hard"&gt;The Problem: Shared State Is Hard&lt;/h2&gt;
&lt;p&gt;You &lt;em&gt;can&lt;/em&gt; share state between threads with mutexes. But every shared mutable variable is a coordination point, a potential bottleneck, and a source of bugs. The more threads touching the same data, the harder the code is to reason about.&lt;/p&gt;</description></item><item><title>Lesson 10: Redis, MongoDB, and Non-Relational Stores — Beyond SQL</title><link>/post/rust/rust-db-nosql/</link><pubDate>Sun, 10 Nov 2024 15:37:00 +0000</pubDate><guid>/post/rust/rust-db-nosql/</guid><description>&lt;p&gt;I spent a week optimizing a Postgres query that powered a leaderboard. It involved a complex window function over millions of rows, and no amount of indexing got it under 200ms. Then a senior engineer walked over, looked at the query, and said &amp;ldquo;Why isn&amp;rsquo;t this in Redis?&amp;rdquo; He was right. I replaced 30 lines of SQL with a sorted set and the response time dropped to 2ms.&lt;/p&gt;
&lt;p&gt;Not every data problem is a SQL problem. Sometimes you need the right tool, not a better query plan.&lt;/p&gt;</description></item><item><title>Lesson 3: move Closures — Sending data to threads</title><link>/post/rust/rust-conc-move-closures/</link><pubDate>Sat, 09 Nov 2024 14:20:00 +0000</pubDate><guid>/post/rust/rust-conc-move-closures/</guid><description>&lt;p&gt;When I first started writing threaded Rust code, I hit the same compiler error about forty times in one afternoon. Something about closures borrowing values that might be dropped. I kept slapping &lt;code&gt;move&lt;/code&gt; on closures until things compiled, without really understanding what was happening.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s a terrible way to learn. So here&amp;rsquo;s the actual explanation I wish I&amp;rsquo;d had.&lt;/p&gt;
&lt;h2 id="the-problem-closures-and-thread-lifetimes"&gt;The Problem: Closures and Thread Lifetimes&lt;/h2&gt;
&lt;p&gt;When you pass a closure to &lt;code&gt;thread::spawn&lt;/code&gt;, the new thread might run for an arbitrary amount of time. It could outlive the function that spawned it. It could outlive the variables it references.&lt;/p&gt;</description></item><item><title>Lesson 2: std::thread — Spawning and joining</title><link>/post/rust/rust-conc-threads/</link><pubDate>Thu, 07 Nov 2024 11:45:00 +0000</pubDate><guid>/post/rust/rust-conc-threads/</guid><description>&lt;p&gt;A few years back I was reviewing a Go service that spawned goroutines like confetti at a parade — hundreds of them, no tracking, no lifecycle management. When the service shut down, half those goroutines just vanished mid-work. Orphaned database connections everywhere. The Go runtime made it &lt;em&gt;so easy&lt;/em&gt; to fire off concurrent work that nobody stopped to think about cleanup.&lt;/p&gt;
&lt;p&gt;Rust&amp;rsquo;s threading model forces you to think about it. Every thread gives you a handle. Every handle demands acknowledgment. You can ignore it — but you have to do so explicitly.&lt;/p&gt;</description></item><item><title>Lesson 9: N+1 Queries, Indexes, and EXPLAIN — Database performance in Rust</title><link>/post/rust/rust-db-performance/</link><pubDate>Wed, 06 Nov 2024 07:52:00 +0000</pubDate><guid>/post/rust/rust-db-performance/</guid><description>&lt;p&gt;A coworker asked me to look at an endpoint that was taking 8 seconds to return 50 orders. The table had 200K rows — not big by any standard. I opened the code, and the pattern was immediately obvious: fetch 50 orders, then for each order, fetch its items in a separate query. Fifty-one database round trips where one would do.&lt;/p&gt;
&lt;p&gt;The N+1 query problem is the most common performance mistake in database-backed applications, and Rust doesn&amp;rsquo;t magically prevent it. You need to know what to look for.&lt;/p&gt;</description></item><item><title>Lesson 1: Why Rust Concurrency Is "Fearless" — The compiler has your back</title><link>/post/rust/rust-conc-why-fearless/</link><pubDate>Tue, 05 Nov 2024 08:30:00 +0000</pubDate><guid>/post/rust/rust-conc-why-fearless/</guid><description>&lt;p&gt;I once spent three days chasing a race condition in a Java service that only manifested under production load. The bug? Two threads updating a shared HashMap — no synchronization, no errors at compile time, no warnings. Just silent data corruption that showed up as incorrect billing amounts. Three days of my life, gone, because the language didn&amp;rsquo;t care.&lt;/p&gt;
&lt;p&gt;Then I tried to write the same bug in Rust. The compiler said no.&lt;/p&gt;</description></item><item><title>Lesson 8: Testing with Real Databases — No more mocking SQL</title><link>/post/rust/rust-db-testing/</link><pubDate>Sun, 03 Nov 2024 13:15:00 +0000</pubDate><guid>/post/rust/rust-db-testing/</guid><description>&lt;p&gt;I spent two days debugging a production issue where a query returned duplicate rows. The unit tests all passed — every mock returned exactly the expected data. The problem was a missing &lt;code&gt;DISTINCT&lt;/code&gt; in a JOIN query that only manifested with real data containing multiple matching rows. The mocks were too perfect. They never produced the messy data that real databases contain.&lt;/p&gt;
&lt;p&gt;That was when I stopped mocking SQL.&lt;/p&gt;
&lt;h2 id="the-problem-with-mocking-database-calls"&gt;The Problem with Mocking Database Calls&lt;/h2&gt;
&lt;p&gt;Mocking databases is popular because it&amp;rsquo;s convenient. You don&amp;rsquo;t need Docker, you don&amp;rsquo;t need a test database, your tests run in milliseconds. But you&amp;rsquo;re testing the wrong thing.&lt;/p&gt;</description></item><item><title>Lesson 7: Building Type-Safe Query Builders — Queries that can't be wrong</title><link>/post/rust/rust-db-query-builder/</link><pubDate>Fri, 01 Nov 2024 09:28:00 +0000</pubDate><guid>/post/rust/rust-db-query-builder/</guid><description>&lt;p&gt;I was reviewing a PR that had a search endpoint with 12 optional filters. The handler was a 200-line function full of &lt;code&gt;if let Some(...)&lt;/code&gt; blocks, each appending a different SQL fragment to a &lt;code&gt;String&lt;/code&gt;. It worked — until someone forgot a space between &lt;code&gt;AND&lt;/code&gt; and a column name, and the query silently returned zero results instead of erroring. No compile error. No test failure. Just a missing space in a string.&lt;/p&gt;</description></item><item><title>Lesson 6: The Repository Pattern in Rust — Abstracting persistence</title><link>/post/rust/rust-db-repository-pattern/</link><pubDate>Wed, 30 Oct 2024 16:40:00 +0000</pubDate><guid>/post/rust/rust-db-repository-pattern/</guid><description>&lt;p&gt;I once inherited a codebase where every HTTP handler had raw SQL queries inline — &lt;code&gt;sqlx::query!&lt;/code&gt; calls scattered through 80+ route handlers. Changing a table name meant grep-and-replace across the entire project. Adding a cache layer meant touching every handler. Testing a handler meant spinning up a real database. It worked, technically, but nobody wanted to touch it.&lt;/p&gt;
&lt;p&gt;The repository pattern fixes this. It puts a wall between your business logic and your database, and that wall pays for itself fast.&lt;/p&gt;</description></item><item><title>Lesson 5: Transactions and Error Rollback — Atomic operations</title><link>/post/rust/rust-db-transactions/</link><pubDate>Mon, 28 Oct 2024 10:05:00 +0000</pubDate><guid>/post/rust/rust-db-transactions/</guid><description>&lt;p&gt;A payment service I worked on had a subtle bug: it deducted money from the user&amp;rsquo;s wallet, then tried to create an order record. If the order insert failed — constraint violation, timeout, anything — the money was already gone. The user&amp;rsquo;s balance was decremented but they had no order. We called these &amp;ldquo;ghost charges&amp;rdquo; internally, and customers called them something less polite.&lt;/p&gt;
&lt;p&gt;The fix was embarrassingly simple: wrap both operations in a transaction.&lt;/p&gt;</description></item><item><title>Lesson 12: Production Deployment — Docker, graceful shutdown, observability</title><link>/post/rust/rust-web-production/</link><pubDate>Mon, 28 Oct 2024 08:55:00 +0000</pubDate><guid>/post/rust/rust-web-production/</guid><description>&lt;p&gt;Shipping to production is where the real education begins. Your local dev environment is a controlled fantasy — one instance, no load balancer, fast database on localhost, unlimited memory. Production is a hostile environment where your service gets killed mid-request, runs out of memory at 3am, and needs to tell you what went wrong without you SSH-ing into a container. This lesson is about surviving out there.&lt;/p&gt;
&lt;h2 id="dockerfile-the-multi-stage-build"&gt;Dockerfile: The Multi-Stage Build&lt;/h2&gt;
&lt;p&gt;Rust binaries are statically linked (or nearly so). A compiled Rust service can run in a scratch or distroless container with no runtime dependencies. This means tiny images — often under 20MB.&lt;/p&gt;</description></item><item><title>Lesson 4: Schema Migrations in Rust Projects — Evolving your database</title><link>/post/rust/rust-db-migrations/</link><pubDate>Sat, 26 Oct 2024 19:12:00 +0000</pubDate><guid>/post/rust/rust-db-migrations/</guid><description>&lt;p&gt;A teammate once ran &lt;code&gt;ALTER TABLE orders DROP COLUMN status&lt;/code&gt; on the production database because he&amp;rsquo;d tested it locally and &amp;ldquo;it worked fine.&amp;rdquo; What he didn&amp;rsquo;t realize was that three other services depended on that column, and they all started throwing errors simultaneously. We spent the evening restoring from a backup.&lt;/p&gt;
&lt;p&gt;Schema migrations exist to prevent exactly this — they&amp;rsquo;re version control for your database.&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Your database schema isn&amp;rsquo;t static. Features get added, requirements change, data models evolve. You need a way to:&lt;/p&gt;</description></item><item><title>Lesson 11: Integration Testing HTTP Services — Testing without mocks</title><link>/post/rust/rust-web-testing/</link><pubDate>Thu, 24 Oct 2024 15:40:00 +0000</pubDate><guid>/post/rust/rust-web-testing/</guid><description>&lt;p&gt;I worked on a codebase that had 600 unit tests with mocked HTTP clients, mocked databases, mocked everything. All 600 passed. The application didn&amp;rsquo;t work. The mocks were wrong — they returned data in a format the real database never produced. Those 600 tests gave the team confidence to ship broken code. Integration tests that hit real infrastructure are harder to write but they tell you whether your application actually works.&lt;/p&gt;</description></item><item><title>Lesson 3: Connection Pooling with deadpool and bb8 — Managing database connections</title><link>/post/rust/rust-db-connection-pools/</link><pubDate>Thu, 24 Oct 2024 14:35:00 +0000</pubDate><guid>/post/rust/rust-db-connection-pools/</guid><description>&lt;p&gt;I once watched a Rust service fall over under modest load — maybe 200 concurrent requests — because every request opened a new Postgres connection, used it for one query, and dropped it. The database was spending more time on TLS handshakes and connection setup than on actual queries. CPU was fine, memory was fine, but &lt;code&gt;pg_stat_activity&lt;/code&gt; showed 200+ connections churning constantly. The fix took ten lines of code: add a connection pool.&lt;/p&gt;</description></item><item><title>Lesson 2: Diesel — The ORM approach</title><link>/post/rust/rust-db-diesel-intro/</link><pubDate>Tue, 22 Oct 2024 08:47:00 +0000</pubDate><guid>/post/rust/rust-db-diesel-intro/</guid><description>&lt;p&gt;I was two weeks into a project where I had to write about 40 CRUD endpoints for an admin panel. Each one needed the same pattern: validate input, build a query, map results to a struct, handle errors. By endpoint number six, I was copy-pasting SQLx queries and changing column names. That&amp;rsquo;s when a colleague asked, &amp;ldquo;Why aren&amp;rsquo;t you using Diesel?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;He was right. Sometimes you don&amp;rsquo;t want to write SQL. Sometimes you want the boilerplate to disappear.&lt;/p&gt;</description></item><item><title>Lesson 10: OpenAPI / Swagger Generation — Documentation from code</title><link>/post/rust/rust-web-openapi/</link><pubDate>Mon, 21 Oct 2024 10:05:00 +0000</pubDate><guid>/post/rust/rust-web-openapi/</guid><description>&lt;p&gt;I&amp;rsquo;ve never seen a team maintain a separate OpenAPI spec in sync with their actual API for more than three months. Someone adds a field, forgets to update the docs, and suddenly the spec says one thing and the API does another. The only API documentation that stays accurate is documentation generated from the code itself. If the code changes, the docs change. No human discipline required.&lt;/p&gt;
&lt;h2 id="the-approach-utoipa"&gt;The Approach: utoipa&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;utoipa&lt;/code&gt; is the go-to crate for generating OpenAPI specs from Rust code. It uses derive macros and attribute annotations to produce an OpenAPI 3.1 JSON spec at compile time. You add annotations to your types and handlers, and utoipa generates a spec that&amp;rsquo;s always in sync with your code.&lt;/p&gt;</description></item><item><title>Lesson 1: SQLx — Compile-time checked queries</title><link>/post/rust/rust-db-sqlx-intro/</link><pubDate>Sun, 20 Oct 2024 11:23:00 +0000</pubDate><guid>/post/rust/rust-db-sqlx-intro/</guid><description>&lt;p&gt;I shipped a typo in a SQL column name to production last year. The column was &lt;code&gt;user_nme&lt;/code&gt; instead of &lt;code&gt;user_name&lt;/code&gt;. The Go service compiled fine, the tests passed (they used mocks), and the bug sat in production for three hours before a customer reported it. Three hours of silent failures because the query returned zero rows instead of erroring out.&lt;/p&gt;
&lt;p&gt;That was the day I started using SQLx in Rust. I haven&amp;rsquo;t shipped a SQL typo since.&lt;/p&gt;</description></item><item><title>Lesson 9: Rate Limiting and Throttling — Protecting your service</title><link>/post/rust/rust-web-rate-limiting/</link><pubDate>Fri, 18 Oct 2024 13:10:00 +0000</pubDate><guid>/post/rust/rust-web-rate-limiting/</guid><description>&lt;p&gt;We launched a public API without rate limiting. Within a week, a single user was making 200 requests per second — not maliciously, just a badly written script with no backoff. Their traffic consumed 40% of our database connections and degraded performance for everyone else. We added rate limiting, their requests started getting 429s, they fixed their script, and everyone was happy. Should&amp;rsquo;ve been there from day one.&lt;/p&gt;
&lt;h2 id="why-rate-limit"&gt;Why Rate Limit&lt;/h2&gt;
&lt;p&gt;Three reasons, in order of importance:&lt;/p&gt;</description></item><item><title>Lesson 8: WebSockets with Axum — Real-time in Rust</title><link>/post/rust/rust-web-websockets/</link><pubDate>Wed, 16 Oct 2024 09:20:00 +0000</pubDate><guid>/post/rust/rust-web-websockets/</guid><description>&lt;p&gt;A startup I consulted for was polling their REST API every 500 milliseconds to check for new messages. Forty thousand clients, each making two requests per second. That&amp;rsquo;s 80,000 requests per second to check if anything changed — and 99% of the time, nothing had. They switched to WebSockets, dropped their server count from 12 to 2, and their AWS bill fell by 70%. Polling is fine for dashboards that refresh every 30 seconds. For anything real-time, you want WebSockets.&lt;/p&gt;</description></item><item><title>Lesson 7: Pagination, Filtering, and Sorting — API patterns that scale</title><link>/post/rust/rust-web-pagination/</link><pubDate>Mon, 14 Oct 2024 16:30:00 +0000</pubDate><guid>/post/rust/rust-web-pagination/</guid><description>&lt;p&gt;We shipped a &amp;ldquo;list all orders&amp;rdquo; endpoint that returned everything. No pagination. Worked great in development with 50 test records. In production, one customer had 340,000 orders. The endpoint took 12 seconds, the response was 45MB, and the frontend crashed trying to render it. We added pagination that afternoon. You should add it before that afternoon.&lt;/p&gt;
&lt;h2 id="offset-based-pagination"&gt;Offset-Based Pagination&lt;/h2&gt;
&lt;p&gt;The most common approach. Simple to implement, easy to understand, and good enough for most internal tools and admin panels.&lt;/p&gt;</description></item><item><title>Lesson 6: Database Integration — SQLx and connection management</title><link>/post/rust/rust-web-database/</link><pubDate>Sat, 12 Oct 2024 07:45:00 +0000</pubDate><guid>/post/rust/rust-web-database/</guid><description>&lt;p&gt;My first Rust web service leaked database connections. I opened a new connection per request and forgot that Rust&amp;rsquo;s ownership system doesn&amp;rsquo;t magically manage TCP sockets. After about 200 concurrent users, PostgreSQL refused new connections and the whole service went down. Connection pooling isn&amp;rsquo;t optional — it&amp;rsquo;s the first thing you set up.&lt;/p&gt;
&lt;h2 id="why-sqlx"&gt;Why SQLx&lt;/h2&gt;
&lt;p&gt;There are three main approaches to database access in Rust:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Diesel&lt;/strong&gt; — A full ORM with a query builder. Generates SQL at compile time. Requires a build step that connects to your database. Strong opinions about schema management.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SeaORM&lt;/strong&gt; — An async ORM inspired by ActiveRecord. Higher level, more magic. Good if you like ORMs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SQLx&lt;/strong&gt; — Not an ORM. You write SQL. SQLx compiles your SQL queries against a real database at compile time, verifying that your SQL is valid and your result types match the columns returned.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I use SQLx because I like writing SQL and I don&amp;rsquo;t trust ORMs in production. ORMs generate queries you can&amp;rsquo;t see, and when they generate bad queries (and they will), debugging is miserable. With SQLx, the SQL is right there in your code, and the compiler verifies it&amp;rsquo;s correct.&lt;/p&gt;</description></item><item><title>Lesson 5: Authentication — JWT, sessions, OAuth</title><link>/post/rust/rust-web-auth/</link><pubDate>Wed, 09 Oct 2024 11:00:00 +0000</pubDate><guid>/post/rust/rust-web-auth/</guid><description>&lt;p&gt;I&amp;rsquo;ve reviewed auth implementations at four different companies. Three of them stored passwords in SHA-256 without salting. One stored them in &lt;em&gt;plain text&lt;/em&gt; in a column called &lt;code&gt;password_encrypted&lt;/code&gt; — because naming it &amp;ldquo;encrypted&amp;rdquo; apparently counted as security. Auth is the part of your application that bad actors actively try to break. Getting it wrong isn&amp;rsquo;t a bug, it&amp;rsquo;s a liability.&lt;/p&gt;
&lt;h2 id="password-hashing-do-this-right-or-dont-do-it-at-all"&gt;Password Hashing: Do This Right or Don&amp;rsquo;t Do It At All&lt;/h2&gt;
&lt;p&gt;Before we talk about tokens or sessions, let&amp;rsquo;s nail password storage. The rules are simple and non-negotiable:&lt;/p&gt;</description></item><item><title>Lesson 10: Conversion Traits — From, Into, TryFrom, AsRef</title><link>/post/rust/rust-stdlib-convert/</link><pubDate>Tue, 08 Oct 2024 15:30:00 +0000</pubDate><guid>/post/rust/rust-stdlib-convert/</guid><description>&lt;p&gt;I used to litter my Rust code with &lt;code&gt;.to_string()&lt;/code&gt;, &lt;code&gt;as&lt;/code&gt;, and manual conversion functions everywhere. Then I learned the conversion traits properly and my APIs went from clunky to clean. These traits are the glue that makes Rust code feel ergonomic — and understanding when to implement each one is the difference between a library that&amp;rsquo;s pleasant to use and one that makes people curse your name.&lt;/p&gt;
&lt;h2 id="from-and-into--infallible-conversion"&gt;From and Into — Infallible Conversion&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;From&amp;lt;T&amp;gt;&lt;/code&gt; defines how to create a type from another type. It can&amp;rsquo;t fail. If there&amp;rsquo;s any possibility of failure, you want &lt;code&gt;TryFrom&lt;/code&gt; instead.&lt;/p&gt;</description></item><item><title>Lesson 4: Request Validation and Error Responses — Clean input handling</title><link>/post/rust/rust-web-request-validation/</link><pubDate>Mon, 07 Oct 2024 19:15:00 +0000</pubDate><guid>/post/rust/rust-web-request-validation/</guid><description>&lt;p&gt;A junior engineer on my team once deployed an endpoint that accepted any string as an email address. Someone submitted &amp;ldquo;lol&amp;rdquo; as their email, the downstream email service threw a cryptic error, and our error tracking lit up with 500s for an hour. Input validation isn&amp;rsquo;t glamorous, but skipping it is how you get paged at dinner.&lt;/p&gt;
&lt;h2 id="the-problem-with-default-error-responses"&gt;The Problem with Default Error Responses&lt;/h2&gt;
&lt;p&gt;Out of the box, Axum&amp;rsquo;s error responses are&amp;hellip; not great. Send malformed JSON to a &lt;code&gt;Json&amp;lt;T&amp;gt;&lt;/code&gt; handler and you get back:&lt;/p&gt;</description></item><item><title>Lesson 3: Middleware with Tower Layers — The composable middleware pattern</title><link>/post/rust/rust-web-middleware/</link><pubDate>Sat, 05 Oct 2024 08:30:00 +0000</pubDate><guid>/post/rust/rust-web-middleware/</guid><description>&lt;p&gt;I once inherited a Node.js codebase with 23 Express middleware functions chained together. Half of them silently swallowed errors, three of them conflicted with each other, and nobody knew what order they ran in. When I started building services in Axum, the Tower middleware model felt like a revelation — not because it&amp;rsquo;s easier (it&amp;rsquo;s actually harder at first), but because it makes middleware &lt;em&gt;composable&lt;/em&gt; and &lt;em&gt;type-checked&lt;/em&gt;. You can&amp;rsquo;t silently swallow errors when the type system forces you to handle them.&lt;/p&gt;</description></item><item><title>Lesson 9: std::sync — Mutex, RwLock, Once, Barrier</title><link>/post/rust/rust-stdlib-sync/</link><pubDate>Fri, 04 Oct 2024 13:25:00 +0000</pubDate><guid>/post/rust/rust-stdlib-sync/</guid><description>&lt;p&gt;I shipped a data race to production exactly once. Go program, shared map, no lock. Took three weeks to reproduce — only happened under heavy load when two goroutines hit the same key simultaneously. The crash dump was useless. In Rust, that code wouldn&amp;rsquo;t have compiled. That&amp;rsquo;s not marketing — it&amp;rsquo;s literally how the type system works.&lt;/p&gt;
&lt;h2 id="arc--shared-ownership-across-threads"&gt;Arc — Shared Ownership Across Threads&lt;/h2&gt;
&lt;p&gt;Before we talk about locks, we need &lt;code&gt;Arc&lt;/code&gt;. You can&amp;rsquo;t share data between threads with just &lt;code&gt;Rc&lt;/code&gt; — it&amp;rsquo;s not thread-safe. &lt;code&gt;Arc&lt;/code&gt; (Atomic Reference Counted) is the thread-safe version.&lt;/p&gt;</description></item><item><title>Lesson 2: Axum from Zero — Routing, handlers, extractors</title><link>/post/rust/rust-web-axum-intro/</link><pubDate>Thu, 03 Oct 2024 14:45:00 +0000</pubDate><guid>/post/rust/rust-web-axum-intro/</guid><description>&lt;p&gt;The first time I tried Axum, I wrote a handler that took five extractor arguments and spent twenty minutes staring at a compiler error that said my function &amp;ldquo;didn&amp;rsquo;t implement Handler.&amp;rdquo; Turns out the order of extractors matters, and there&amp;rsquo;s a limit on how many you can have. Nobody tells you that upfront. So I&amp;rsquo;m telling you now.&lt;/p&gt;
&lt;h2 id="routing-fundamentals"&gt;Routing Fundamentals&lt;/h2&gt;
&lt;p&gt;Axum&amp;rsquo;s router is just a struct that maps HTTP methods and paths to handler functions. No macros, no attributes — you build routes with method calls.&lt;/p&gt;</description></item><item><title>Lesson 1: The Rust Web Landscape — Axum, Actix, Rocket and why I pick Axum</title><link>/post/rust/rust-web-landscape/</link><pubDate>Tue, 01 Oct 2024 10:22:00 +0000</pubDate><guid>/post/rust/rust-web-landscape/</guid><description>&lt;p&gt;I spent three weeks building a service in Actix-web before ripping it out and switching to Axum. Not because Actix was bad — it&amp;rsquo;s genuinely fast and battle-tested. I switched because every time I needed custom middleware, I was fighting the framework instead of writing my application. That experience taught me something: in Rust web development, the framework you pick determines how much you fight the type system versus how much you work &lt;em&gt;with&lt;/em&gt; it.&lt;/p&gt;</description></item><item><title>Lesson 8: std::time — Duration, Instant, SystemTime</title><link>/post/rust/rust-stdlib-time/</link><pubDate>Tue, 01 Oct 2024 07:50:00 +0000</pubDate><guid>/post/rust/rust-stdlib-time/</guid><description>&lt;p&gt;A few months ago I tracked down a bug where a cache TTL check was wrong because someone compared &lt;code&gt;SystemTime&lt;/code&gt; values that had been serialized and deserialized across a system clock adjustment. The cached timestamps jumped backward, and suddenly entries that should&amp;rsquo;ve expired were &amp;ldquo;fresh&amp;rdquo; again. That&amp;rsquo;s the kind of lesson that teaches you the difference between monotonic clocks and wall clocks.&lt;/p&gt;
&lt;h2 id="two-clocks-two-types"&gt;Two Clocks, Two Types&lt;/h2&gt;
&lt;p&gt;Rust gives you two time types because computers have two fundamentally different clocks:&lt;/p&gt;</description></item><item><title>Lesson 7: std::process — Running external commands</title><link>/post/rust/rust-stdlib-process/</link><pubDate>Sun, 29 Sep 2024 19:15:00 +0000</pubDate><guid>/post/rust/rust-stdlib-process/</guid><description>&lt;p&gt;I once wrote a deployment script in Bash that grew to 800 lines with nested conditionals, string interpolation bugs, and error handling that amounted to &amp;ldquo;hope for the best.&amp;rdquo; Rewrote it in Rust using &lt;code&gt;std::process::Command&lt;/code&gt; — same functionality, but with actual error handling and type safety. The number of failed deployments dropped to zero.&lt;/p&gt;
&lt;h2 id="command--the-builder"&gt;Command — The Builder&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;std::process::Command&lt;/code&gt; is a builder for spawning child processes. You construct the command, configure it, then either run it to completion or spawn it and interact with its I/O.&lt;/p&gt;</description></item><item><title>Lesson 6: std::net — TCP, UDP, sockets</title><link>/post/rust/rust-stdlib-net/</link><pubDate>Fri, 27 Sep 2024 09:40:00 +0000</pubDate><guid>/post/rust/rust-stdlib-net/</guid><description>&lt;p&gt;The first network program I ever wrote — a chat server in college — had a bug where it would block forever waiting for one client&amp;rsquo;s message while all the other clients hung. Classic single-threaded socket mistake. Rust&amp;rsquo;s &lt;code&gt;std::net&lt;/code&gt; module gives you the same low-level socket primitives, but the ownership system actually helps you avoid some of those pitfalls.&lt;/p&gt;
&lt;h2 id="tcp--the-reliable-one"&gt;TCP — The Reliable One&lt;/h2&gt;
&lt;p&gt;TCP gives you ordered, reliable byte streams. You connect, you send bytes, they arrive in order (or the connection dies trying). That&amp;rsquo;s the deal.&lt;/p&gt;</description></item><item><title>Lesson 5: std::fmt — Formatting internals</title><link>/post/rust/rust-stdlib-fmt/</link><pubDate>Tue, 24 Sep 2024 11:05:00 +0000</pubDate><guid>/post/rust/rust-stdlib-fmt/</guid><description>&lt;p&gt;I once shipped a monitoring dashboard where all the latency values showed up as &amp;ldquo;Duration { secs: 0, nanos: 234000000 }&amp;rdquo; because I&amp;rsquo;d used &lt;code&gt;{:?}&lt;/code&gt; instead of implementing &lt;code&gt;Display&lt;/code&gt;. That&amp;rsquo;s the kind of thing that makes you sit down and actually learn how formatting works.&lt;/p&gt;
&lt;h2 id="display-vs-debug"&gt;Display vs. Debug&lt;/h2&gt;
&lt;p&gt;These two traits are the foundation of everything in &lt;code&gt;std::fmt&lt;/code&gt;. Every time you use &lt;code&gt;println!&lt;/code&gt;, &lt;code&gt;format!&lt;/code&gt;, or &lt;code&gt;write!&lt;/code&gt;, you&amp;rsquo;re invoking one of them.&lt;/p&gt;</description></item><item><title>Lesson 10: Testing CLI Applications — End-to-end CLI tests</title><link>/post/rust/rust-cli-testing/</link><pubDate>Sun, 22 Sep 2024 12:10:00 +0000</pubDate><guid>/post/rust/rust-cli-testing/</guid><description>&lt;p&gt;I shipped a CLI tool with 100% unit test coverage on the core logic. Users immediately found three bugs. The argument parser accepted &lt;code&gt;--port&lt;/code&gt; but the value wasn&amp;rsquo;t being passed to the server. The &lt;code&gt;--json&lt;/code&gt; flag produced output that wasn&amp;rsquo;t valid JSON because of a stray debug print. And &lt;code&gt;--help&lt;/code&gt; showed the wrong default for &lt;code&gt;--timeout&lt;/code&gt;. None of these bugs lived in the &amp;ldquo;core logic.&amp;rdquo; They lived in the glue between clap, the output formatter, and the actual binary. Unit tests didn&amp;rsquo;t catch them because unit tests don&amp;rsquo;t run the actual binary.&lt;/p&gt;</description></item><item><title>Lesson 4: std::fs and std::path — Filesystem operations done right</title><link>/post/rust/rust-stdlib-fs/</link><pubDate>Sat, 21 Sep 2024 16:10:00 +0000</pubDate><guid>/post/rust/rust-stdlib-fs/</guid><description>&lt;p&gt;A colleague once deployed a script that used string concatenation for file paths: &lt;code&gt;dir + &amp;quot;/&amp;quot; + filename&lt;/code&gt;. Worked perfectly on Linux, blew up on Windows, and silently corrupted paths when &lt;code&gt;dir&lt;/code&gt; ended with a slash. Rust&amp;rsquo;s &lt;code&gt;Path&lt;/code&gt; type exists specifically to prevent this class of bug.&lt;/p&gt;
&lt;h2 id="path-vs-pathbuf--the-strstring-split"&gt;Path vs. PathBuf — The &amp;amp;str/String Split&lt;/h2&gt;
&lt;p&gt;Just like Rust has &lt;code&gt;&amp;amp;str&lt;/code&gt; (borrowed) and &lt;code&gt;String&lt;/code&gt; (owned), it has &lt;code&gt;&amp;amp;Path&lt;/code&gt; (borrowed) and &lt;code&gt;PathBuf&lt;/code&gt; (owned). The duality is identical:&lt;/p&gt;</description></item><item><title>Lesson 3: std::io — Read, Write, BufRead, Seek</title><link>/post/rust/rust-stdlib-io/</link><pubDate>Thu, 19 Sep 2024 08:30:00 +0000</pubDate><guid>/post/rust/rust-stdlib-io/</guid><description>&lt;p&gt;Early in my Rust journey, I wrote a log parser that read a 2GB file byte by byte using &lt;code&gt;read()&lt;/code&gt; without buffering. It took forty minutes. Adding a &lt;code&gt;BufReader&lt;/code&gt; wrapper — one line of code — brought it down to three seconds. That&amp;rsquo;s when I learned that understanding &lt;code&gt;std::io&lt;/code&gt; isn&amp;rsquo;t optional.&lt;/p&gt;
&lt;h2 id="the-four-core-traits"&gt;The Four Core Traits&lt;/h2&gt;
&lt;p&gt;Rust&amp;rsquo;s I/O system is built on four traits. Everything — files, network sockets, stdin, pipes, in-memory buffers — implements some combination of these:&lt;/p&gt;</description></item><item><title>Lesson 9: Building TUIs with ratatui — Terminal user interfaces</title><link>/post/rust/rust-cli-tui/</link><pubDate>Thu, 19 Sep 2024 07:55:00 +0000</pubDate><guid>/post/rust/rust-cli-tui/</guid><description>&lt;p&gt;I was monitoring a deployment through five separate terminal windows — one for logs, one for metrics, one for the deployment status, one for the database, and one running htop. Alt-tabbing between them like a madman. Then a colleague showed me their custom TUI dashboard that combined all five views into a single terminal screen, with tabs and live-updating graphs. It was written in Rust with ratatui. I rebuilt it that weekend.&lt;/p&gt;</description></item><item><title>Lesson 2: Iterator Trait and Adapters — The full picture</title><link>/post/rust/rust-stdlib-iterators/</link><pubDate>Tue, 17 Sep 2024 14:45:00 +0000</pubDate><guid>/post/rust/rust-stdlib-iterators/</guid><description>&lt;p&gt;The moment Rust iterators clicked for me was when I realized they&amp;rsquo;re not loops with extra steps — they&amp;rsquo;re a completely different way of expressing data transformations. I&amp;rsquo;d been writing imperative loops for fifteen years, and the first time I refactored a gnarly nested-loop function into an iterator chain, the result was half the lines and twice as readable.&lt;/p&gt;
&lt;h2 id="the-iterator-trait"&gt;The Iterator Trait&lt;/h2&gt;
&lt;p&gt;Everything starts here. The &lt;code&gt;Iterator&lt;/code&gt; trait is shockingly simple:&lt;/p&gt;</description></item><item><title>Lesson 8: Distribution — Static binaries, cargo-dist, Homebrew</title><link>/post/rust/rust-cli-distribution/</link><pubDate>Mon, 16 Sep 2024 19:40:00 +0000</pubDate><guid>/post/rust/rust-cli-distribution/</guid><description>&lt;p&gt;I built a CLI tool that three teams at work used daily. It lived in a shared directory on an NFS mount. Every time I pushed an update, I&amp;rsquo;d post in Slack: &amp;ldquo;new version in /shared/tools, please copy it to your PATH.&amp;rdquo; Half the team was running a version from three months ago because they forgot. The other half had four copies scattered across their home directories. Distribution matters. If installing your tool is harder than &lt;code&gt;brew install myapp&lt;/code&gt;, most people won&amp;rsquo;t bother.&lt;/p&gt;</description></item><item><title>Lesson 1: Collections Deep Dive — Vec, VecDeque, BTreeMap and when to use which</title><link>/post/rust/rust-stdlib-collections/</link><pubDate>Sun, 15 Sep 2024 10:22:00 +0000</pubDate><guid>/post/rust/rust-stdlib-collections/</guid><description>&lt;p&gt;I spent two days debugging a performance cliff in a data pipeline — turns out I&amp;rsquo;d been using a &lt;code&gt;HashMap&lt;/code&gt; where a &lt;code&gt;BTreeMap&lt;/code&gt; would&amp;rsquo;ve cut iteration time in half because I needed sorted output downstream. The collection you pick matters more than most people think.&lt;/p&gt;
&lt;h2 id="the-big-picture"&gt;The Big Picture&lt;/h2&gt;
&lt;p&gt;Rust&amp;rsquo;s standard library ships a small but deliberate set of collections. Unlike languages that give you seventeen flavors of list, Rust gives you a few well-designed options and expects you to understand the trade-offs. That&amp;rsquo;s actually a feature — fewer choices means you can develop genuine intuition about when to use what.&lt;/p&gt;</description></item><item><title>Lesson 7: Cross-Compilation for Linux, Mac, Windows — Build once, run anywhere</title><link>/post/rust/rust-cli-cross-compile/</link><pubDate>Sat, 14 Sep 2024 10:25:00 +0000</pubDate><guid>/post/rust/rust-cli-cross-compile/</guid><description>&lt;p&gt;First time I tried to cross-compile a Rust binary from my Mac to Linux, I ran &lt;code&gt;cargo build --target x86_64-unknown-linux-gnu&lt;/code&gt; and got hit with a wall of linker errors. Missing &lt;code&gt;cc&lt;/code&gt;, wrong &lt;code&gt;libc&lt;/code&gt;, something about &lt;code&gt;crt1.o&lt;/code&gt;. It felt like the &amp;ldquo;build once, run anywhere&amp;rdquo; promise was a lie. It wasn&amp;rsquo;t — I just didn&amp;rsquo;t understand how Rust&amp;rsquo;s compilation model interacts with system libraries. Once that clicked, cross-compilation became routine.&lt;/p&gt;
&lt;h2 id="how-rust-compilation-works"&gt;How Rust Compilation Works&lt;/h2&gt;
&lt;p&gt;Rust compiles to LLVM IR, then LLVM generates machine code for the target architecture. That part works across platforms — LLVM knows how to emit x86_64, aarch64, arm, riscv, wasm, and more. The problem is the &lt;em&gt;linker&lt;/em&gt;.&lt;/p&gt;</description></item><item><title>Lesson 6: Subcommands and Complex CLI Structures — git-style interfaces</title><link>/post/rust/rust-cli-subcommands/</link><pubDate>Wed, 11 Sep 2024 13:50:00 +0000</pubDate><guid>/post/rust/rust-cli-subcommands/</guid><description>&lt;p&gt;Every tool starts as &lt;code&gt;mytool --flag input.txt&lt;/code&gt;. Then someone asks for a second mode. Then a third. Before you know it, you have &lt;code&gt;mytool --mode=convert --input foo --output bar&lt;/code&gt; and &lt;code&gt;mytool --mode=validate --strict --input foo&lt;/code&gt; and users are scrolling through &lt;code&gt;--help&lt;/code&gt; trying to find the three flags that matter for their use case. The answer is subcommands. &lt;code&gt;git commit&lt;/code&gt;, &lt;code&gt;docker build&lt;/code&gt;, &lt;code&gt;cargo test&lt;/code&gt; — separate commands with separate flags, unified under one binary.&lt;/p&gt;</description></item><item><title>Lesson 5: Signal Handling and Graceful Shutdown — Clean exits</title><link>/post/rust/rust-cli-signals/</link><pubDate>Mon, 09 Sep 2024 09:17:00 +0000</pubDate><guid>/post/rust/rust-cli-signals/</guid><description>&lt;p&gt;I had a CLI tool that converted video files. Big ones — 10, 20 gigabytes each. The conversion created a temp file, wrote the converted output there, then renamed it to the final destination. Hit Ctrl+C at the wrong moment and you&amp;rsquo;d get a half-written 15GB temp file sitting on disk. Users ran out of disk space without knowing why. All because I never handled signals properly.&lt;/p&gt;
&lt;h2 id="what-happens-when-you-press-ctrlc"&gt;What Happens When You Press Ctrl+C&lt;/h2&gt;
&lt;p&gt;When you press Ctrl+C in a terminal, the kernel sends &lt;code&gt;SIGINT&lt;/code&gt; (signal interrupt) to the foreground process group. By default, this kills your program immediately. No destructors run. No &lt;code&gt;Drop&lt;/code&gt; implementations execute. Temporary files stay on disk. Database connections aren&amp;rsquo;t closed. Partial writes aren&amp;rsquo;t rolled back.&lt;/p&gt;</description></item><item><title>Lesson 12: Test Architecture — When to unit, integration, or e2e</title><link>/post/rust/rust-test-architecture/</link><pubDate>Sun, 08 Sep 2024 11:10:00 +0000</pubDate><guid>/post/rust/rust-test-architecture/</guid><description>&lt;p&gt;I inherited a Rust project with 800 tests. Running them took 45 minutes. I dug in and found: 600 unit tests that mocked every dependency (most tested nothing meaningful), 180 integration tests that duplicated what the unit tests already covered, and 20 end-to-end tests that were flaky because they hit a staging server. The project had &lt;em&gt;more tests&lt;/em&gt; than any codebase I&amp;rsquo;d seen — and also &lt;em&gt;more bugs&lt;/em&gt;. The volume was high, but the strategy was garbage.&lt;/p&gt;</description></item><item><title>Lesson 4: Colored Output and Progress Bars — UX for the terminal</title><link>/post/rust/rust-cli-colored-output/</link><pubDate>Sat, 07 Sep 2024 16:33:00 +0000</pubDate><guid>/post/rust/rust-cli-colored-output/</guid><description>&lt;p&gt;I used to think terminal output was either plain text or ANSI escape code soup. Then I looked at how tools like &lt;code&gt;cargo&lt;/code&gt;, &lt;code&gt;ripgrep&lt;/code&gt;, and &lt;code&gt;bat&lt;/code&gt; handle their output — color used purposefully to draw the eye, progress bars that give you actual information, spinners that tell you something is happening. Good terminal UX isn&amp;rsquo;t about making things pretty. It&amp;rsquo;s about making information scannable.&lt;/p&gt;
&lt;h2 id="raw-ansi-escape-codes"&gt;Raw ANSI Escape Codes&lt;/h2&gt;
&lt;p&gt;Before using any crate, you should understand what&amp;rsquo;s actually happening. Terminal colors are just special byte sequences embedded in the output stream:&lt;/p&gt;</description></item><item><title>Lesson 11: Testing in CI — GitHub Actions for Rust</title><link>/post/rust/rust-test-ci/</link><pubDate>Thu, 05 Sep 2024 16:45:00 +0000</pubDate><guid>/post/rust/rust-test-ci/</guid><description>&lt;p&gt;I merged a PR last year that passed all tests on my M2 MacBook and broke on the Linux CI runner. The issue? I&amp;rsquo;d used &lt;code&gt;std::path::PathBuf&lt;/code&gt; with hardcoded forward slashes, which worked fine on macOS but blew up on Linux because the test setup expected a specific path format. CI exists to catch exactly this kind of &amp;ldquo;it works on my machine&amp;rdquo; problem. Here&amp;rsquo;s how to set it up properly for Rust.&lt;/p&gt;</description></item><item><title>Lesson 3: Configuration Files and Environment Variables — Config that scales</title><link>/post/rust/rust-cli-config/</link><pubDate>Thu, 05 Sep 2024 11:08:00 +0000</pubDate><guid>/post/rust/rust-cli-config/</guid><description>&lt;p&gt;I shipped a CLI tool with 23 flags once. Twenty-three. The &lt;code&gt;--help&lt;/code&gt; output scrolled past two terminal screens. Users hated it, nobody could remember the flags, and every deployment script was a wall of backslash-continued command lines. That&amp;rsquo;s when I learned: if your tool has more than about eight flags, you need a configuration file.&lt;/p&gt;
&lt;h2 id="the-configuration-hierarchy"&gt;The Configuration Hierarchy&lt;/h2&gt;
&lt;p&gt;Every serious CLI tool follows the same precedence order:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Command-line flags (highest priority)&lt;/li&gt;
&lt;li&gt;Environment variables&lt;/li&gt;
&lt;li&gt;Project-local config file (&lt;code&gt;.myapp.toml&lt;/code&gt; in the current directory)&lt;/li&gt;
&lt;li&gt;User config file (&lt;code&gt;~/.config/myapp/config.toml&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;System config file (&lt;code&gt;/etc/myapp/config.toml&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Compiled-in defaults (lowest priority)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Each layer overrides the one below it. This lets users set defaults in their home directory, override them per-project, and override those on a per-invocation basis with flags. kubectl, git, docker — they all work this way.&lt;/p&gt;</description></item><item><title>Lesson 2: stdin, stdout, stderr — I/O patterns</title><link>/post/rust/rust-cli-io/</link><pubDate>Tue, 03 Sep 2024 08:45:00 +0000</pubDate><guid>/post/rust/rust-cli-io/</guid><description>&lt;p&gt;A coworker once asked me to review a Rust CLI they&amp;rsquo;d written. It read a CSV file, transformed some columns, and wrote the result. Worked perfectly — until someone piped in a 2GB file. The tool ate 4GB of RAM, hung for thirty seconds, then crashed. They were reading the entire file into a &lt;code&gt;String&lt;/code&gt; before processing a single line. Classic.&lt;/p&gt;
&lt;h2 id="why-io-is-harder-than-it-looks"&gt;Why I/O Is Harder Than It Looks&lt;/h2&gt;
&lt;p&gt;Unix tools work because of a simple contract: read from stdin, write to stdout, errors to stderr. &lt;code&gt;cat file | grep pattern | sort | uniq -c&lt;/code&gt;. Each program does one thing. They compose through pipes. It&amp;rsquo;s beautiful when it works.&lt;/p&gt;</description></item><item><title>Lesson 10: Benchmarking with criterion — Measure, don't guess</title><link>/post/rust/rust-test-benchmarks/</link><pubDate>Mon, 02 Sep 2024 13:20:00 +0000</pubDate><guid>/post/rust/rust-test-benchmarks/</guid><description>&lt;p&gt;I once optimized a hot loop by replacing a &lt;code&gt;HashMap&lt;/code&gt; lookup with a &lt;code&gt;Vec&lt;/code&gt; indexed by a precomputed key. Felt clever. Ran the benchmarks. The &amp;ldquo;optimized&amp;rdquo; version was 15% &lt;em&gt;slower&lt;/em&gt; because the &lt;code&gt;Vec&lt;/code&gt; was huge, cache-cold, and the access pattern was random. Without the benchmark, I would&amp;rsquo;ve shipped that &amp;ldquo;improvement&amp;rdquo; and bragged about it. Performance intuition is unreliable. Measurement isn&amp;rsquo;t.&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Rust is fast by default, but that doesn&amp;rsquo;t mean your code is fast. You can still write O(n^2) algorithms, cause cache thrashing, allocate unnecessarily, or clone data you could borrow. Worse, optimizations that seem obvious can backfire in ways you don&amp;rsquo;t expect.&lt;/p&gt;</description></item><item><title>Lesson 1: clap — Argument parsing done right</title><link>/post/rust/rust-cli-clap/</link><pubDate>Sun, 01 Sep 2024 14:22:00 +0000</pubDate><guid>/post/rust/rust-cli-clap/</guid><description>&lt;p&gt;I&amp;rsquo;ve written argument parsers by hand in four different languages. Every single time, I ended up with a tangled mess of string matching, edge cases around flags that take optional values, and help text that drifted out of sync with reality within a week. Then I found clap.&lt;/p&gt;
&lt;h2 id="the-problem-with-rolling-your-own"&gt;The Problem With Rolling Your Own&lt;/h2&gt;
&lt;p&gt;Parsing command-line arguments &lt;em&gt;seems&lt;/em&gt; simple. You grab &lt;code&gt;std::env::args()&lt;/code&gt;, maybe split on &lt;code&gt;=&lt;/code&gt;, handle some &lt;code&gt;--flag&lt;/code&gt; and &lt;code&gt;-f&lt;/code&gt; cases. Works great until someone passes &lt;code&gt;--output=&lt;/code&gt; with no value. Or uses &lt;code&gt;-vvv&lt;/code&gt; for triple verbosity. Or expects &lt;code&gt;--help&lt;/code&gt; to just work. Or wants &lt;code&gt;--color=always|never|auto&lt;/code&gt;. Suddenly your 40-line parser is 400 lines of spaghetti.&lt;/p&gt;</description></item><item><title>Lesson 9: Code Coverage — tarpaulin and llvm-cov</title><link>/post/rust/rust-test-coverage/</link><pubDate>Fri, 30 Aug 2024 07:55:00 +0000</pubDate><guid>/post/rust/rust-test-coverage/</guid><description>&lt;p&gt;I worked on a team that had a strict &amp;ldquo;90% code coverage&amp;rdquo; policy. Every PR had to hit that number or it got rejected. The result? People wrote tests like &lt;code&gt;assert!(true)&lt;/code&gt; just to touch lines. We had 92% coverage and bugs everywhere. Coverage is a useful signal. It&amp;rsquo;s a terrible goal.&lt;/p&gt;
&lt;p&gt;That said, knowing &lt;em&gt;which&lt;/em&gt; lines your tests don&amp;rsquo;t touch is genuinely valuable. It tells you where your blind spots are. Here&amp;rsquo;s how to measure it in Rust.&lt;/p&gt;</description></item><item><title>Lesson 8: Snapshot Testing with insta — Catch regressions instantly</title><link>/post/rust/rust-test-snapshot/</link><pubDate>Tue, 27 Aug 2024 10:30:00 +0000</pubDate><guid>/post/rust/rust-test-snapshot/</guid><description>&lt;p&gt;I refactored a code formatter once — changed how it handles indentation for nested blocks. I had assertions checking specific outputs for five test cases, and they all passed. But the formatter produced subtly different output for a sixth pattern I hadn&amp;rsquo;t tested, and a user filed a bug three days later. If I&amp;rsquo;d had snapshot tests, I would&amp;rsquo;ve seen every output change in a diff during the refactor. Five minutes of review instead of three days of embarrassment.&lt;/p&gt;</description></item><item><title>Lesson 7: Fuzz Testing with cargo-fuzz — Breaking your code automatically</title><link>/post/rust/rust-test-fuzzing/</link><pubDate>Sat, 24 Aug 2024 20:05:00 +0000</pubDate><guid>/post/rust/rust-test-fuzzing/</guid><description>&lt;p&gt;A friend of mine maintains a binary parser in Rust. It had hundreds of unit tests, property tests, the works. He ran a fuzzer against it on a Friday afternoon, left it running over the weekend. Monday morning: 23 unique crashes. Some were panics from unexpected inputs. Two were infinite loops. One was a stack overflow from deeply nested structures. All from inputs no human would ever think to construct.&lt;/p&gt;</description></item><item><title>Lesson 6: Property-Based Testing with proptest — Let the computer find your bugs</title><link>/post/rust/rust-test-property/</link><pubDate>Thu, 22 Aug 2024 15:40:00 +0000</pubDate><guid>/post/rust/rust-test-property/</guid><description>&lt;p&gt;I wrote a URL parser once. Had fifty hand-crafted test cases. All green. Pushed to production. Within a week, a user sent a URL with a percent-encoded space followed by a Unicode character, and the parser crashed. I never would have thought to write that test case. A property-based test would have found it in under a second.&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The Problem&lt;/h2&gt;
&lt;p&gt;When you write tests by hand, you test the cases you think of. But bugs don&amp;rsquo;t live in the cases you think of — they live in the ones you don&amp;rsquo;t. Your test for &lt;code&gt;sort([3, 1, 2])&lt;/code&gt; passes, but does your sort handle a list with ten million duplicates? A list of all negative numbers? An empty list? A list with &lt;code&gt;i32::MAX&lt;/code&gt; and &lt;code&gt;i32::MIN&lt;/code&gt; adjacent?&lt;/p&gt;</description></item><item><title>Lesson 5: Mocking — mockall and faking dependencies</title><link>/post/rust/rust-test-mocking/</link><pubDate>Mon, 19 Aug 2024 08:15:00 +0000</pubDate><guid>/post/rust/rust-test-mocking/</guid><description>&lt;p&gt;I spent an embarrassing amount of time early in my Rust journey trying to mock a database connection by hand. I built a fake struct, implemented the trait, tracked method calls with &lt;code&gt;RefCell&amp;lt;Vec&amp;lt;...&amp;gt;&amp;gt;&lt;/code&gt; wrappers, wrote expectation-checking logic&amp;hellip; and ended up with 200 lines of mock code to test 15 lines of business logic. Then I found &lt;code&gt;mockall&lt;/code&gt; and felt like an idiot.&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Real code has dependencies. Your payment processor calls Stripe. Your user service queries a database. Your notification system sends emails. You can&amp;rsquo;t — and shouldn&amp;rsquo;t — hit these real services in unit tests. They&amp;rsquo;re slow, flaky, expensive, and non-deterministic.&lt;/p&gt;</description></item><item><title>Lesson 4: Test Fixtures and Setup/Teardown — Reusable test infrastructure</title><link>/post/rust/rust-test-fixtures/</link><pubDate>Sat, 17 Aug 2024 11:30:00 +0000</pubDate><guid>/post/rust/rust-test-fixtures/</guid><description>&lt;p&gt;I had a test file last year where every single test started with the same twelve lines of setup — creating a database connection, inserting seed data, configuring a logger. Twelve lines, copy-pasted forty times. When I needed to change the seed data, I had to update forty tests. I missed three. Those three tests silently tested against stale data for two months.&lt;/p&gt;
&lt;p&gt;Fixtures exist to prevent this kind of insanity.&lt;/p&gt;</description></item><item><title>Lesson 3: Doc Tests — Tested documentation</title><link>/post/rust/rust-test-doc-tests/</link><pubDate>Wed, 14 Aug 2024 18:10:00 +0000</pubDate><guid>/post/rust/rust-test-doc-tests/</guid><description>&lt;p&gt;I once read the docs for a popular Rust crate, copied the example verbatim, and it didn&amp;rsquo;t compile. The API had changed two versions ago but nobody updated the docs. I spent twenty minutes debugging code that was supposed to be the &amp;ldquo;getting started&amp;rdquo; guide. Rust has a built-in solution to this exact problem, and it&amp;rsquo;s one of the most underappreciated features in the language.&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Documentation lies. Not on purpose — it lies because code evolves and docs don&amp;rsquo;t. A function signature changes, a parameter gets renamed, a return type shifts from &lt;code&gt;String&lt;/code&gt; to &lt;code&gt;&amp;amp;str&lt;/code&gt;, and the example in the docstring quietly becomes fiction. No compiler warning, no test failure, just a frustrated user copy-pasting broken code.&lt;/p&gt;</description></item><item><title>Lesson 2: Integration Tests — The tests/ directory</title><link>/post/rust/rust-test-integration/</link><pubDate>Mon, 12 Aug 2024 09:45:00 +0000</pubDate><guid>/post/rust/rust-test-integration/</guid><description>&lt;p&gt;A colleague once reviewed one of my Rust libraries and said, &amp;ldquo;Your unit tests pass, but I can&amp;rsquo;t actually use the crate — the public API doesn&amp;rsquo;t compose the way anyone would expect.&amp;rdquo; He was right. I&amp;rsquo;d tested every internal function meticulously and completely ignored the experience of someone actually calling my library from the outside. That&amp;rsquo;s the gap integration tests fill.&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Unit tests live inside your source files and can access private functions. That&amp;rsquo;s great for verifying internal logic, but it creates a blind spot: you never validate that your &lt;em&gt;public API&lt;/em&gt; actually makes sense. You can have perfect internal functions that combine into a terrible user experience.&lt;/p&gt;</description></item><item><title>Lesson 1: Unit Tests — #[test] and assertions</title><link>/post/rust/rust-test-unit-basics/</link><pubDate>Sat, 10 Aug 2024 14:22:00 +0000</pubDate><guid>/post/rust/rust-test-unit-basics/</guid><description>&lt;p&gt;I shipped a Rust library last year that had zero tests. Not because I&amp;rsquo;m lazy — I was prototyping, moving fast, &amp;ldquo;I&amp;rsquo;ll add tests later.&amp;rdquo; You know how that story ends. A one-character typo in a boundary check sat in production for three weeks before someone&amp;rsquo;s data got silently corrupted. Three weeks. I&amp;rsquo;d have caught it with one &lt;code&gt;assert_eq!&lt;/code&gt; and thirty seconds of effort.&lt;/p&gt;
&lt;p&gt;Never again. Here&amp;rsquo;s how testing actually works in Rust, from the ground up.&lt;/p&gt;</description></item><item><title>Lesson 8: Refutable vs Irrefutable Patterns — Where they apply</title><link>/post/rust/rust-patterns-refutable/</link><pubDate>Thu, 08 Aug 2024 12:40:00 +0000</pubDate><guid>/post/rust/rust-patterns-refutable/</guid><description>&lt;p&gt;I remember the first time the Rust compiler told me &amp;ldquo;refutable pattern in local binding.&amp;rdquo; I stared at the error for five minutes, Googled &amp;ldquo;refutable pattern Rust,&amp;rdquo; read the explanation, and thought: &amp;ldquo;That&amp;rsquo;s&amp;hellip; actually a really good distinction that no other language makes explicit.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Every pattern in Rust is either refutable or irrefutable. Understanding which is which — and where each is allowed — clears up a whole class of confusing compiler errors.&lt;/p&gt;</description></item><item><title>Lesson 7: The Visitor Pattern via Enums — When trait objects won't cut it</title><link>/post/rust/rust-patterns-visitor/</link><pubDate>Mon, 05 Aug 2024 20:15:00 +0000</pubDate><guid>/post/rust/rust-patterns-visitor/</guid><description>&lt;p&gt;I spent a week once trying to make trait objects work for an AST walker. Every new operation meant a new trait, a new impl block for every node type, and a growing pile of boilerplate. When I rewrote the whole thing with an enum and match, the code shrank by 60% and got faster. Not every problem needs dynamic dispatch.&lt;/p&gt;
&lt;h2 id="the-problem-open-vs-closed-hierarchies"&gt;The Problem: Open vs. Closed Hierarchies&lt;/h2&gt;
&lt;p&gt;In object-oriented languages, the visitor pattern exists because class hierarchies are &amp;ldquo;open&amp;rdquo; — anyone can add new subclasses, so you can&amp;rsquo;t write a switch over all possible types. You need double dispatch through interfaces.&lt;/p&gt;</description></item><item><title>Lesson 6: State Machines with Enums — Compile-time guarantees</title><link>/post/rust/rust-patterns-state-machines/</link><pubDate>Fri, 02 Aug 2024 07:20:00 +0000</pubDate><guid>/post/rust/rust-patterns-state-machines/</guid><description>&lt;p&gt;Three years ago, I debugged a payment processing system where an order could go from &amp;ldquo;refunded&amp;rdquo; back to &amp;ldquo;shipped.&amp;rdquo; Nobody intended for that to happen. The state machine was implemented with a status string and a bunch of if/else checks scattered across eight files. One developer added a shortcut. Review missed it. Customers got refund emails followed by shipping confirmations.&lt;/p&gt;
&lt;p&gt;If the state machine had been in the type system, that shortcut wouldn&amp;rsquo;t have compiled.&lt;/p&gt;</description></item><item><title>Lesson 5: Enums Carrying Data — Modeling real domains</title><link>/post/rust/rust-patterns-enums-data/</link><pubDate>Tue, 30 Jul 2024 09:55:00 +0000</pubDate><guid>/post/rust/rust-patterns-enums-data/</guid><description>&lt;p&gt;The worst bug I ever shipped came from a &lt;code&gt;status&lt;/code&gt; field that was a string. It could be &amp;ldquo;active&amp;rdquo;, &amp;ldquo;Active&amp;rdquo;, &amp;ldquo;ACTIVE&amp;rdquo;, &amp;ldquo;active &amp;ldquo;, or — my personal favorite — &amp;ldquo;acitve&amp;rdquo;. Six months of data with a typo nobody caught because strings don&amp;rsquo;t have a compiler checking them.&lt;/p&gt;
&lt;p&gt;Rust enums would have made that bug impossible.&lt;/p&gt;
&lt;h2 id="the-problem-with-primitive-obsession"&gt;The Problem With Primitive Obsession&lt;/h2&gt;
&lt;p&gt;In most languages, developers reach for strings, integers, and booleans to represent domain concepts. A user&amp;rsquo;s role is a string. An order status is an integer. A payment method is&amp;hellip; also a string. This is called &amp;ldquo;primitive obsession&amp;rdquo; and it&amp;rsquo;s responsible for an entire category of bugs:&lt;/p&gt;</description></item><item><title>Lesson 10: Production Error Architecture — Logging, reporting, recovery</title><link>/post/rust/rust-errors-production-patterns/</link><pubDate>Sun, 28 Jul 2024 11:30:00 +0000</pubDate><guid>/post/rust/rust-errors-production-patterns/</guid><description>&lt;p&gt;I shipped a Rust service to production once with great error types, proper &lt;code&gt;Result&lt;/code&gt; propagation, context chains — the whole nine yards. And then I couldn&amp;rsquo;t debug anything because every error got logged as a single flat string with no request ID, no trace correlation, and no distinction between &amp;ldquo;user sent bad input&amp;rdquo; and &amp;ldquo;our database is on fire.&amp;rdquo; Having good error &lt;em&gt;types&lt;/em&gt; is half the battle. The other half is what you &lt;em&gt;do&lt;/em&gt; with those errors when they reach the top of your stack.&lt;/p&gt;</description></item><item><title>Lesson 4: Or Patterns, @ Bindings, and Rest Patterns — The full syntax</title><link>/post/rust/rust-patterns-or-at/</link><pubDate>Sat, 27 Jul 2024 14:10:00 +0000</pubDate><guid>/post/rust/rust-patterns-or-at/</guid><description>&lt;p&gt;I was reviewing a PR last year where someone had written twelve match arms for an enum — four groups of three variants that all did the same thing. Each group was identical code, copy-pasted three times. I left a one-line comment: &amp;ldquo;or patterns.&amp;rdquo; The whole match collapsed from 36 lines to 12.&lt;/p&gt;
&lt;p&gt;Most Rust developers learn the basics of &lt;code&gt;match&lt;/code&gt; early and never dig into the full pattern syntax. That&amp;rsquo;s a shame, because there are three features that eliminate a ton of redundancy: or patterns, &lt;code&gt;@&lt;/code&gt; bindings, and rest patterns.&lt;/p&gt;</description></item><item><title>Lesson 3: Match Guards and Bindings — Fine-grained control</title><link>/post/rust/rust-patterns-guards/</link><pubDate>Thu, 25 Jul 2024 08:30:00 +0000</pubDate><guid>/post/rust/rust-patterns-guards/</guid><description>&lt;p&gt;A few months back I was writing a rate limiter. The logic was simple: if the request is from an internal IP &lt;em&gt;and&lt;/em&gt; the rate is under the limit, allow it. If it&amp;rsquo;s external &lt;em&gt;and&lt;/em&gt; under a different limit, allow it. Otherwise, reject. I started with nested &lt;code&gt;if&lt;/code&gt; statements inside match arms and ended up with something that looked like a plate of spaghetti. Then I rewrote it with match guards and the whole function collapsed to six clean lines.&lt;/p&gt;</description></item><item><title>Lesson 9: panic!, unwrap, expect — When crashing is correct</title><link>/post/rust/rust-errors-panic-strategy/</link><pubDate>Wed, 24 Jul 2024 07:55:00 +0000</pubDate><guid>/post/rust/rust-errors-panic-strategy/</guid><description>&lt;p&gt;There&amp;rsquo;s a pervasive myth in the Rust community that &lt;code&gt;unwrap()&lt;/code&gt; is always bad. I&amp;rsquo;ve seen code reviews where people mechanically replace every &lt;code&gt;unwrap()&lt;/code&gt; with a &lt;code&gt;match&lt;/code&gt; or &lt;code&gt;.expect()&lt;/code&gt;, even when the &lt;code&gt;unwrap()&lt;/code&gt; was perfectly correct. The truth is more nuanced: &lt;code&gt;panic!&lt;/code&gt; is a tool, and like any tool, the question isn&amp;rsquo;t &amp;ldquo;should I ever use it?&amp;rdquo; but &amp;ldquo;when is it the right choice?&amp;rdquo;&lt;/p&gt;
&lt;h2 id="what-panic-actually-does"&gt;What panic! Actually Does&lt;/h2&gt;
&lt;p&gt;When you call &lt;code&gt;panic!()&lt;/code&gt;, Rust does one of two things depending on your configuration:&lt;/p&gt;</description></item><item><title>Lesson 2: Destructuring — Structs, tuples, enums, nested</title><link>/post/rust/rust-patterns-destructuring/</link><pubDate>Mon, 22 Jul 2024 16:45:00 +0000</pubDate><guid>/post/rust/rust-patterns-destructuring/</guid><description>&lt;p&gt;I used to write code like &lt;code&gt;point.x&lt;/code&gt;, &lt;code&gt;point.y&lt;/code&gt;, &lt;code&gt;point.z&lt;/code&gt; over and over in the same function. Three fields, three accesses, repeated twelve times. Then I learned destructuring and realized I&amp;rsquo;d been doing the equivalent of opening a package, looking at each item individually, and putting it back before looking at the next one.&lt;/p&gt;
&lt;p&gt;Destructuring is opening the package and grabbing everything at once.&lt;/p&gt;
&lt;h2 id="the-problem-with-field-by-field-access"&gt;The Problem With Field-by-Field Access&lt;/h2&gt;
&lt;p&gt;When you work with compound types — structs, tuples, enums with data — you constantly need to pull values out of them. In most languages, this means dot access or method calls, and you end up with verbose code that obscures the actual logic:&lt;/p&gt;</description></item><item><title>Lesson 8: Adding Context — Error chains and backtraces</title><link>/post/rust/rust-errors-error-context/</link><pubDate>Sun, 21 Jul 2024 13:10:00 +0000</pubDate><guid>/post/rust/rust-errors-error-context/</guid><description>&lt;p&gt;Picture this: it&amp;rsquo;s 2 AM, your pager goes off, and the log says &lt;code&gt;&amp;quot;connection refused&amp;quot;&lt;/code&gt;. Connection to &lt;em&gt;what&lt;/em&gt;? From &lt;em&gt;where&lt;/em&gt;? During &lt;em&gt;which operation&lt;/em&gt;? That single error message is technically correct and practically useless. I&amp;rsquo;ve been in that exact situation enough times to develop strong opinions about error context. Every error in a production system should answer three questions: what happened, where did it happen, and what was the system trying to do when it happened.&lt;/p&gt;</description></item><item><title>Lesson 1: match — Exhaustive by design</title><link>/post/rust/rust-patterns-match-exhaustive/</link><pubDate>Sat, 20 Jul 2024 11:23:00 +0000</pubDate><guid>/post/rust/rust-patterns-match-exhaustive/</guid><description>&lt;p&gt;I shipped a Python service once that had a &lt;code&gt;match&lt;/code&gt; statement handling four message types. Three months later, the team added a fifth type. Nobody updated the match. The default branch silently swallowed the new messages, and we lost two days of analytics data before anyone noticed.&lt;/p&gt;
&lt;p&gt;Rust&amp;rsquo;s &lt;code&gt;match&lt;/code&gt; wouldn&amp;rsquo;t have let that happen.&lt;/p&gt;
&lt;h2 id="the-problem-with-non-exhaustive-matching"&gt;The Problem With Non-Exhaustive Matching&lt;/h2&gt;
&lt;p&gt;Most languages treat pattern matching (or switch/case) as a convenience. You list the cases you care about, slap a default at the bottom, and move on. The problem is that code evolves. New variants get added, old assumptions break, and that default case quietly covers up bugs.&lt;/p&gt;</description></item><item><title>Lesson 7: Library vs Application Error Strategy — They're not the same</title><link>/post/rust/rust-errors-library-vs-app/</link><pubDate>Thu, 18 Jul 2024 09:40:00 +0000</pubDate><guid>/post/rust/rust-errors-library-vs-app/</guid><description>&lt;p&gt;I learned this the hard way. I was building a parsing library and used &lt;code&gt;anyhow::Error&lt;/code&gt; as my error type in all public functions. A user opened an issue: &amp;ldquo;I can&amp;rsquo;t match on your errors to handle different parse failures differently.&amp;rdquo; They were right. I&amp;rsquo;d designed a library with application-level error handling, and it was a terrible experience for consumers. Took me a weekend to refactor everything to typed errors.&lt;/p&gt;
&lt;p&gt;Libraries and applications have fundamentally different error handling requirements. Mix them up and you&amp;rsquo;ll either frustrate your users or drown in unnecessary boilerplate.&lt;/p&gt;</description></item><item><title>Lesson 6: anyhow — When you don't care about the type</title><link>/post/rust/rust-errors-anyhow/</link><pubDate>Tue, 16 Jul 2024 16:20:00 +0000</pubDate><guid>/post/rust/rust-errors-anyhow/</guid><description>&lt;p&gt;There&amp;rsquo;s a dirty secret in Rust error handling: half the time, you don&amp;rsquo;t actually need typed errors. You need errors that are easy to create, easy to chain, and easy to print. That&amp;rsquo;s &lt;code&gt;anyhow&lt;/code&gt;. Same author as &lt;code&gt;thiserror&lt;/code&gt; (David Tolnay), completely different use case. Where &lt;code&gt;thiserror&lt;/code&gt; is for defining precise error types, &lt;code&gt;anyhow&lt;/code&gt; is for &lt;em&gt;using&lt;/em&gt; errors without ceremony.&lt;/p&gt;
&lt;h2 id="the-problem-anyhow-solves"&gt;The Problem anyhow Solves&lt;/h2&gt;
&lt;p&gt;Without &lt;code&gt;anyhow&lt;/code&gt;, when you have a function that can fail in multiple unrelated ways, you either:&lt;/p&gt;</description></item><item><title>Lesson 5: thiserror — Derive your way to clean errors</title><link>/post/rust/rust-errors-thiserror/</link><pubDate>Sun, 14 Jul 2024 10:00:00 +0000</pubDate><guid>/post/rust/rust-errors-thiserror/</guid><description>&lt;p&gt;After writing my third custom error type by hand — with the &lt;code&gt;Display&lt;/code&gt; impl, the &lt;code&gt;Error&lt;/code&gt; impl, the &lt;code&gt;From&lt;/code&gt; impls — I thought, &amp;ldquo;there has to be a better way.&amp;rdquo; There was. It&amp;rsquo;s called &lt;code&gt;thiserror&lt;/code&gt;, and it&amp;rsquo;s probably the most widely-used error handling crate in the Rust ecosystem. David Tolnay wrote it, which means it&amp;rsquo;s well-designed, well-maintained, and does exactly one thing with zero bloat.&lt;/p&gt;
&lt;h2 id="what-thiserror-does"&gt;What thiserror Does&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;thiserror&lt;/code&gt; is a derive macro that generates &lt;code&gt;Display&lt;/code&gt;, &lt;code&gt;Error&lt;/code&gt;, and &lt;code&gt;From&lt;/code&gt; implementations for your error types. It produces the exact same code you&amp;rsquo;d write by hand — no runtime cost, no extra dependencies at runtime (it&amp;rsquo;s a proc-macro, so it&amp;rsquo;s only a compile-time dependency).&lt;/p&gt;</description></item><item><title>Lesson 15: GATs — Generic associated types explained</title><link>/post/rust/rust-generics-gats/</link><pubDate>Fri, 12 Jul 2024 18:00:00 +0000</pubDate><guid>/post/rust/rust-generics-gats/</guid><description>&lt;p&gt;GATs (Generic Associated Types) took &lt;em&gt;seven years&lt;/em&gt; from proposal to stabilization. That&amp;rsquo;s not because Rust&amp;rsquo;s team is slow — it&amp;rsquo;s because GATs are genuinely hard to get right, and they unlock patterns that were previously impossible without unsafe code or painful workarounds. When they finally landed in Rust 1.65, I immediately rewrote a chunk of a database abstraction layer that had been haunting me for months.&lt;/p&gt;
&lt;p&gt;The one-liner: GATs let associated types have their own generic parameters, including lifetimes.&lt;/p&gt;</description></item><item><title>Lesson 4: Designing Custom Error Types — Your domain, your errors</title><link>/post/rust/rust-errors-custom-types/</link><pubDate>Thu, 11 Jul 2024 19:15:00 +0000</pubDate><guid>/post/rust/rust-errors-custom-types/</guid><description>&lt;p&gt;I once worked on a project where every error was &lt;code&gt;Box&amp;lt;dyn std::error::Error&amp;gt;&lt;/code&gt;. Debugging was miserable. When something failed in production, the logs said things like &amp;ldquo;invalid input&amp;rdquo; with zero context about &lt;em&gt;which&lt;/em&gt; input, &lt;em&gt;where&lt;/em&gt; in the pipeline, or &lt;em&gt;why&lt;/em&gt; it was invalid. That&amp;rsquo;s when I learned that good error types aren&amp;rsquo;t an afterthought — they&amp;rsquo;re part of your domain model.&lt;/p&gt;
&lt;h2 id="why-custom-error-types-matter"&gt;Why Custom Error Types Matter&lt;/h2&gt;
&lt;p&gt;Standard library errors like &lt;code&gt;io::Error&lt;/code&gt; and &lt;code&gt;ParseIntError&lt;/code&gt; are fine for what they describe. But your application has its own failure modes. A payment processor doesn&amp;rsquo;t just fail with &amp;ldquo;IO error&amp;rdquo; — it fails with &amp;ldquo;card declined,&amp;rdquo; &amp;ldquo;insufficient funds,&amp;rdquo; &amp;ldquo;gateway timeout,&amp;rdquo; &amp;ldquo;idempotency key conflict.&amp;rdquo; These are domain-specific failures that deserve domain-specific types.&lt;/p&gt;</description></item><item><title>Lesson 14: Const Generics — Types parameterized by values</title><link>/post/rust/rust-generics-const-generics/</link><pubDate>Wed, 10 Jul 2024 15:30:00 +0000</pubDate><guid>/post/rust/rust-generics-const-generics/</guid><description>&lt;p&gt;Before const generics landed (Rust 1.51), working with arrays was painful. You couldn&amp;rsquo;t write a function that accepted &lt;code&gt;[T; N]&lt;/code&gt; for any &lt;code&gt;N&lt;/code&gt;. The standard library had trait implementations for arrays up to size 32 — manually written, one per size. Need &lt;code&gt;[u8; 33]&lt;/code&gt; to implement &lt;code&gt;Debug&lt;/code&gt;? Too bad. That era is over, and honestly, const generics are one of the most underappreciated features in modern Rust.&lt;/p&gt;
&lt;p&gt;Instead of parameterizing types by &lt;em&gt;other types&lt;/em&gt;, you parameterize them by &lt;em&gt;values&lt;/em&gt;. An array&amp;rsquo;s size, a buffer&amp;rsquo;s capacity, a matrix&amp;rsquo;s dimensions — baked into the type system at compile time.&lt;/p&gt;</description></item><item><title>Lesson 3: The ? Operator — Propagation made elegant</title><link>/post/rust/rust-errors-question-mark/</link><pubDate>Tue, 09 Jul 2024 14:30:00 +0000</pubDate><guid>/post/rust/rust-errors-question-mark/</guid><description>&lt;p&gt;Before the &lt;code&gt;?&lt;/code&gt; operator existed, Rust had &lt;code&gt;try!()&lt;/code&gt; — a macro that did the same thing but looked ugly and nested poorly. When &lt;code&gt;?&lt;/code&gt; landed in Rust 1.13, it was one of those rare language changes where everyone immediately agreed it was better. I remember refactoring an entire crate the same week, deleting dozens of &lt;code&gt;match&lt;/code&gt; blocks and &lt;code&gt;try!()&lt;/code&gt; calls, and the code just&amp;hellip; breathed.&lt;/p&gt;
&lt;h2 id="what--actually-does"&gt;What ? Actually Does&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;?&lt;/code&gt; operator is syntactic sugar for early return on error. When you write this:&lt;/p&gt;</description></item><item><title>Lesson 13: Monomorphization — How generics become fast</title><link>/post/rust/rust-generics-monomorphization/</link><pubDate>Sun, 07 Jul 2024 11:45:00 +0000</pubDate><guid>/post/rust/rust-generics-monomorphization/</guid><description>&lt;p&gt;&amp;ldquo;Zero-cost abstractions&amp;rdquo; is Rust&amp;rsquo;s battle cry. But what does that actually mean for generics? How does &lt;code&gt;Vec&amp;lt;i32&amp;gt;&lt;/code&gt; and &lt;code&gt;Vec&amp;lt;String&amp;gt;&lt;/code&gt; both exist without runtime overhead? The answer is monomorphization — the compiler generates a separate, specialized copy of your generic code for each concrete type used. You write it once, the compiler duplicates it for each type, and the result runs as fast as hand-written specialized code.&lt;/p&gt;
&lt;p&gt;This is brilliant. It&amp;rsquo;s also a double-edged sword. And understanding the mechanism changes how you design generic APIs.&lt;/p&gt;</description></item><item><title>Lesson 2: Result and Option — The foundation</title><link>/post/rust/rust-errors-result-option/</link><pubDate>Sun, 07 Jul 2024 08:45:00 +0000</pubDate><guid>/post/rust/rust-errors-result-option/</guid><description>&lt;p&gt;When I first started writing Rust, I used &lt;code&gt;match&lt;/code&gt; on every single &lt;code&gt;Result&lt;/code&gt; and &lt;code&gt;Option&lt;/code&gt;. My code looked like a staircase — match inside match inside match, indented halfway across the screen. Then a colleague showed me combinators, and suddenly the code read like prose instead of a tax form.&lt;/p&gt;
&lt;h2 id="result-more-than-just-ok-and-err"&gt;Result: More Than Just Ok and Err&lt;/h2&gt;
&lt;p&gt;You already know &lt;code&gt;Result&amp;lt;T, E&amp;gt;&lt;/code&gt; has two variants. But &lt;code&gt;Result&lt;/code&gt; comes with a &lt;em&gt;huge&lt;/em&gt; set of methods that let you transform, combine, and short-circuit without writing explicit &lt;code&gt;match&lt;/code&gt; blocks everywhere.&lt;/p&gt;</description></item><item><title>Lesson 1: Rust's Error Philosophy — No exceptions, no surprises</title><link>/post/rust/rust-errors-philosophy/</link><pubDate>Fri, 05 Jul 2024 11:23:00 +0000</pubDate><guid>/post/rust/rust-errors-philosophy/</guid><description>&lt;p&gt;I spent three years writing Java before I touched Rust. In Java, any function call might throw an exception — checked, unchecked, runtime, whatever. You never really &lt;em&gt;know&lt;/em&gt; what&amp;rsquo;s going to blow up until it does. The first time I wrote Rust code that forced me to handle every possible failure at the call site, I thought it was annoying. Six months later, I realized it was the sanest approach to errors I&amp;rsquo;d ever used.&lt;/p&gt;</description></item><item><title>Lesson 12: Operator Overloading with Traits — Making your types feel native</title><link>/post/rust/rust-traits-operator-overloading/</link><pubDate>Thu, 04 Jul 2024 20:15:00 +0000</pubDate><guid>/post/rust/rust-traits-operator-overloading/</guid><description>&lt;p&gt;I was building a linear algebra library and got tired of writing &lt;code&gt;vector_a.add(&amp;amp;vector_b)&lt;/code&gt; everywhere. It looked ugly. It read poorly. Math should look like math — &lt;code&gt;a + b&lt;/code&gt;, not &lt;code&gt;a.add(&amp;amp;b)&lt;/code&gt;. In Rust, operator overloading isn&amp;rsquo;t some dark magic — it&amp;rsquo;s just trait implementation. Every operator maps to a trait in &lt;code&gt;std::ops&lt;/code&gt;, and implementing that trait makes the operator work on your type.&lt;/p&gt;
&lt;h2 id="the-add-trait"&gt;The &lt;code&gt;Add&lt;/code&gt; Trait&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;+&lt;/code&gt; operator desugars to a call to &lt;code&gt;Add::add&lt;/code&gt;:&lt;/p&gt;</description></item><item><title>Lesson 11: Essential Std Traits — Iterator, Display, From, Default</title><link>/post/rust/rust-traits-std-traits/</link><pubDate>Tue, 02 Jul 2024 08:30:00 +0000</pubDate><guid>/post/rust/rust-traits-std-traits/</guid><description>&lt;p&gt;There&amp;rsquo;s a tier list of Rust traits. Some you&amp;rsquo;ll implement once in your career. Some you&amp;rsquo;ll implement weekly. And then there are the ones you&amp;rsquo;ll implement so often they become muscle memory — &lt;code&gt;Display&lt;/code&gt;, &lt;code&gt;From&lt;/code&gt;, &lt;code&gt;Default&lt;/code&gt;, &lt;code&gt;Iterator&lt;/code&gt;. These four (plus a few friends) are the backbone of idiomatic Rust. If you internalize them, your types will feel native. If you skip them, your types will feel like second-class citizens in the ecosystem.&lt;/p&gt;</description></item><item><title>Lesson 10: Orphan Rules and the Newtype Workaround — Coherence in practice</title><link>/post/rust/rust-traits-orphan-rules/</link><pubDate>Sun, 30 Jun 2024 17:55:00 +0000</pubDate><guid>/post/rust/rust-traits-orphan-rules/</guid><description>&lt;p&gt;Picture this: you&amp;rsquo;re using two crates — &lt;code&gt;serde&lt;/code&gt; and some &lt;code&gt;db_client&lt;/code&gt; crate. You want to implement &lt;code&gt;serde::Serialize&lt;/code&gt; for &lt;code&gt;db_client::Row&lt;/code&gt;. Sounds reasonable. You write the &lt;code&gt;impl&lt;/code&gt;, and the compiler slaps you: &amp;ldquo;only traits defined in the current crate can be implemented for types defined outside of the current crate.&amp;rdquo; Welcome to the orphan rules.&lt;/p&gt;
&lt;p&gt;My first reaction was frustration. My second reaction, after dealing with diamond dependency problems in C++ for years, was &amp;ldquo;oh, this is actually protecting me.&amp;rdquo;&lt;/p&gt;</description></item><item><title>Lesson 9: Blanket Implementations — Implementing for all T</title><link>/post/rust/rust-traits-blanket-impls/</link><pubDate>Fri, 28 Jun 2024 10:20:00 +0000</pubDate><guid>/post/rust/rust-traits-blanket-impls/</guid><description>&lt;p&gt;The moment blanket implementations clicked for me, I felt like I&amp;rsquo;d been handed a cheat code. You write &lt;code&gt;impl&amp;lt;T: Display&amp;gt; ToString for T&lt;/code&gt; and suddenly &lt;em&gt;every single type&lt;/em&gt; that implements &lt;code&gt;Display&lt;/code&gt; automatically gets &lt;code&gt;ToString&lt;/code&gt;. One line of implementation logic, infinite types covered. This is how the standard library builds massive capability trees from small pieces.&lt;/p&gt;
&lt;h2 id="whats-a-blanket-implementation"&gt;What&amp;rsquo;s a Blanket Implementation?&lt;/h2&gt;
&lt;p&gt;A blanket implementation implements a trait for all types matching a bound, rather than for a specific type:&lt;/p&gt;</description></item><item><title>Lesson 8: Object Safety — Why some traits can't be dyn</title><link>/post/rust/rust-traits-object-safety/</link><pubDate>Tue, 25 Jun 2024 13:40:00 +0000</pubDate><guid>/post/rust/rust-traits-object-safety/</guid><description>&lt;p&gt;The first time I got the error &amp;ldquo;the trait &lt;code&gt;Clone&lt;/code&gt; cannot be made into an object,&amp;rdquo; I stared at it for a solid minute. Clone is one of the most fundamental traits in Rust. How can it not work with &lt;code&gt;dyn&lt;/code&gt;? The answer is &lt;em&gt;object safety&lt;/em&gt; — a set of rules that determine which traits can be used as trait objects. It&amp;rsquo;s one of those things that seems arbitrary until you understand &lt;em&gt;why&lt;/em&gt; the rules exist.&lt;/p&gt;</description></item><item><title>Lesson 7: dyn Trait — Runtime polymorphism and its cost</title><link>/post/rust/rust-traits-dynamic-dispatch/</link><pubDate>Sun, 23 Jun 2024 19:15:00 +0000</pubDate><guid>/post/rust/rust-traits-dynamic-dispatch/</guid><description>&lt;p&gt;Every Rust programmer hits this wall eventually. You have a &lt;code&gt;Vec&lt;/code&gt; and you want to put different types in it — all implementing the same trait, but different concrete types. You try &lt;code&gt;Vec&amp;lt;impl Trait&amp;gt;&lt;/code&gt; and the compiler says no. You try &lt;code&gt;Vec&amp;lt;T&amp;gt;&lt;/code&gt; with generics and realize &lt;code&gt;T&lt;/code&gt; can only be one type at a time. That&amp;rsquo;s when you discover &lt;code&gt;dyn Trait&lt;/code&gt;, and the first real tradeoff in Rust&amp;rsquo;s type system: static dispatch vs dynamic dispatch.&lt;/p&gt;</description></item><item><title>Lesson 6: Supertraits — Building trait hierarchies</title><link>/post/rust/rust-traits-supertraits/</link><pubDate>Fri, 21 Jun 2024 08:50:00 +0000</pubDate><guid>/post/rust/rust-traits-supertraits/</guid><description>&lt;p&gt;I had a trait called &lt;code&gt;Serialize&lt;/code&gt; (homebrew, pre-serde days) that needed to format things as strings. I kept calling &lt;code&gt;.to_string()&lt;/code&gt; inside default methods and wondering why the compiler complained. The type implementing my trait didn&amp;rsquo;t necessarily implement &lt;code&gt;Display&lt;/code&gt;. The fix was obvious in hindsight — make &lt;code&gt;Display&lt;/code&gt; a &lt;em&gt;supertrait&lt;/em&gt; of &lt;code&gt;Serialize&lt;/code&gt;. If you want to serialize, you must be displayable. Period.&lt;/p&gt;
&lt;p&gt;Supertraits let you build trait hierarchies where implementing one trait &lt;em&gt;requires&lt;/em&gt; implementing another.&lt;/p&gt;</description></item><item><title>Lesson 5: Associated Types vs Generic Parameters — Choosing the right tool</title><link>/post/rust/rust-traits-associated-types/</link><pubDate>Wed, 19 Jun 2024 16:05:00 +0000</pubDate><guid>/post/rust/rust-traits-associated-types/</guid><description>&lt;p&gt;Here&amp;rsquo;s a question that confused me for months: why does &lt;code&gt;Iterator&lt;/code&gt; use an associated type (&lt;code&gt;type Item&lt;/code&gt;) instead of a generic parameter (&lt;code&gt;Iterator&amp;lt;T&amp;gt;&lt;/code&gt;)? They look like they do the same thing. They kinda do. But the choice between them changes your entire API&amp;rsquo;s ergonomics, and picking wrong leads to annoying code downstream.&lt;/p&gt;
&lt;p&gt;The short answer: associated types mean &amp;ldquo;one implementation per type,&amp;rdquo; generic parameters mean &amp;ldquo;many implementations per type.&amp;rdquo; But the implications are deeper than that.&lt;/p&gt;</description></item><item><title>Lesson 15: When You're Fighting the Borrow Checker — Restructure, Don't Hack</title><link>/post/rust/rust-own-fighting-borrow-checker/</link><pubDate>Tue, 18 Jun 2024 12:38:00 +0000</pubDate><guid>/post/rust/rust-own-fighting-borrow-checker/</guid><description>&lt;p&gt;Every Rust developer has been there. You&amp;rsquo;re implementing something that feels simple. It works in your head. But the borrow checker says no. You try different approaches. More errors. You start adding &lt;code&gt;.clone()&lt;/code&gt; everywhere. You wrap things in &lt;code&gt;Rc&amp;lt;RefCell&amp;lt;...&amp;gt;&amp;gt;&lt;/code&gt;. You consider &lt;code&gt;unsafe&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Stop. Take a breath. The borrow checker isn&amp;rsquo;t wrong — your data flow is confused. And there&amp;rsquo;s almost always a clean restructuring that makes the error disappear.&lt;/p&gt;
&lt;p&gt;This lesson is a field guide. Real patterns that fight the borrow checker, and the restructurings that fix them.&lt;/p&gt;</description></item><item><title>Lesson 4: where Clauses — When bounds get complex</title><link>/post/rust/rust-traits-where-clauses/</link><pubDate>Mon, 17 Jun 2024 11:30:00 +0000</pubDate><guid>/post/rust/rust-traits-where-clauses/</guid><description>&lt;p&gt;You know that moment when a function signature gets so long it wraps three times in your editor and you can&amp;rsquo;t even find the return type? I hit that wall writing a generic cache layer that needed &lt;code&gt;Hash + Eq + Clone + Debug&lt;/code&gt; on the key, &lt;code&gt;Serialize + DeserializeOwned + Clone&lt;/code&gt; on the value, and &lt;code&gt;Display&lt;/code&gt; on both. The inline bounds turned my function signature into an unreadable mess.&lt;/p&gt;</description></item><item><title>Lesson 3: Trait Bounds — Constraining generic types</title><link>/post/rust/rust-traits-trait-bounds/</link><pubDate>Fri, 14 Jun 2024 21:10:00 +0000</pubDate><guid>/post/rust/rust-traits-trait-bounds/</guid><description>&lt;p&gt;I once wrote a generic function that looked perfectly reasonable — accepted any &lt;code&gt;T&lt;/code&gt;, did some work, returned a result. It compiled. Then I tried to actually &lt;em&gt;use&lt;/em&gt; it with a type that didn&amp;rsquo;t have &lt;code&gt;Clone&lt;/code&gt;, and suddenly the compiler was screaming at me with errors pointing at the function internals rather than the call site. That&amp;rsquo;s backwards. The fix? Trait bounds. Declare what you need upfront so the errors land where they belong.&lt;/p&gt;</description></item><item><title>Lesson 14: Pin and Unpin — Why Async Needs Them</title><link>/post/rust/rust-own-pin-unpin/</link><pubDate>Fri, 14 Jun 2024 20:05:00 +0000</pubDate><guid>/post/rust/rust-own-pin-unpin/</guid><description>&lt;p&gt;&lt;code&gt;Pin&lt;/code&gt; is the concept that makes experienced Rust developers pause. Not because it&amp;rsquo;s inherently complex — it&amp;rsquo;s actually a pretty small API — but because understanding &lt;em&gt;why&lt;/em&gt; it exists requires connecting several ideas: self-referential structs, async/await desugaring, and move semantics.&lt;/p&gt;
&lt;p&gt;I struggled with Pin for months. Then I understood what async does under the hood, and Pin suddenly made perfect sense. So that&amp;rsquo;s how I&amp;rsquo;m going to explain it.&lt;/p&gt;
&lt;h2 id="the-setup-what-async-really-does"&gt;The Setup: What Async Really Does&lt;/h2&gt;
&lt;p&gt;When you write an async function:&lt;/p&gt;</description></item><item><title>Lesson 2: Default Implementations and Selective Overrides — Don't repeat yourself</title><link>/post/rust/rust-traits-default-impl/</link><pubDate>Wed, 12 Jun 2024 14:45:00 +0000</pubDate><guid>/post/rust/rust-traits-default-impl/</guid><description>&lt;p&gt;Here&amp;rsquo;s something that bugged me when I first started with traits: I had six different types all implementing the same trait, and five of them had &lt;em&gt;identical&lt;/em&gt; method bodies. I was copying the same three lines into five &lt;code&gt;impl&lt;/code&gt; blocks like a human xerox machine. There had to be a better way.&lt;/p&gt;
&lt;p&gt;There is. Default implementations.&lt;/p&gt;
&lt;h2 id="the-basics"&gt;The Basics&lt;/h2&gt;
&lt;p&gt;A trait can provide a default body for any of its methods. Implementors can then choose to override it — or just accept the default:&lt;/p&gt;</description></item><item><title>Lesson 1: Trait Fundamentals — Defining shared behavior</title><link>/post/rust/rust-traits-fundamentals/</link><pubDate>Mon, 10 Jun 2024 09:22:00 +0000</pubDate><guid>/post/rust/rust-traits-fundamentals/</guid><description>&lt;p&gt;I spent my first month in Rust writing &lt;code&gt;impl&lt;/code&gt; blocks that looked suspiciously like Java interfaces. Copy-paste, copy-paste, tweak one method, ship it. Then I hit a wall — a refactor where I needed to swap out a storage backend, and every single call site had hardcoded the concrete type. That&amp;rsquo;s when traits stopped being &amp;ldquo;a feature I should learn&amp;rdquo; and became &amp;ldquo;the thing saving me from rewriting 4,000 lines.&amp;rdquo;&lt;/p&gt;</description></item><item><title>Lesson 13: Weak References — Breaking Cycles</title><link>/post/rust/rust-own-weak-references/</link><pubDate>Mon, 10 Jun 2024 09:15:00 +0000</pubDate><guid>/post/rust/rust-own-weak-references/</guid><description>&lt;p&gt;Reference counting has a fatal flaw: cycles. If A owns B and B owns A, neither reference count hits zero. The memory is leaked — not freed when it should be, not freed ever. In a garbage-collected language, the GC would detect this cycle and clean it up. Rust&amp;rsquo;s &lt;code&gt;Rc&lt;/code&gt;/&lt;code&gt;Arc&lt;/code&gt; can&amp;rsquo;t do that.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Weak&lt;/code&gt; references are the solution. And honestly, if you understand why they exist, you understand half of what makes garbage collectors complex.&lt;/p&gt;</description></item><item><title>Lesson 12: Rc and Arc — Shared Ownership</title><link>/post/rust/rust-own-rc-arc/</link><pubDate>Fri, 07 Jun 2024 17:50:00 +0000</pubDate><guid>/post/rust/rust-own-rc-arc/</guid><description>&lt;p&gt;Rust&amp;rsquo;s ownership model says every value has one owner. But what happens when multiple parts of your program genuinely need to own the same data? Trees with shared nodes. Graphs with cycles. Observer patterns. Caches shared between subsystems.&lt;/p&gt;
&lt;p&gt;Single ownership doesn&amp;rsquo;t fit everything. &lt;code&gt;Rc&lt;/code&gt; and &lt;code&gt;Arc&lt;/code&gt; are Rust&amp;rsquo;s answer — reference-counted smart pointers that enable shared ownership with deterministic cleanup.&lt;/p&gt;
&lt;h2 id="the-problem-multiple-owners"&gt;The Problem: Multiple Owners&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// This doesn&amp;#39;t work — who owns the shared config?
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ServiceA&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; config: &lt;span style="color:#a6e22e"&gt;AppConfig&lt;/span&gt;, &lt;span style="color:#75715e"&gt;// owns it
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ServiceB&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; config: &lt;span style="color:#a6e22e"&gt;AppConfig&lt;/span&gt;, &lt;span style="color:#75715e"&gt;// also owns a copy? expensive clone
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You could pass references, but then you&amp;rsquo;re dealing with lifetimes everywhere. And if the config needs to outlive any individual service, you need &lt;code&gt;'static&lt;/code&gt; or owned data.&lt;/p&gt;</description></item><item><title>Lesson 11: Interior Mutability — Cell, RefCell, and the Rules</title><link>/post/rust/rust-own-interior-mutability/</link><pubDate>Tue, 04 Jun 2024 11:30:00 +0000</pubDate><guid>/post/rust/rust-own-interior-mutability/</guid><description>&lt;p&gt;Rust&amp;rsquo;s borrowing rules say you can&amp;rsquo;t mutate through a shared reference. Period. Except — sometimes you need to. Caching, reference counting, lazy initialization, mock objects in tests. Legitimate use cases where the &amp;ldquo;no mutation through &lt;code&gt;&amp;amp;T&lt;/code&gt;&amp;rdquo; rule is too restrictive.&lt;/p&gt;
&lt;p&gt;Interior mutability is the escape hatch. It moves the borrow check from compile time to runtime. And yes, it can panic if you get it wrong. That&amp;rsquo;s the tradeoff.&lt;/p&gt;
&lt;h2 id="the-problem-mutation-through-self"&gt;The Problem: Mutation Through &amp;amp;self&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;re building a struct with a method that logically doesn&amp;rsquo;t modify the struct&amp;rsquo;s public state but needs to update some internal bookkeeping:&lt;/p&gt;</description></item><item><title>Lesson 10: Self-Referential Structs — The Problem and Solutions</title><link>/post/rust/rust-own-self-referential/</link><pubDate>Sun, 02 Jun 2024 15:40:00 +0000</pubDate><guid>/post/rust/rust-own-self-referential/</guid><description>&lt;p&gt;Every Rust developer eventually tries to build this: a struct that owns some data AND holds a reference into that data. It seems perfectly reasonable. It&amp;rsquo;s also the single thing Rust refuses to let you do safely — and for very good reasons.&lt;/p&gt;
&lt;p&gt;I wasted two days on this in my second month of Rust. Let me save you those two days.&lt;/p&gt;
&lt;h2 id="the-desire"&gt;The Desire&lt;/h2&gt;
&lt;p&gt;You want a struct like this:&lt;/p&gt;</description></item><item><title>Lesson 9: Higher-Ranked Trait Bounds — for&lt;'a&gt; Explained</title><link>/post/rust/rust-own-higher-ranked/</link><pubDate>Fri, 31 May 2024 21:15:00 +0000</pubDate><guid>/post/rust/rust-own-higher-ranked/</guid><description>&lt;p&gt;If regular lifetime annotations are Rust&amp;rsquo;s intermediate boss, &lt;code&gt;for&amp;lt;'a&amp;gt;&lt;/code&gt; is the final boss. Higher-Ranked Trait Bounds (HRTBs) look terrifying in signatures, but they solve a very specific and real problem.&lt;/p&gt;
&lt;p&gt;I avoided understanding HRTBs for over a year. Then I tried to write a function that takes a closure accepting references with &lt;em&gt;any&lt;/em&gt; lifetime, and suddenly I had no choice. Let me save you that year.&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Say you want a function that accepts a closure. The closure takes a &lt;code&gt;&amp;amp;str&lt;/code&gt; and returns something:&lt;/p&gt;</description></item><item><title>Lesson 8: 'static — Not What You Think</title><link>/post/rust/rust-own-static-lifetime/</link><pubDate>Wed, 29 May 2024 13:25:00 +0000</pubDate><guid>/post/rust/rust-own-static-lifetime/</guid><description>&lt;p&gt;&lt;code&gt;'static&lt;/code&gt; is the most misunderstood lifetime in Rust. Most people think it means &amp;ldquo;lives forever&amp;rdquo; or &amp;ldquo;global variable.&amp;rdquo; It kind of does — but also doesn&amp;rsquo;t. And the way it interacts with trait bounds will surprise you.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve seen experienced Rust developers get this wrong. So don&amp;rsquo;t feel bad if it&amp;rsquo;s been confusing.&lt;/p&gt;
&lt;h2 id="what-static-actually-means"&gt;What &amp;lsquo;static Actually Means&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;'static&lt;/code&gt; means: &amp;ldquo;this reference is valid for the entire duration of the program.&amp;rdquo;&lt;/p&gt;</description></item><item><title>Lesson 7: Lifetimes in Structs — References That Live in Types</title><link>/post/rust/rust-own-struct-lifetimes/</link><pubDate>Mon, 27 May 2024 10:42:00 +0000</pubDate><guid>/post/rust/rust-own-struct-lifetimes/</guid><description>&lt;p&gt;Putting a reference inside a struct is where lifetimes go from &amp;ldquo;mildly confusing&amp;rdquo; to &amp;ldquo;wait, what?&amp;rdquo; for most people. I remember spending an entire afternoon trying to make a struct hold a &lt;code&gt;&amp;amp;str&lt;/code&gt; and wondering why the compiler kept yelling at me.&lt;/p&gt;
&lt;p&gt;The fundamental tension: structs outlive function calls. References might not. The compiler needs you to prove the reference won&amp;rsquo;t dangle.&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The Problem&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// This won&amp;#39;t compile
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Excerpt&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; content: &lt;span style="color:#66d9ef"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;str&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;error[E0106]: missing lifetime specifier
 --&amp;gt; src/main.rs:2:14
 |
2 | content: &amp;amp;str,
 | ^ expected named lifetime parameter
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Why? Because the compiler needs to know: how long does the thing &lt;code&gt;content&lt;/code&gt; points to live? Without that information, it can&amp;rsquo;t guarantee the reference is valid for the struct&amp;rsquo;s entire lifetime.&lt;/p&gt;</description></item><item><title>Lesson 6: Lifetime Elision Rules — Why You Usually Don't Write 'a</title><link>/post/rust/rust-own-lifetime-elision/</link><pubDate>Sat, 25 May 2024 19:08:00 +0000</pubDate><guid>/post/rust/rust-own-lifetime-elision/</guid><description>&lt;p&gt;If lifetimes are so important, why don&amp;rsquo;t you see &lt;code&gt;'a&lt;/code&gt; plastered all over most Rust code? Because the compiler is smart enough to figure it out 90% of the time.&lt;/p&gt;
&lt;p&gt;Early Rust required explicit lifetime annotations on every function that dealt with references. The community quickly realized that the same patterns showed up over and over. So the Rust team codified those patterns into &amp;ldquo;elision rules&amp;rdquo; — three rules that let the compiler infer lifetimes automatically.&lt;/p&gt;</description></item><item><title>Lesson 5: Lifetime Annotations — Teaching the Compiler Relationships</title><link>/post/rust/rust-own-lifetime-annotations/</link><pubDate>Thu, 23 May 2024 08:55:00 +0000</pubDate><guid>/post/rust/rust-own-lifetime-annotations/</guid><description>&lt;p&gt;Lifetime annotations are where most people&amp;rsquo;s Rust learning hits a wall. The syntax looks alien. The error messages talk about &amp;ldquo;named lifetimes.&amp;rdquo; Your code compiles fine until you add a second reference parameter, and then suddenly the compiler wants you to annotate things with &lt;code&gt;'a&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the thing most tutorials get wrong: lifetime annotations don&amp;rsquo;t change how long anything lives. They describe relationships that already exist.&lt;/p&gt;
&lt;h2 id="why-lifetimes-exist"&gt;Why Lifetimes Exist&lt;/h2&gt;
&lt;p&gt;Consider this function:&lt;/p&gt;</description></item><item><title>Lesson 25: When to Reach for unsafe — And when not to</title><link>/post/rust/rust-idioms-unsafe-boundary/</link><pubDate>Wed, 22 May 2024 17:08:00 +0000</pubDate><guid>/post/rust/rust-idioms-unsafe-boundary/</guid><description>&lt;p&gt;This is the lesson I almost didn&amp;rsquo;t write. Not because &lt;code&gt;unsafe&lt;/code&gt; is hard to explain, but because most Rust developers will never need it — and I don&amp;rsquo;t want to encourage reaching for it prematurely. I&amp;rsquo;ve seen codebases riddled with &lt;code&gt;unsafe&lt;/code&gt; blocks because the developer didn&amp;rsquo;t know the safe alternative existed. That&amp;rsquo;s the worst outcome.&lt;/p&gt;
&lt;p&gt;But &lt;code&gt;unsafe&lt;/code&gt; exists for a reason. Understanding when it&amp;rsquo;s appropriate — and more importantly, when it isn&amp;rsquo;t — is part of being a competent Rust developer.&lt;/p&gt;</description></item><item><title>Lesson 4: The Borrow Checker — What It's Really Doing</title><link>/post/rust/rust-own-borrow-checker/</link><pubDate>Tue, 21 May 2024 16:33:00 +0000</pubDate><guid>/post/rust/rust-own-borrow-checker/</guid><description>&lt;p&gt;I used to think the borrow checker was my enemy. It rejected valid programs! It made simple things hard! It was too conservative!&lt;/p&gt;
&lt;p&gt;Then I started maintaining a large C++ codebase and found three use-after-free bugs in one week. The borrow checker stopped looking like an enemy real fast.&lt;/p&gt;
&lt;h2 id="borrowing-using-without-owning"&gt;Borrowing: Using Without Owning&lt;/h2&gt;
&lt;p&gt;Ownership transfer is clean but inflexible. You can&amp;rsquo;t always afford to give your data away — sometimes you just want to let another function &lt;em&gt;look at it&lt;/em&gt; for a moment. That&amp;rsquo;s borrowing.&lt;/p&gt;</description></item><item><title>Lesson 24: Writing Great Rust Documentation — rustdoc that actually helps</title><link>/post/rust/rust-idioms-documentation/</link><pubDate>Mon, 20 May 2024 14:30:00 +0000</pubDate><guid>/post/rust/rust-idioms-documentation/</guid><description>&lt;p&gt;The Rust ecosystem has some of the best documentation I&amp;rsquo;ve seen in any language. And it&amp;rsquo;s not an accident — the tooling actively &lt;em&gt;encourages&lt;/em&gt; good documentation. Doc comments are first-class citizens. Examples in docs are compiled and tested. The standard library sets an impossibly high bar that crate authors actually try to meet.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re publishing a crate and the docs are an afterthought, you&amp;rsquo;re doing it wrong. Documentation is the API.&lt;/p&gt;</description></item><item><title>Lesson 3: Copy vs Clone — When Data Gets Duplicated</title><link>/post/rust/rust-own-copy-clone/</link><pubDate>Sun, 19 May 2024 11:10:00 +0000</pubDate><guid>/post/rust/rust-own-copy-clone/</guid><description>&lt;p&gt;A coworker once asked me: &amp;ldquo;If Rust moves everything, how do I ever use a value twice?&amp;rdquo; Fair question. The answer is two traits that look similar but behave very differently — &lt;code&gt;Copy&lt;/code&gt; and &lt;code&gt;Clone&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Getting these confused will either tank your performance or confuse the hell out of you. Probably both.&lt;/p&gt;
&lt;h2 id="copy-implicit-cheap-and-bitwise"&gt;Copy: Implicit, Cheap, and Bitwise&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Copy&lt;/code&gt; is a marker trait. It tells the compiler: &amp;ldquo;this type is so cheap to duplicate that you should do it automatically on assignment instead of moving.&amp;rdquo;&lt;/p&gt;</description></item><item><title>Lesson 23: Clippy Is Your Mentor — Listen to it</title><link>/post/rust/rust-idioms-clippy/</link><pubDate>Sat, 18 May 2024 10:55:00 +0000</pubDate><guid>/post/rust/rust-idioms-clippy/</guid><description>&lt;p&gt;I&amp;rsquo;ll say something controversial: Clippy taught me more about idiomatic Rust than any book. Not because it explains concepts — it doesn&amp;rsquo;t. But because it catches you every time you write non-idiomatic code and shows you the better way. It&amp;rsquo;s like having a senior Rust developer looking over your shoulder, constantly saying &amp;ldquo;there&amp;rsquo;s a cleaner way to do that.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Run &lt;code&gt;cargo clippy&lt;/code&gt; on every project. Every commit. No exceptions.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="what-clippy-is"&gt;What Clippy Is&lt;/h2&gt;
&lt;p&gt;Clippy is Rust&amp;rsquo;s official linter — over 700 lint rules that catch everything from style issues to actual bugs. It ships with rustup, so you already have it.&lt;/p&gt;</description></item><item><title>Lesson 2: Move Semantics — Why Assignment Transfers Ownership</title><link>/post/rust/rust-own-move-semantics/</link><pubDate>Fri, 17 May 2024 14:45:00 +0000</pubDate><guid>/post/rust/rust-own-move-semantics/</guid><description>&lt;p&gt;The first time Rust told me I couldn&amp;rsquo;t use a variable after assigning it to another one, I stared at my screen for a solid minute. In what universe does &lt;code&gt;let b = a&lt;/code&gt; make &lt;code&gt;a&lt;/code&gt; invalid?&lt;/p&gt;
&lt;p&gt;This universe. Rust&amp;rsquo;s universe. And honestly — it&amp;rsquo;s the only sane default.&lt;/p&gt;
&lt;h2 id="what-a-move-actually-is"&gt;What a Move Actually Is&lt;/h2&gt;
&lt;p&gt;In most languages, &lt;code&gt;let b = a&lt;/code&gt; copies the data or copies a reference. In Rust, for heap-allocated types, it &lt;em&gt;moves&lt;/em&gt; the ownership. The bits get copied (the stack portion — pointer, length, capacity), but the old variable is invalidated.&lt;/p&gt;</description></item><item><title>Lesson 22: Feature Flags and Conditional Compilation — cfg and features</title><link>/post/rust/rust-idioms-cfg-features/</link><pubDate>Thu, 16 May 2024 12:45:00 +0000</pubDate><guid>/post/rust/rust-idioms-cfg-features/</guid><description>&lt;p&gt;I once worked on a crate that pulled in 200+ transitive dependencies because it unconditionally depended on &lt;code&gt;tokio&lt;/code&gt;, &lt;code&gt;serde&lt;/code&gt;, &lt;code&gt;reqwest&lt;/code&gt;, and &lt;code&gt;tracing&lt;/code&gt;. Most users only needed one of those. The compile time was brutal, and the binary was enormous.&lt;/p&gt;
&lt;p&gt;Feature flags solve this. They let users opt into functionality they need and skip everything else. It&amp;rsquo;s the &amp;ldquo;pay for what you use&amp;rdquo; principle applied to compilation.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="cfg--conditional-compilation"&gt;cfg — Conditional Compilation&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;#[cfg(...)]&lt;/code&gt; attribute conditionally includes code based on compile-time configuration:&lt;/p&gt;</description></item><item><title>Lesson 1: The Mental Model — Stack, Heap, and Ownership</title><link>/post/rust/rust-own-mental-model/</link><pubDate>Wed, 15 May 2024 09:22:00 +0000</pubDate><guid>/post/rust/rust-own-mental-model/</guid><description>&lt;p&gt;I spent my first three months in Rust confused about ownership. Not because the concept is hard — it&amp;rsquo;s genuinely simple — but because every tutorial I read explained it through the lens of &amp;ldquo;rules to memorize.&amp;rdquo; Three rules. Memorize them. Move on.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s backwards. You don&amp;rsquo;t learn ownership by memorizing rules. You learn it by understanding where your data actually lives.&lt;/p&gt;
&lt;h2 id="where-data-lives-changes-everything"&gt;Where Data Lives Changes Everything&lt;/h2&gt;
&lt;p&gt;Every value in your program sits in one of two places: the stack or the heap. If you&amp;rsquo;ve done any C or C++, this is familiar territory. If you haven&amp;rsquo;t — don&amp;rsquo;t worry, this isn&amp;rsquo;t as scary as systems programmers make it sound.&lt;/p&gt;</description></item><item><title>Lesson 21: The Turbofish ::&lt;&gt; — Explicit type parameters</title><link>/post/rust/rust-idioms-turbofish/</link><pubDate>Tue, 14 May 2024 08:22:00 +0000</pubDate><guid>/post/rust/rust-idioms-turbofish/</guid><description>&lt;p&gt;The first time I saw &lt;code&gt;::&amp;lt;&amp;gt;&lt;/code&gt; in Rust code, I thought someone was having a stroke at the keyboard. &lt;code&gt;collect::&amp;lt;Vec&amp;lt;_&amp;gt;&amp;gt;()&lt;/code&gt;? What is that colon-colon-angle-bracket monstrosity?&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s called the turbofish. Named by the community because &lt;code&gt;::&amp;lt;&amp;gt;&lt;/code&gt; looks like a fish (&lt;code&gt;::&amp;lt;&amp;gt;&lt;/code&gt; — see the eyes and the mouth?). It&amp;rsquo;s goofy. It&amp;rsquo;s lovable. And once you understand it, you&amp;rsquo;ll use it constantly.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="what-the-turbofish-does"&gt;What the Turbofish Does&lt;/h2&gt;
&lt;p&gt;The turbofish provides explicit type parameters to generic functions or methods when the compiler can&amp;rsquo;t infer them.&lt;/p&gt;</description></item><item><title>Lesson 20: API Design Guidelines — The Rust way</title><link>/post/rust/rust-idioms-api-design/</link><pubDate>Sun, 12 May 2024 16:40:00 +0000</pubDate><guid>/post/rust/rust-idioms-api-design/</guid><description>&lt;p&gt;I&amp;rsquo;ve written APIs in a dozen languages, and Rust is the only one where the community has near-universal agreement on how APIs should look. There&amp;rsquo;s an unofficial (but incredibly thorough) Rust API Guidelines document, and most popular crates follow it closely. When you learn these conventions, every new crate feels familiar.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what I&amp;rsquo;ve distilled from reading hundreds of crate APIs and writing a few of my own.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="naming-conventions"&gt;Naming Conventions&lt;/h2&gt;
&lt;p&gt;Rust has strong naming conventions, and deviating from them makes your crate feel alien.&lt;/p&gt;</description></item><item><title>Lesson 19: PhantomData — Tagging types without runtime cost</title><link>/post/rust/rust-idioms-phantom-data/</link><pubDate>Fri, 10 May 2024 09:15:00 +0000</pubDate><guid>/post/rust/rust-idioms-phantom-data/</guid><description>&lt;p&gt;When I first saw &lt;code&gt;PhantomData&lt;/code&gt; in a codebase, I thought it was some kind of hack. A zero-sized field that exists only to satisfy the compiler? It felt like a workaround for a language limitation. But the more I used it, the more I realized it&amp;rsquo;s actually a precision tool — it lets you encode information in the type system without any runtime cost.&lt;/p&gt;
&lt;p&gt;We already used it in the typestate lesson. Now let&amp;rsquo;s understand it properly.&lt;/p&gt;</description></item><item><title>Lesson 18: Drop and RAII — Deterministic cleanup</title><link>/post/rust/rust-idioms-drop/</link><pubDate>Wed, 08 May 2024 11:30:00 +0000</pubDate><guid>/post/rust/rust-idioms-drop/</guid><description>&lt;p&gt;In Go, you write &lt;code&gt;defer f.Close()&lt;/code&gt; and hope you didn&amp;rsquo;t forget one. In Python, you use &lt;code&gt;with&lt;/code&gt; blocks and hope the context manager is implemented correctly. In Java, you use try-with-resources and hope &lt;code&gt;AutoCloseable.close()&lt;/code&gt; doesn&amp;rsquo;t throw. In C, you write &lt;code&gt;free()&lt;/code&gt; and pray.&lt;/p&gt;
&lt;p&gt;In Rust, cleanup happens automatically when a value goes out of scope. Always. No exceptions. No forgetting. That&amp;rsquo;s RAII — Resource Acquisition Is Initialization — and the &lt;code&gt;Drop&lt;/code&gt; trait is how Rust implements it.&lt;/p&gt;</description></item><item><title>Lesson 17: Zero-Cost Abstractions — What it actually means</title><link>/post/rust/rust-idioms-zero-cost/</link><pubDate>Mon, 06 May 2024 14:52:00 +0000</pubDate><guid>/post/rust/rust-idioms-zero-cost/</guid><description>&lt;p&gt;&amp;ldquo;Zero-cost abstractions&amp;rdquo; is Rust&amp;rsquo;s most-repeated promise and its most-misunderstood concept. People hear &amp;ldquo;zero cost&amp;rdquo; and think &amp;ldquo;free.&amp;rdquo; It doesn&amp;rsquo;t mean free. It means: &lt;strong&gt;you don&amp;rsquo;t pay for what you don&amp;rsquo;t use, and what you do use, you couldn&amp;rsquo;t hand-code any better.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s a Bjarne Stroustrup quote, originally about C++. But Rust actually delivers on it in ways C++ often doesn&amp;rsquo;t.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="what-zero-cost-actually-means"&gt;What Zero-Cost Actually Means&lt;/h2&gt;
&lt;p&gt;Consider iterators. In Python, iterator chains create intermediate objects. In Java, streams can have overhead from boxing and virtual dispatch. In Rust:&lt;/p&gt;</description></item><item><title>Lesson 16: Enums Over Booleans — Make illegal states unrepresentable</title><link>/post/rust/rust-idioms-enums-over-bools/</link><pubDate>Sun, 05 May 2024 08:17:00 +0000</pubDate><guid>/post/rust/rust-idioms-enums-over-bools/</guid><description>&lt;p&gt;I once spent an entire afternoon debugging a function with this signature: &lt;code&gt;process(data, true, false, true)&lt;/code&gt;. Three booleans. What did they mean? I had to read the function definition every single time I encountered a call site. Is &lt;code&gt;true&lt;/code&gt; for &amp;ldquo;verbose&amp;rdquo;? For &amp;ldquo;dry-run&amp;rdquo;? For &amp;ldquo;force&amp;rdquo;?&lt;/p&gt;
&lt;p&gt;Booleans are the most overused type in programming. They encode exactly one bit of information — yes or no — and they tell you nothing about &lt;em&gt;what question they&amp;rsquo;re answering&lt;/em&gt;. Enums fix this.&lt;/p&gt;</description></item><item><title>Lesson 15: Exhaustive Matching — Let the compiler help</title><link>/post/rust/rust-idioms-exhaustive-matching/</link><pubDate>Fri, 03 May 2024 22:08:00 +0000</pubDate><guid>/post/rust/rust-idioms-exhaustive-matching/</guid><description>&lt;p&gt;A teammate once added a new payment method to our system — &lt;code&gt;CryptoCurrency&lt;/code&gt; — and forgot to update the fee calculation logic. In production. For three weeks. Nobody noticed because the switch statement in Java had a &lt;code&gt;default&lt;/code&gt; case that silently applied a 0% fee. Free crypto transfers for everyone.&lt;/p&gt;
&lt;p&gt;In Rust, the compiler would have caught this the moment the new variant was added. That&amp;rsquo;s exhaustive matching — and it&amp;rsquo;s one of Rust&amp;rsquo;s most underappreciated features.&lt;/p&gt;</description></item><item><title>Lesson 14: derive Is Your Best Friend — The macros you should always use</title><link>/post/rust/rust-idioms-derive/</link><pubDate>Thu, 02 May 2024 10:30:00 +0000</pubDate><guid>/post/rust/rust-idioms-derive/</guid><description>&lt;p&gt;I review a lot of Rust code, and one of my biggest pet peeves is types without &lt;code&gt;#[derive(Debug)]&lt;/code&gt;. You hit an error, you try to print the value, and you get that lovely message: &amp;ldquo;MyStruct doesn&amp;rsquo;t implement Debug.&amp;rdquo; Then you have to go add it, recompile, and try again.&lt;/p&gt;
&lt;p&gt;Just derive it from the start. Derive liberally. Your future self will thank you.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-derives-you-should-almost-always-use"&gt;The Derives You Should Almost Always Use&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s my standard starting point for any struct or enum:&lt;/p&gt;</description></item><item><title>Lesson 13: Display and Debug — Formatting done right</title><link>/post/rust/rust-idioms-display-debug/</link><pubDate>Tue, 30 Apr 2024 18:45:00 +0000</pubDate><guid>/post/rust/rust-idioms-display-debug/</guid><description>&lt;p&gt;Pop quiz: what&amp;rsquo;s the difference between &lt;code&gt;{}&lt;/code&gt; and &lt;code&gt;{:?}&lt;/code&gt; in a &lt;code&gt;println!&lt;/code&gt;? If your answer is &amp;ldquo;one looks prettier,&amp;rdquo; you&amp;rsquo;re not wrong — but you&amp;rsquo;re missing the bigger picture.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Display&lt;/code&gt; and &lt;code&gt;Debug&lt;/code&gt; serve fundamentally different audiences. &lt;code&gt;Display&lt;/code&gt; is for humans — end users, log readers, UI consumers. &lt;code&gt;Debug&lt;/code&gt; is for developers — it&amp;rsquo;s what you see in error messages, test failures, and debug sessions. Conflating the two leads to types that are either too verbose for users or too opaque for debugging.&lt;/p&gt;</description></item><item><title>Lesson 12: Designing Error Types — thiserror vs anyhow</title><link>/post/rust/rust-idioms-error-design/</link><pubDate>Mon, 29 Apr 2024 12:18:00 +0000</pubDate><guid>/post/rust/rust-idioms-error-design/</guid><description>&lt;p&gt;I&amp;rsquo;ve seen two extremes in Rust error handling. On one end: &lt;code&gt;Box&amp;lt;dyn std::error::Error&amp;gt;&lt;/code&gt; everywhere, a stringly-typed mess where you can&amp;rsquo;t distinguish a network timeout from a parse failure. On the other end: 47 custom error types with hand-written &lt;code&gt;Display&lt;/code&gt; and &lt;code&gt;From&lt;/code&gt; implementations, an over-engineered cathedral of boilerplate.&lt;/p&gt;
&lt;p&gt;The right answer is somewhere in the middle. And the two crates that get you there are &lt;code&gt;thiserror&lt;/code&gt; and &lt;code&gt;anyhow&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-wrong-ways"&gt;The Wrong Ways&lt;/h2&gt;
&lt;h3 id="wrong-way-1-string-errors"&gt;Wrong Way #1: String Errors&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;parse_config&lt;/span&gt;(input: &lt;span style="color:#66d9ef"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;str&lt;/span&gt;) -&amp;gt; Result&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;u16&lt;/span&gt;, String&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; port: &lt;span style="color:#66d9ef"&gt;u16&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; input.parse().map_err(&lt;span style="color:#f92672"&gt;|&lt;/span&gt;e&lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;format!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;bad port: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, e))&lt;span style="color:#f92672"&gt;?&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; port &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1024&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; Err(&lt;span style="color:#e6db74"&gt;&amp;#34;port must be &amp;gt;= 1024&amp;#34;&lt;/span&gt;.to_string());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Ok(port)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The caller gets a &lt;code&gt;String&lt;/code&gt;. What can they do with it? Print it. That&amp;rsquo;s about it. They can&amp;rsquo;t match on error variants, can&amp;rsquo;t programmatically decide how to handle different failures, can&amp;rsquo;t distinguish between &amp;ldquo;bad port&amp;rdquo; and &amp;ldquo;port too low.&amp;rdquo; It&amp;rsquo;s a dead end.&lt;/p&gt;</description></item><item><title>Lesson 11: Cow — Clone on write for flexible APIs</title><link>/post/rust/rust-idioms-cow/</link><pubDate>Sat, 27 Apr 2024 15:33:00 +0000</pubDate><guid>/post/rust/rust-idioms-cow/</guid><description>&lt;p&gt;There&amp;rsquo;s a function signature dilemma that every Rust developer hits: should this function return &lt;code&gt;&amp;amp;str&lt;/code&gt; or &lt;code&gt;String&lt;/code&gt;? If you return &lt;code&gt;&amp;amp;str&lt;/code&gt;, you avoid allocation — but sometimes you &lt;em&gt;need&lt;/em&gt; to create a new string. If you return &lt;code&gt;String&lt;/code&gt;, you allocate every time — even when you don&amp;rsquo;t need to.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Cow&lt;/code&gt; says: &amp;ldquo;Why not both?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The name stands for &amp;ldquo;Clone on Write,&amp;rdquo; and it&amp;rsquo;s one of those types that seems weird until you use it — and then you wonder how you lived without it.&lt;/p&gt;</description></item><item><title>Lesson 10: Deref Coercion — Why &amp;String works as &amp;str</title><link>/post/rust/rust-idioms-deref-coercion/</link><pubDate>Thu, 25 Apr 2024 21:10:00 +0000</pubDate><guid>/post/rust/rust-idioms-deref-coercion/</guid><description>&lt;p&gt;Here&amp;rsquo;s something that confused me for weeks when I started Rust: I&amp;rsquo;d write a function that takes &lt;code&gt;&amp;amp;str&lt;/code&gt;, and I could pass it a &lt;code&gt;&amp;amp;String&lt;/code&gt;. I&amp;rsquo;d write a function that takes &lt;code&gt;&amp;amp;[i32]&lt;/code&gt;, and I could pass it a &lt;code&gt;&amp;amp;Vec&amp;lt;i32&amp;gt;&lt;/code&gt;. I&amp;rsquo;d use &lt;code&gt;*&lt;/code&gt; to dereference a &lt;code&gt;Box&amp;lt;T&amp;gt;&lt;/code&gt; and get a &lt;code&gt;T&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;How? Why? The answer is &lt;code&gt;Deref&lt;/code&gt; coercion — one of Rust&amp;rsquo;s most elegant features, and one of the most poorly explained.&lt;/p&gt;</description></item><item><title>Lesson 9: From and Into — Seamless type conversions</title><link>/post/rust/rust-idioms-from-into/</link><pubDate>Wed, 24 Apr 2024 07:55:00 +0000</pubDate><guid>/post/rust/rust-idioms-from-into/</guid><description>&lt;p&gt;The first time I saw &lt;code&gt;.into()&lt;/code&gt; in Rust code, I was baffled. &amp;ldquo;Into &lt;em&gt;what&lt;/em&gt;?&amp;rdquo; There was no type annotation, no explicit conversion function, just &lt;code&gt;.into()&lt;/code&gt; hanging off a value like it knew exactly what to become. And somehow the compiler figured it out.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the &lt;code&gt;From&lt;/code&gt;/&lt;code&gt;Into&lt;/code&gt; trait pair. It&amp;rsquo;s one of the most-used patterns in idiomatic Rust, and once you understand it, your APIs will feel dramatically smoother.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-basics-from-and-into-are-mirror-images"&gt;The Basics: &lt;code&gt;From&lt;/code&gt; and &lt;code&gt;Into&lt;/code&gt; Are Mirror Images&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// If you implement From&amp;lt;A&amp;gt; for B...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;impl&lt;/span&gt; From&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;i32&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; MyType {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt;(value: &lt;span style="color:#66d9ef"&gt;i32&lt;/span&gt;) -&amp;gt; &lt;span style="color:#a6e22e"&gt;Self&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; MyType(value)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// ...you get Into&amp;lt;B&amp;gt; for A for free
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; x: &lt;span style="color:#a6e22e"&gt;MyType&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;42.&lt;/span&gt;into();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Always implement &lt;code&gt;From&lt;/code&gt;, never &lt;code&gt;Into&lt;/code&gt; directly.&lt;/strong&gt; The standard library provides a blanket implementation: if &lt;code&gt;From&amp;lt;A&amp;gt;&lt;/code&gt; exists for &lt;code&gt;B&lt;/code&gt;, then &lt;code&gt;Into&amp;lt;B&amp;gt;&lt;/code&gt; is automatically available for &lt;code&gt;A&lt;/code&gt;. Implementing &lt;code&gt;Into&lt;/code&gt; directly doesn&amp;rsquo;t give you the reverse.&lt;/p&gt;</description></item><item><title>Lesson 8: The Builder Pattern in Rust — Ergonomic construction</title><link>/post/rust/rust-idioms-builder/</link><pubDate>Mon, 22 Apr 2024 19:42:00 +0000</pubDate><guid>/post/rust/rust-idioms-builder/</guid><description>&lt;p&gt;Every Rust dev eventually runs into the &amp;ldquo;struct with 12 fields&amp;rdquo; problem. You&amp;rsquo;ve got a configuration type. Some fields are required, some are optional, some have sensible defaults. In Java, you&amp;rsquo;d use a Builder. In Python, you&amp;rsquo;d use keyword arguments. In Go, you&amp;rsquo;d use functional options.&lt;/p&gt;
&lt;p&gt;In Rust? You have options. And one of them is clearly better than the rest.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-problem-constructor-explosion"&gt;The Problem: Constructor Explosion&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ServerConfig&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; host: String,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; port: &lt;span style="color:#66d9ef"&gt;u16&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; max_connections: &lt;span style="color:#66d9ef"&gt;usize&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; timeout_secs: &lt;span style="color:#66d9ef"&gt;u64&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tls_enabled: &lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; log_level: String,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; workers: &lt;span style="color:#66d9ef"&gt;usize&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// This is painful
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; config &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ServerConfig {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; host: String::from(&lt;span style="color:#e6db74"&gt;&amp;#34;0.0.0.0&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; port: &lt;span style="color:#ae81ff"&gt;8080&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; max_connections: &lt;span style="color:#ae81ff"&gt;1000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; timeout_secs: &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tls_enabled: &lt;span style="color:#a6e22e"&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; log_level: String::from(&lt;span style="color:#e6db74"&gt;&amp;#34;info&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; workers: &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Every field must be specified. There&amp;rsquo;s no concept of &amp;ldquo;default.&amp;rdquo; If you add a new field later, every construction site breaks. And callers have no idea which values are &amp;ldquo;important&amp;rdquo; and which are &amp;ldquo;just use the default.&amp;rdquo;&lt;/p&gt;</description></item><item><title>Lesson 7: The Newtype Pattern — Type safety for free</title><link>/post/rust/rust-idioms-newtype/</link><pubDate>Sun, 21 Apr 2024 13:15:00 +0000</pubDate><guid>/post/rust/rust-idioms-newtype/</guid><description>&lt;p&gt;True story: a Mars orbiter was lost because one team used pounds and another used newtons. Same numeric type, different semantic meanings. $327 million, gone.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;d think we&amp;rsquo;d have learned. But I still see codebases where user IDs, product IDs, and order IDs are all &lt;code&gt;i64&lt;/code&gt;. Where distances are &lt;code&gt;f64&lt;/code&gt; whether they&amp;rsquo;re meters or feet. Where an email address is just a &lt;code&gt;String&lt;/code&gt; that you hope someone validated.&lt;/p&gt;
&lt;p&gt;The newtype pattern fixes this. It gives you type safety with zero runtime cost.&lt;/p&gt;</description></item><item><title>Lesson 6: The Typestate Pattern — Compile-time state machines</title><link>/post/rust/rust-idioms-type-state/</link><pubDate>Fri, 19 Apr 2024 10:28:00 +0000</pubDate><guid>/post/rust/rust-idioms-type-state/</guid><description>&lt;p&gt;I once spent two days debugging a production issue where someone called &lt;code&gt;.send()&lt;/code&gt; on an HTTP request builder &lt;em&gt;before&lt;/em&gt; setting the URL. The code compiled fine — it was a runtime error that only surfaced under specific conditions. In Python. In Java. In Go. This kind of bug is everywhere.&lt;/p&gt;
&lt;p&gt;In Rust, you can make it literally impossible to compile. The typestate pattern encodes state transitions into the type system. If the state machine says you can&amp;rsquo;t send before setting a URL, the compiler enforces it. Not with runtime checks. Not with assertions. With &lt;em&gt;types&lt;/em&gt;.&lt;/p&gt;</description></item><item><title>Lesson 25: Where to Go from Here — Your Rust learning path</title><link>/post/rust/rust-scratch-whats-next/</link><pubDate>Thu, 18 Apr 2024 18:00:00 +0000</pubDate><guid>/post/rust/rust-scratch-whats-next/</guid><description>&lt;p&gt;You&amp;rsquo;ve made it through 24 lessons. You understand ownership, borrowing, structs, enums, traits, generics, error handling, closures, iterators, and file I/O. That&amp;rsquo;s not nothing — that&amp;rsquo;s the foundation of every Rust program ever written. But foundations are for building on. Here&amp;rsquo;s where to go from here.&lt;/p&gt;
&lt;h2 id="what-you-know-now"&gt;What You Know Now&lt;/h2&gt;
&lt;p&gt;Take a second to appreciate what you&amp;rsquo;ve learned. You can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Set up a Rust project with Cargo&lt;/li&gt;
&lt;li&gt;Write functions with proper ownership and borrowing&lt;/li&gt;
&lt;li&gt;Model data with structs and enums&lt;/li&gt;
&lt;li&gt;Handle errors with Result and the &lt;code&gt;?&lt;/code&gt; operator&lt;/li&gt;
&lt;li&gt;Use collections: Vec, HashMap, HashSet&lt;/li&gt;
&lt;li&gt;Write generic code with trait bounds&lt;/li&gt;
&lt;li&gt;Process data with iterators and closures&lt;/li&gt;
&lt;li&gt;Test your code with &lt;code&gt;#[test]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Read and write files&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&amp;rsquo;s a real skill set. You can build useful things right now. Before diving into advanced topics, I strongly recommend you build something. Nothing fancy — a command-line tool, a file processor, a simple data structure. The gap between &amp;ldquo;I understand the concepts&amp;rdquo; and &amp;ldquo;I can write a program&amp;rdquo; is only bridged by practice.&lt;/p&gt;</description></item><item><title>Lesson 5: Iterator Chains Over Manual Loops — Functional Rust</title><link>/post/rust/rust-idioms-iterator-chains/</link><pubDate>Thu, 18 Apr 2024 16:03:00 +0000</pubDate><guid>/post/rust/rust-idioms-iterator-chains/</guid><description>&lt;p&gt;I used to write Rust like I was still writing C — &lt;code&gt;for&lt;/code&gt; loops everywhere, mutable accumulators, index variables. The code worked, but it was &lt;em&gt;loud&lt;/em&gt;. Five lines to express what should be one. Then I started using iterator chains, and honestly? I&amp;rsquo;m never going back.&lt;/p&gt;
&lt;p&gt;Iterator chains aren&amp;rsquo;t just syntactic sugar. They&amp;rsquo;re often &lt;em&gt;faster&lt;/em&gt; than hand-written loops because the compiler can optimize them better. Zero-cost abstractions in action.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-imperative-way-vs-the-idiomatic-way"&gt;The Imperative Way vs The Idiomatic Way&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s a task: given a list of strings, find all that start with &amp;ldquo;error:&amp;rdquo;, strip the prefix, trim whitespace, and collect the results.&lt;/p&gt;</description></item><item><title>Lesson 24: Reading and Writing Files — Real I/O in Rust</title><link>/post/rust/rust-scratch-file-io/</link><pubDate>Tue, 16 Apr 2024 13:45:00 +0000</pubDate><guid>/post/rust/rust-scratch-file-io/</guid><description>&lt;p&gt;Every tutorial teaches you to manipulate data in memory, but real programs read from files and write to files. Rust&amp;rsquo;s file I/O is built on the same ownership and error handling principles you&amp;rsquo;ve been learning — and once you see how &lt;code&gt;Result&lt;/code&gt;, iterators, and traits come together, the entire language design starts to feel cohesive.&lt;/p&gt;
&lt;h2 id="reading-a-file--the-quick-way"&gt;Reading a File — The Quick Way&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;use&lt;/span&gt; std::fs;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;match&lt;/span&gt; fs::read_to_string(&lt;span style="color:#e6db74"&gt;&amp;#34;hello.txt&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Ok(content) &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;File contents:&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{content}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Err(e) &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;eprintln!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Error reading file: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{e}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;fs::read_to_string&lt;/code&gt; reads the entire file into a &lt;code&gt;String&lt;/code&gt;. It returns &lt;code&gt;Result&amp;lt;String, io::Error&amp;gt;&lt;/code&gt; — because file operations can fail (file doesn&amp;rsquo;t exist, permission denied, disk error, etc.).&lt;/p&gt;</description></item><item><title>Lesson 4: if let and while let — Concise pattern matching</title><link>/post/rust/rust-idioms-if-let-while-let/</link><pubDate>Tue, 16 Apr 2024 08:45:00 +0000</pubDate><guid>/post/rust/rust-idioms-if-let-while-let/</guid><description>&lt;p&gt;There&amp;rsquo;s a special kind of code smell that I call &amp;ldquo;match bloat&amp;rdquo; — when you write a six-line &lt;code&gt;match&lt;/code&gt; statement to handle a single variant and throw an underscore wildcard on everything else. I wrote hundreds of these before discovering that Rust has a better way.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re writing &lt;code&gt;match&lt;/code&gt; just to handle one case, you&amp;rsquo;re doing too much work.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-problem-match-bloat"&gt;The Problem: Match Bloat&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s code I see all the time, especially from developers coming from the previous lesson on &lt;code&gt;Option&lt;/code&gt; and &lt;code&gt;Result&lt;/code&gt;:&lt;/p&gt;</description></item><item><title>Lesson 3: Option and Result Are Your Control Flow — Stop using sentinel values</title><link>/post/rust/rust-idioms-option-result/</link><pubDate>Sun, 14 Apr 2024 11:22:00 +0000</pubDate><guid>/post/rust/rust-idioms-option-result/</guid><description>&lt;p&gt;I once inherited a C codebase where &lt;code&gt;-1&lt;/code&gt; meant &amp;ldquo;not found,&amp;rdquo; &lt;code&gt;0&lt;/code&gt; meant &amp;ldquo;error,&amp;rdquo; and &lt;code&gt;NULL&lt;/code&gt; meant&amp;hellip; well, it depended on the function. Sometimes it meant &amp;ldquo;empty,&amp;rdquo; sometimes &amp;ldquo;uninitialized,&amp;rdquo; sometimes &amp;ldquo;we ran out of memory.&amp;rdquo; The codebase had roughly 40 unique sentinel values across different modules, and half the bugs were someone forgetting which magic number meant what.&lt;/p&gt;
&lt;p&gt;Rust looked at that mess and said: &amp;ldquo;How about we just&amp;hellip; don&amp;rsquo;t.&amp;rdquo;&lt;/p&gt;</description></item><item><title>Lesson 23: Writing Your First Tests — #[test] and assert!</title><link>/post/rust/rust-scratch-testing/</link><pubDate>Sun, 14 Apr 2024 11:00:00 +0000</pubDate><guid>/post/rust/rust-scratch-testing/</guid><description>&lt;p&gt;I have a rule: I won&amp;rsquo;t merge code without tests. Not because I&amp;rsquo;m a purist — because I&amp;rsquo;ve been burned too many times. Rust makes testing so frictionless that there&amp;rsquo;s no excuse to skip it. Tests live in the same file as your code. They run with one command. The tooling just works.&lt;/p&gt;
&lt;h2 id="your-first-test"&gt;Your First Test&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;add&lt;/span&gt;(a: &lt;span style="color:#66d9ef"&gt;i32&lt;/span&gt;, b: &lt;span style="color:#66d9ef"&gt;i32&lt;/span&gt;) -&amp;gt; &lt;span style="color:#66d9ef"&gt;i32&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; a &lt;span style="color:#f92672"&gt;+&lt;/span&gt; b
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#[cfg(test)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;mod&lt;/span&gt; tests {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;use&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;super&lt;/span&gt;::&lt;span style="color:#f92672"&gt;*&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;#[test]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_add&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;assert_eq!&lt;/span&gt;(add(&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;), &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;#[test]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_add_negative&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;assert_eq!&lt;/span&gt;(add(&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;), &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;#[test]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_add_zero&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;assert_eq!&lt;/span&gt;(add(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;), &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Run with &lt;code&gt;cargo test&lt;/code&gt;:&lt;/p&gt;</description></item><item><title>Lesson 2: Borrow Strategically — &amp;T, &amp;mut T, and when to clone</title><link>/post/rust/rust-idioms-borrowing-patterns/</link><pubDate>Fri, 12 Apr 2024 14:37:00 +0000</pubDate><guid>/post/rust/rust-idioms-borrowing-patterns/</guid><description>&lt;p&gt;A colleague once showed me their Rust code where every function parameter was &lt;code&gt;String&lt;/code&gt; — never &lt;code&gt;&amp;amp;str&lt;/code&gt;, never &lt;code&gt;&amp;amp;String&lt;/code&gt;. When I asked why, they said &amp;ldquo;the borrow checker kept yelling at me, so I just take ownership of everything.&amp;rdquo; Classic.&lt;/p&gt;
&lt;p&gt;I get it. The borrow checker can feel like an overzealous hall monitor. But once you understand the borrowing rules — really understand them — you&amp;rsquo;ll realize it&amp;rsquo;s not restricting you. It&amp;rsquo;s showing you a better design.&lt;/p&gt;</description></item><item><title>Lesson 22: Iterators — Lazy, composable data processing</title><link>/post/rust/rust-scratch-iterators/</link><pubDate>Fri, 12 Apr 2024 09:30:00 +0000</pubDate><guid>/post/rust/rust-scratch-iterators/</guid><description>&lt;p&gt;I once profiled a Go service and found it was spending 40% of CPU time allocating intermediate slices in a data pipeline. Each transformation created a new slice, copied data, processed it, then created another. In Rust, iterator chains do the same transformation with zero intermediate allocations. The data flows through the pipeline element by element, transformed in place. That&amp;rsquo;s not just elegant — it&amp;rsquo;s measurably faster.&lt;/p&gt;
&lt;h2 id="the-iterator-trait"&gt;The Iterator Trait&lt;/h2&gt;
&lt;p&gt;At its core, an iterator is any type that implements the &lt;code&gt;Iterator&lt;/code&gt; trait:&lt;/p&gt;</description></item><item><title>Lesson 21: Closures — Functions that capture</title><link>/post/rust/rust-scratch-closures/</link><pubDate>Wed, 10 Apr 2024 12:15:00 +0000</pubDate><guid>/post/rust/rust-scratch-closures/</guid><description>&lt;p&gt;Closures are where Rust stops feeling like a systems language and starts feeling like a functional one. You&amp;rsquo;ve already been using them — every time you passed &lt;code&gt;|x| x * 2&lt;/code&gt; to &lt;code&gt;.map()&lt;/code&gt; or &lt;code&gt;|a, b| a.cmp(b)&lt;/code&gt; to &lt;code&gt;.sort_by()&lt;/code&gt;, that was a closure. Time to understand what&amp;rsquo;s actually happening under the hood.&lt;/p&gt;
&lt;h2 id="what-is-a-closure"&gt;What Is a Closure?&lt;/h2&gt;
&lt;p&gt;A closure is an anonymous function that can capture variables from its surrounding scope:&lt;/p&gt;</description></item><item><title>Lesson 1: Think in Ownership — Not pointers</title><link>/post/rust/rust-idioms-ownership-thinking/</link><pubDate>Wed, 10 Apr 2024 09:14:00 +0000</pubDate><guid>/post/rust/rust-idioms-ownership-thinking/</guid><description>&lt;p&gt;I spent three months fighting the borrow checker before I realized the problem wasn&amp;rsquo;t the borrow checker — it was me. I kept thinking in C++ pointers and Java references. Every &lt;code&gt;String&lt;/code&gt; was just &amp;ldquo;data on the heap.&amp;rdquo; Every function call was &amp;ldquo;passing a reference.&amp;rdquo; And every compiler error felt like Rust was being unreasonable.&lt;/p&gt;
&lt;p&gt;It wasn&amp;rsquo;t. I was just thinking about memory the wrong way.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-cjava-mental-model-and-why-it-fails"&gt;The C++/Java Mental Model (And Why It Fails)&lt;/h2&gt;
&lt;p&gt;If you come from C++, your brain maps everything to raw pointers, smart pointers, or references. If you come from Java/Python/Go, everything is a reference behind the scenes — you never think about who owns what, because the GC handles it.&lt;/p&gt;</description></item><item><title>Lesson 20: Generics — Writing code that works for any type</title><link>/post/rust/rust-scratch-generics-intro/</link><pubDate>Mon, 08 Apr 2024 20:00:00 +0000</pubDate><guid>/post/rust/rust-scratch-generics-intro/</guid><description>&lt;p&gt;I used to think generics were fancy academic stuff that you&amp;rsquo;d rarely need in practice. Then I wrote my third function that was identical to a previous one except it operated on &lt;code&gt;f64&lt;/code&gt; instead of &lt;code&gt;i32&lt;/code&gt;. Generics aren&amp;rsquo;t luxury features — they&amp;rsquo;re how you stop writing the same function five times for five different types.&lt;/p&gt;
&lt;h2 id="the-problem-generics-solve"&gt;The Problem Generics Solve&lt;/h2&gt;
&lt;p&gt;Without generics, you write duplicate code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;largest_i32&lt;/span&gt;(list: &lt;span style="color:#66d9ef"&gt;&amp;amp;&lt;/span&gt;[&lt;span style="color:#66d9ef"&gt;i32&lt;/span&gt;]) -&amp;gt; &lt;span style="color:#66d9ef"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;i32&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;mut&lt;/span&gt; largest &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;list[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; item &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt; list {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; item &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; largest {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; largest &lt;span style="color:#f92672"&gt;=&lt;/span&gt; item;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; largest
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;largest_f64&lt;/span&gt;(list: &lt;span style="color:#66d9ef"&gt;&amp;amp;&lt;/span&gt;[&lt;span style="color:#66d9ef"&gt;f64&lt;/span&gt;]) -&amp;gt; &lt;span style="color:#66d9ef"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;f64&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;mut&lt;/span&gt; largest &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;list[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; item &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt; list {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; item &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; largest {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; largest &lt;span style="color:#f92672"&gt;=&lt;/span&gt; item;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; largest
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; ints &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;vec!&lt;/span&gt;[&lt;span style="color:#ae81ff"&gt;34&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;50&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;25&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;65&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; floats &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;vec!&lt;/span&gt;[&lt;span style="color:#ae81ff"&gt;3.4&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;5.0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;2.5&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;10.0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;6.5&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Largest int: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, largest_i32(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;ints));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Largest float: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, largest_f64(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;floats));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These functions are identical except for the type. Generics let you write it once:&lt;/p&gt;</description></item><item><title>Lesson 19: Traits — Your first abstraction</title><link>/post/rust/rust-scratch-traits-intro/</link><pubDate>Sun, 07 Apr 2024 15:45:00 +0000</pubDate><guid>/post/rust/rust-scratch-traits-intro/</guid><description>&lt;p&gt;Traits are the mechanism I miss most when I leave Rust. They&amp;rsquo;re interfaces without inheritance, type classes without the math, and the foundation of every abstraction in the language. If structs define what data &lt;em&gt;is&lt;/em&gt;, traits define what data &lt;em&gt;does&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="what-is-a-trait"&gt;What Is a Trait?&lt;/h2&gt;
&lt;p&gt;A trait defines a set of methods that a type can implement:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;trait&lt;/span&gt; Greet {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hello&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;self) -&amp;gt; String;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Person&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: String,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Robot&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; id: &lt;span style="color:#66d9ef"&gt;u32&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;impl&lt;/span&gt; Greet &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; Person {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hello&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;self) -&amp;gt; String {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;format!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Hi, I&amp;#39;m &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;!&amp;#34;&lt;/span&gt;, self.name)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;impl&lt;/span&gt; Greet &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; Robot {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hello&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;self) -&amp;gt; String {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;format!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;UNIT-&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; OPERATIONAL&amp;#34;&lt;/span&gt;, self.id)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; person &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Person { name: String::from(&lt;span style="color:#e6db74"&gt;&amp;#34;Alice&amp;#34;&lt;/span&gt;) };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; robot &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Robot { id: &lt;span style="color:#ae81ff"&gt;42&lt;/span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, person.hello());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, robot.hello());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The trait &lt;code&gt;Greet&lt;/code&gt; declares that any implementing type must have a &lt;code&gt;hello&lt;/code&gt; method. &lt;code&gt;Person&lt;/code&gt; and &lt;code&gt;Robot&lt;/code&gt; each provide their own implementation. Different types, same interface.&lt;/p&gt;</description></item><item><title>Lesson 18: Crates, Cargo.toml, and Dependencies — The Rust ecosystem</title><link>/post/rust/rust-scratch-crates/</link><pubDate>Fri, 05 Apr 2024 10:30:00 +0000</pubDate><guid>/post/rust/rust-scratch-crates/</guid><description>&lt;p&gt;The first time I added a dependency in Rust, I was shocked. Add one line to Cargo.toml, run &lt;code&gt;cargo build&lt;/code&gt;, and it downloads, compiles, and links everything automatically. Coming from C++ where dependency management is a special circle of hell, Cargo felt like cheating.&lt;/p&gt;
&lt;h2 id="what-is-a-crate"&gt;What Is a Crate?&lt;/h2&gt;
&lt;p&gt;A crate is a compilation unit in Rust — the smallest amount of code the compiler considers at a time. There are two kinds:&lt;/p&gt;</description></item><item><title>Lesson 17: Modules — Organizing your code</title><link>/post/rust/rust-scratch-modules/</link><pubDate>Wed, 03 Apr 2024 14:00:00 +0000</pubDate><guid>/post/rust/rust-scratch-modules/</guid><description>&lt;p&gt;Rust&amp;rsquo;s module system confused me for longer than I&amp;rsquo;d like to admit. I came from Go, where packages map directly to directories. Rust&amp;rsquo;s module system is more flexible — and more confusing as a result. But once you understand the mental model, it&amp;rsquo;s actually quite elegant. The key insight: the file system doesn&amp;rsquo;t define the module tree. You do.&lt;/p&gt;
&lt;h2 id="modules-in-a-single-file"&gt;Modules in a Single File&lt;/h2&gt;
&lt;p&gt;The simplest case — modules defined inline:&lt;/p&gt;</description></item><item><title>Lesson 16: Result and the ? Operator — Errors as values</title><link>/post/rust/rust-scratch-error-handling/</link><pubDate>Mon, 01 Apr 2024 08:45:00 +0000</pubDate><guid>/post/rust/rust-scratch-error-handling/</guid><description>&lt;p&gt;Exception-based error handling has a fundamental flaw: you can&amp;rsquo;t tell by looking at a function signature whether it might throw. Go fixed this by returning &lt;code&gt;(value, error)&lt;/code&gt; tuples, but then you&amp;rsquo;re back to forgetting to check the error. Rust&amp;rsquo;s &lt;code&gt;Result&lt;/code&gt; type gets it right — errors are values, and the type system makes them impossible to ignore.&lt;/p&gt;
&lt;h2 id="the-result-type"&gt;The Result Type&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Built into the language:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// enum Result&amp;lt;T, E&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Ok(T),
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Err(E),
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;Result&amp;lt;T, E&amp;gt;&lt;/code&gt; is either &lt;code&gt;Ok(T)&lt;/code&gt; (success with a value) or &lt;code&gt;Err(E)&lt;/code&gt; (failure with an error). You&amp;rsquo;ve seen &lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt; — &lt;code&gt;Result&lt;/code&gt; is similar but carries error information when something goes wrong.&lt;/p&gt;</description></item><item><title>Lesson 15: Vec, HashMap, and HashSet — The collections you'll use daily</title><link>/post/rust/rust-scratch-collections/</link><pubDate>Sat, 30 Mar 2024 11:15:00 +0000</pubDate><guid>/post/rust/rust-scratch-collections/</guid><description>&lt;p&gt;In my experience, about 80% of all data structures in real programs are either lists or key-value maps. Rust nails both of them. &lt;code&gt;Vec&lt;/code&gt; and &lt;code&gt;HashMap&lt;/code&gt; are fast, safe, and ergonomic — and once you know these two plus &lt;code&gt;HashSet&lt;/code&gt;, you can build almost anything.&lt;/p&gt;
&lt;h2 id="vec--the-dynamic-array"&gt;Vec — The Dynamic Array&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Vec&amp;lt;T&amp;gt;&lt;/code&gt; is Rust&amp;rsquo;s growable array. It stores elements contiguously on the heap, like &lt;code&gt;ArrayList&lt;/code&gt; in Java or &lt;code&gt;std::vector&lt;/code&gt; in C++.&lt;/p&gt;</description></item><item><title>Lesson 14: Methods and Associated Functions — impl blocks explained</title><link>/post/rust/rust-scratch-methods/</link><pubDate>Thu, 28 Mar 2024 17:30:00 +0000</pubDate><guid>/post/rust/rust-scratch-methods/</guid><description>&lt;p&gt;In most object-oriented languages, data and behavior are bundled together inside classes. Rust separates them — you define data with &lt;code&gt;struct&lt;/code&gt; (or &lt;code&gt;enum&lt;/code&gt;) and attach behavior with &lt;code&gt;impl&lt;/code&gt; blocks. This separation is cleaner than it sounds. You can add methods to a type from anywhere, not just its original definition. And there&amp;rsquo;s no inheritance tax.&lt;/p&gt;
&lt;h2 id="your-first-impl-block"&gt;Your First impl Block&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Rectangle&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; width: &lt;span style="color:#66d9ef"&gt;f64&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; height: &lt;span style="color:#66d9ef"&gt;f64&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;impl&lt;/span&gt; Rectangle {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;area&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;self) -&amp;gt; &lt;span style="color:#66d9ef"&gt;f64&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self.width &lt;span style="color:#f92672"&gt;*&lt;/span&gt; self.height
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;perimeter&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;self) -&amp;gt; &lt;span style="color:#66d9ef"&gt;f64&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;2.0&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt; (self.width &lt;span style="color:#f92672"&gt;+&lt;/span&gt; self.height)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;is_square&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;self) -&amp;gt; &lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (self.width &lt;span style="color:#f92672"&gt;-&lt;/span&gt; self.height).abs() &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;f64&lt;/span&gt;::&lt;span style="color:#66d9ef"&gt;EPSILON&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; rect &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Rectangle { width: &lt;span style="color:#ae81ff"&gt;10.0&lt;/span&gt;, height: &lt;span style="color:#ae81ff"&gt;5.0&lt;/span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Area: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, rect.area());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Perimeter: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, rect.perimeter());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Is square: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, rect.is_square());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;impl Rectangle&lt;/code&gt; block defines methods for the &lt;code&gt;Rectangle&lt;/code&gt; type. Inside the block, &lt;code&gt;&amp;amp;self&lt;/code&gt; is a reference to the instance the method is called on. It&amp;rsquo;s equivalent to &lt;code&gt;self: &amp;amp;Self&lt;/code&gt;, where &lt;code&gt;Self&lt;/code&gt; is an alias for &lt;code&gt;Rectangle&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Lesson 13: Pattern Matching — The match superpower</title><link>/post/rust/rust-scratch-pattern-matching/</link><pubDate>Tue, 26 Mar 2024 09:45:00 +0000</pubDate><guid>/post/rust/rust-scratch-pattern-matching/</guid><description>&lt;p&gt;Pattern matching ruined switch statements for me. After using &lt;code&gt;match&lt;/code&gt; in Rust for a few months, going back to C-style switch/case feels like using a butter knife to do surgery. The compiler checks that you&amp;rsquo;ve covered every case. It destructures data inline. It does not fall through. It is, hands down, the most elegant control flow construct I&amp;rsquo;ve ever used.&lt;/p&gt;
&lt;h2 id="basic-match"&gt;Basic match&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; x &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;match&lt;/span&gt; x {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;one&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;two&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;three&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _ &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;something else&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;_&lt;/code&gt; is the catch-all pattern — it matches anything. Think of it as the &lt;code&gt;default&lt;/code&gt; case in a switch statement, except it&amp;rsquo;s mandatory when the other arms don&amp;rsquo;t cover all possibilities.&lt;/p&gt;</description></item><item><title>Lesson 12: Enums and Option — Null safety by design</title><link>/post/rust/rust-scratch-enums/</link><pubDate>Mon, 25 Mar 2024 12:00:00 +0000</pubDate><guid>/post/rust/rust-scratch-enums/</guid><description>&lt;p&gt;Tony Hoare called null his &amp;ldquo;billion-dollar mistake.&amp;rdquo; He invented it in 1965 and has publicly apologized for it multiple times since. Rust took him seriously. There is no null in Rust — and the replacement is so much better that going back to languages with null feels like giving up a superpower.&lt;/p&gt;
&lt;h2 id="basic-enums"&gt;Basic Enums&lt;/h2&gt;
&lt;p&gt;At their simplest, enums define a type that can be one of several variants:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;enum&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Direction&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; North,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; South,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; East,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; West,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;describe&lt;/span&gt;(dir: &lt;span style="color:#66d9ef"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Direction&lt;/span&gt;) -&amp;gt; &lt;span style="color:#66d9ef"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;str&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;match&lt;/span&gt; dir {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Direction::North &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;heading north&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Direction::South &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;heading south&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Direction::East &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;heading east&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Direction::West &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;heading west&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; d &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Direction::North;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{:?}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, d, describe(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;d));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If this is all enums did, they&amp;rsquo;d be equivalent to C enums. Mildly useful. Not exciting.&lt;/p&gt;</description></item><item><title>Lesson 11: Structs — Modeling your domain</title><link>/post/rust/rust-scratch-structs/</link><pubDate>Sat, 23 Mar 2024 20:15:00 +0000</pubDate><guid>/post/rust/rust-scratch-structs/</guid><description>&lt;p&gt;I worked on a Go codebase once where someone had passed around a &lt;code&gt;map[string]interface{}&lt;/code&gt; for user data. It was fine until someone misspelled &amp;ldquo;email&amp;rdquo; as &amp;ldquo;emial&amp;rdquo; and we spent half a day tracking down why emails weren&amp;rsquo;t sending. Structs are how you prevent this entire category of mistake — named fields with typed data, checked at compile time.&lt;/p&gt;
&lt;h2 id="defining-a-struct"&gt;Defining a Struct&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;User&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: String,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; email: String,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; age: &lt;span style="color:#66d9ef"&gt;u32&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; active: &lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; user &lt;span style="color:#f92672"&gt;=&lt;/span&gt; User {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: String::from(&lt;span style="color:#e6db74"&gt;&amp;#34;Alice&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; email: String::from(&lt;span style="color:#e6db74"&gt;&amp;#34;alice@example.com&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; age: &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; active: &lt;span style="color:#a6e22e"&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; (&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;) - age &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, user.name, user.email, user.age);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Struct names are &lt;code&gt;PascalCase&lt;/code&gt;. Field names are &lt;code&gt;snake_case&lt;/code&gt;. These aren&amp;rsquo;t suggestions — the compiler warns you if you deviate.&lt;/p&gt;</description></item><item><title>Lesson 10: Strings — String vs &amp;str and why it matters</title><link>/post/rust/rust-scratch-strings/</link><pubDate>Fri, 22 Mar 2024 15:30:00 +0000</pubDate><guid>/post/rust/rust-scratch-strings/</guid><description>&lt;p&gt;Strings are the #1 source of confusion for Rust beginners. I see the same questions every week: &amp;ldquo;Why are there two string types?&amp;rdquo; &amp;ldquo;Why can&amp;rsquo;t I index a string?&amp;rdquo; &amp;ldquo;Why is this so much harder than in Python?&amp;rdquo; It&amp;rsquo;s not harder — it&amp;rsquo;s &lt;em&gt;more honest&lt;/em&gt;. Other languages hide the complexity of text. Rust makes you deal with it.&lt;/p&gt;
&lt;h2 id="the-two-string-types"&gt;The Two String Types&lt;/h2&gt;
&lt;p&gt;Rust has two main string types:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;String&lt;/code&gt;&lt;/strong&gt; — owned, heap-allocated, growable, mutable&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;&amp;amp;str&lt;/code&gt;&lt;/strong&gt; — borrowed, a slice/view into string data, immutable (usually)&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; owned: String &lt;span style="color:#f92672"&gt;=&lt;/span&gt; String::from(&lt;span style="color:#e6db74"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;); &lt;span style="color:#75715e"&gt;// heap-allocated, you own it
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; borrowed: &lt;span style="color:#66d9ef"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;str&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// string literal, embedded in binary
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{owned}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{borrowed}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The relationship between &lt;code&gt;String&lt;/code&gt; and &lt;code&gt;&amp;amp;str&lt;/code&gt; is exactly like &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;amp;[u8]&lt;/code&gt;. A &lt;code&gt;String&lt;/code&gt; is a buffer you own. A &lt;code&gt;&amp;amp;str&lt;/code&gt; is a view into someone else&amp;rsquo;s string data (or a literal baked into your binary).&lt;/p&gt;</description></item><item><title>Lesson 9: Slices — Views into data</title><link>/post/rust/rust-scratch-slices/</link><pubDate>Wed, 20 Mar 2024 10:45:00 +0000</pubDate><guid>/post/rust/rust-scratch-slices/</guid><description>&lt;p&gt;Slices clicked for me when I stopped thinking of them as a language feature and started thinking of them as a design pattern: &amp;ldquo;here&amp;rsquo;s a window into someone else&amp;rsquo;s data.&amp;rdquo; They&amp;rsquo;re one of Rust&amp;rsquo;s most elegant ideas, and you&amp;rsquo;ll use them everywhere.&lt;/p&gt;
&lt;h2 id="what-is-a-slice"&gt;What Is a Slice?&lt;/h2&gt;
&lt;p&gt;A slice is a reference to a contiguous sequence of elements in a collection. It doesn&amp;rsquo;t own the data — it&amp;rsquo;s a view into data owned by something else.&lt;/p&gt;</description></item><item><title>Lesson 8: Borrowing and References — Sharing without giving</title><link>/post/rust/rust-scratch-borrowing/</link><pubDate>Mon, 18 Mar 2024 13:00:00 +0000</pubDate><guid>/post/rust/rust-scratch-borrowing/</guid><description>&lt;p&gt;After the last lesson, you might be thinking &amp;ldquo;so every function I call takes my data and I never see it again?&amp;rdquo; That would be terrible. Borrowing is the answer — it lets you share data without transferring ownership, and it&amp;rsquo;s where Rust&amp;rsquo;s safety guarantees really shine.&lt;/p&gt;
&lt;h2 id="references-with-"&gt;References with &amp;amp;&lt;/h2&gt;
&lt;p&gt;A reference lets you refer to a value without owning it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;print_length&lt;/span&gt;(s: &lt;span style="color:#66d9ef"&gt;&amp;amp;&lt;/span&gt;String) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Length of &amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, s, s.len());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; s &lt;span style="color:#f92672"&gt;=&lt;/span&gt; String::from(&lt;span style="color:#e6db74"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print_length(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;s); &lt;span style="color:#75715e"&gt;// lend s to the function
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;I still own: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{s}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;); &lt;span style="color:#75715e"&gt;// s is still valid
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;&amp;amp;&lt;/code&gt; creates a reference. &lt;code&gt;&amp;amp;s&lt;/code&gt; doesn&amp;rsquo;t move &lt;code&gt;s&lt;/code&gt; — it creates a pointer to &lt;code&gt;s&lt;/code&gt; that the function can use. When the function returns, the reference goes away but &lt;code&gt;s&lt;/code&gt; remains untouched.&lt;/p&gt;</description></item><item><title>Lesson 7: Ownership — The rule that changes everything</title><link>/post/rust/rust-scratch-ownership/</link><pubDate>Sat, 16 Mar 2024 08:30:00 +0000</pubDate><guid>/post/rust/rust-scratch-ownership/</guid><description>&lt;p&gt;I spent three hours fighting the borrow checker on my first real Rust project. I was furious. Then I realized every single error the compiler flagged was a genuine bug — a use-after-free, a data race, a dangling reference. The compiler wasn&amp;rsquo;t being difficult. It was saving me from myself.&lt;/p&gt;
&lt;h2 id="why-ownership-exists"&gt;Why Ownership Exists&lt;/h2&gt;
&lt;p&gt;In C, you allocate memory and free it manually. Forget to free? Memory leak. Free twice? Undefined behavior. Use after free? Crash (if you&amp;rsquo;re lucky) or silent corruption (if you&amp;rsquo;re not).&lt;/p&gt;</description></item><item><title>Lesson 6: Control Flow — if, loop, while, for — and why there's no ternary</title><link>/post/rust/rust-scratch-control-flow/</link><pubDate>Thu, 14 Mar 2024 19:10:00 +0000</pubDate><guid>/post/rust/rust-scratch-control-flow/</guid><description>&lt;p&gt;I once reviewed a pull request that had seven levels of nested if-else. In Go. The author said &amp;ldquo;the language doesn&amp;rsquo;t give me better tools.&amp;rdquo; In Rust, you&amp;rsquo;ve got &lt;code&gt;match&lt;/code&gt;, labeled loops, &lt;code&gt;if let&lt;/code&gt;, and blocks-as-expressions — enough to keep your code flat and readable even when the logic is complex.&lt;/p&gt;
&lt;h2 id="if--else"&gt;if / else&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; temperature &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;35&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; temperature &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;It&amp;#39;s hot&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; temperature &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;It&amp;#39;s nice&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;It&amp;#39;s cold&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Standard stuff. No parentheses around the condition — that&amp;rsquo;s a syntax error in Rust. The braces are mandatory, even for single-line bodies. No arguing about whether to use braces. They&amp;rsquo;re required. Discussion over.&lt;/p&gt;</description></item><item><title>Lesson 5: Functions, Expressions, and Statements — Everything is an expression</title><link>/post/rust/rust-scratch-functions/</link><pubDate>Wed, 13 Mar 2024 10:20:00 +0000</pubDate><guid>/post/rust/rust-scratch-functions/</guid><description>&lt;p&gt;When I first read that &amp;ldquo;everything in Rust is an expression,&amp;rdquo; I thought it was marketing fluff. It&amp;rsquo;s not. It&amp;rsquo;s a genuine design principle that affects how you write code every single day, and once you internalize it, going back to statement-heavy languages feels clunky.&lt;/p&gt;
&lt;h2 id="defining-functions"&gt;Defining Functions&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;add&lt;/span&gt;(a: &lt;span style="color:#66d9ef"&gt;i32&lt;/span&gt;, b: &lt;span style="color:#66d9ef"&gt;i32&lt;/span&gt;) -&amp;gt; &lt;span style="color:#66d9ef"&gt;i32&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; a &lt;span style="color:#f92672"&gt;+&lt;/span&gt; b
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; result &lt;span style="color:#f92672"&gt;=&lt;/span&gt; add(&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;7&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;3 + 7 = &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{result}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A few things to notice:&lt;/p&gt;</description></item><item><title>Lesson 4: Variables, Mutability, and Primitive Types — Let, let mut, and the type system</title><link>/post/rust/rust-scratch-variables-types/</link><pubDate>Mon, 11 Mar 2024 16:45:00 +0000</pubDate><guid>/post/rust/rust-scratch-variables-types/</guid><description>&lt;p&gt;Coming from JavaScript, I was stunned the first time Rust refused to compile because I tried to reassign a variable. &amp;ldquo;What do you mean it&amp;rsquo;s immutable by default? Who designs a language like that?&amp;rdquo; Turns out — people who&amp;rsquo;ve debugged enough mutable state to know better.&lt;/p&gt;
&lt;h2 id="variables-with-let"&gt;Variables with let&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; x &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;x is &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{x}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;let&lt;/code&gt; creates a variable binding. It binds a name to a value. By default, that binding is &lt;strong&gt;immutable&lt;/strong&gt; — you can&amp;rsquo;t change it.&lt;/p&gt;</description></item><item><title>Lesson 3: Hello, World — Anatomy of a Rust program</title><link>/post/rust/rust-scratch-first-program/</link><pubDate>Sat, 09 Mar 2024 11:00:00 +0000</pubDate><guid>/post/rust/rust-scratch-first-program/</guid><description>&lt;p&gt;The first program I ever wrote was in BASIC on a Commodore 64 emulator. Took me twenty minutes to figure out why PRINT didn&amp;rsquo;t work (I was typing PIRNT). Rust&amp;rsquo;s version of Hello World looks simple — four lines — but there&amp;rsquo;s a surprising amount of language design packed into those four lines.&lt;/p&gt;
&lt;h2 id="the-program"&gt;The Program&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Hello, world!&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. Three lines if you&amp;rsquo;re counting. But each piece tells you something about how Rust thinks.&lt;/p&gt;</description></item><item><title>Lesson 2: Installing Rust — rustup, cargo, and your first build</title><link>/post/rust/rust-scratch-toolchain/</link><pubDate>Thu, 07 Mar 2024 14:30:00 +0000</pubDate><guid>/post/rust/rust-scratch-toolchain/</guid><description>&lt;p&gt;I&amp;rsquo;ve installed Rust on maybe fifty machines at this point — Linux servers, Macs, Windows boxes, even a Raspberry Pi. The process has gotten remarkably smooth. Rustup is one of the best toolchain managers in any language ecosystem, and I say that without hesitation.&lt;/p&gt;
&lt;h2 id="installing-rustup"&gt;Installing rustup&lt;/h2&gt;
&lt;p&gt;Rustup is the official Rust toolchain installer and manager. It handles downloading the compiler, updating it, and switching between versions. One command gets you everything.&lt;/p&gt;</description></item><item><title>Lesson 1: Why Rust Exists — And why you should care</title><link>/post/rust/rust-scratch-why-rust/</link><pubDate>Tue, 05 Mar 2024 09:15:00 +0000</pubDate><guid>/post/rust/rust-scratch-why-rust/</guid><description>&lt;p&gt;I mass-deleted a production database once because a C program I&amp;rsquo;d written had a use-after-free bug that corrupted a pointer used for query routing. Took us fourteen hours to recover. That was the week I started learning Rust.&lt;/p&gt;
&lt;h2 id="the-problem-rust-solves"&gt;The Problem Rust Solves&lt;/h2&gt;
&lt;p&gt;Every few years, someone publishes a study on CVEs in major software projects. The numbers are always the same: roughly 70% of critical security vulnerabilities in C and C++ codebases are memory safety issues. Buffer overflows, use-after-free, double-free, null pointer dereferences. The same bugs, decade after decade.&lt;/p&gt;</description></item><item><title>Mastering Rust: The Complete Guide to Pattern Matching</title><link>/post/rust/mastering-rust-the-complete-guide-to-pattern-matching/</link><pubDate>Thu, 29 Feb 2024 18:30:00 +0000</pubDate><guid>/post/rust/mastering-rust-the-complete-guide-to-pattern-matching/</guid><description>&lt;p&gt;&lt;img src="/images/Default_create_rust_programming_ferris_the_crab_logo_4.webp" alt=""&gt;&lt;br&gt;
Looking to take your Rust skills to the next level? Master the art of pattern matching, one of Rust&amp;rsquo;s most versatile features! This beginner-friendly guide dives into the key concepts with clear examples, helping you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Understand the basics of advanced pattern matching in Rust.&lt;/li&gt;
&lt;li&gt;Apply these techniques to write cleaner, more efficient code.&lt;/li&gt;
&lt;li&gt;Avoid common pitfalls and best practices to follow.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="1-basics-of-pattern-matching"&gt;1. Basics of Pattern Matching&lt;/h2&gt;
&lt;h4 id="11-what-is-pattern-matching"&gt;1.1 What is Pattern Matching?&lt;/h4&gt;
&lt;p&gt;Imagine a toolbox filled with different tools for different tasks. Pattern matching works similarly, allowing you to compare your data against various &amp;ldquo;patterns&amp;rdquo; and execute the exact code you need based on the match. This brings more flexibility and security compared to traditional &amp;ldquo;if-else&amp;rdquo; statements in other languages.&lt;br&gt;
&lt;br&gt;
&lt;strong&gt;Getting Started with match:&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Traits: Mastering Traits in Rust — Navigating Edge Cases and Best Practices (Part 3)</title><link>/post/rust/traits-mastering-traits-in-rustnavigating-edge-cases-and-best-practices-part3/</link><pubDate>Thu, 29 Feb 2024 18:30:00 +0000</pubDate><guid>/post/rust/traits-mastering-traits-in-rustnavigating-edge-cases-and-best-practices-part3/</guid><description>&lt;hr&gt;
&lt;h3&gt;&lt;img src="/images/traits.webp" alt=""&gt;&lt;/h3&gt;
&lt;p&gt;Hello again, Rust enthusiasts! We’ve journeyed through the foundational concepts and dived into the advanced territories of Rust’s trait system in our previous posts.&lt;/p&gt;
&lt;p&gt;Today, we’re at the final frontier, ready to tackle the intricacies of mastering traits in Rust. This installment is all about navigating through edge cases, understanding best practices, and making the most out of Rust’s powerful trait system. So, let’s get started and wrap up our series with a deep dive into the art of mastering Rust traits.&lt;/p&gt;</description></item><item><title>From Novice to Master: 10 Must-Try Low-Level Programming Projects in Rust</title><link>/post/rust/from-novice-to-master-10-must-try-low-level-programming-projects-in-rust/</link><pubDate>Wed, 28 Feb 2024 18:30:00 +0000</pubDate><guid>/post/rust/from-novice-to-master-10-must-try-low-level-programming-projects-in-rust/</guid><description>&lt;hr&gt;
&lt;h3&gt;&lt;img src="/images/crab.webp" alt=""&gt;&lt;/h3&gt;
&lt;p&gt;Hey there! If you’ve been following along, you know we’ve been deep-diving into Rust, exploring its nooks and crannies. This time around, I’m switching gears to something more hands-on. I want to walk you through a curated list of projects that have not only sharpened my skills but promise to elevate yours from beginner to pro.&lt;/p&gt;
&lt;h3 id="1-building-a-guessing-game-with-atwist"&gt;1. Building a Guessing Game with a Twist&lt;/h3&gt;
&lt;p&gt;Alright, let&amp;rsquo;s dive into something that&amp;rsquo;s simple at first glance but has layers to peel back—a guessing game, but not just any guessing game. We&amp;rsquo;re adding a twist to make it more engaging and a tad more complex, perfect for getting your feet wet with Rust&amp;rsquo;s more nuanced features.&lt;/p&gt;</description></item><item><title>Traits: Advanced Trait Concepts and Dynamic Dispatch in Rust (Part 2)</title><link>/post/rust/traits-part2-advance-trait-concepts/</link><pubDate>Wed, 28 Feb 2024 18:30:00 +0000</pubDate><guid>/post/rust/traits-part2-advance-trait-concepts/</guid><description>&lt;hr&gt;
&lt;h3 id="trait-objects-and-dynamicdispatch"&gt;&lt;img src="/images/traits.webp" alt=""&gt; Trait Objects and Dynamic Dispatch&lt;/h3&gt;
&lt;p&gt;In Rust, polymorphism achieved through traits can take two forms: static and dynamic dispatch. Static dispatch is like knowing exactly what tool you’re going to use for a job, making it fast and efficient. Dynamic dispatch, on the other hand, is more flexible, allowing you to choose the right tool while the job is already underway.&lt;/p&gt;
&lt;h3 id="understanding-trait-objects-and-dynkeyword"&gt;Understanding Trait Objects and dyn Keyword&lt;/h3&gt;
&lt;p&gt;Trait objects with the dyn keyword allow for this kind of runtime flexibility. Imagine you&amp;rsquo;re a chef with a set of kitchen tools (each representing a type). Each tool has a special function, like chopping, stirring, or scooping. In Rust, these functions are like traits, and the tools are the types that implement these traits.&lt;/p&gt;</description></item><item><title>Traits: Understanding Rust Traits  - The Foundation (Part 1)</title><link>/post/rust/traits-understanding-rust-traitsthe-foundation-part1/</link><pubDate>Wed, 28 Feb 2024 18:30:00 +0000</pubDate><guid>/post/rust/traits-understanding-rust-traitsthe-foundation-part1/</guid><description>&lt;hr&gt;
&lt;p&gt;&lt;img src="/images/traits.webp" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Welcome, fellow Rustaceans and curious minds! Today, we embark on the first installment of our series designed to explore Rust’s powerful trait system. Traits are at the heart of Rust’s type system, offering a flexible way to define shared behavior. In this post, we’ll lay the foundation, exploring what traits are, how they’re used, and why they’re so integral to Rust programming. Grab a cup of your favorite brew, and let’s dive in!&lt;/p&gt;</description></item><item><title>Mastering Rust Lifetimes: The Comprehensive Guide</title><link>/post/rust/mastering-rust-lifetimes-the-comprehensive-guide/</link><pubDate>Fri, 23 Feb 2024 18:30:00 +0000</pubDate><guid>/post/rust/mastering-rust-lifetimes-the-comprehensive-guide/</guid><description>&lt;h1&gt;Mastering Rust Lifetimes: The Comprehensive Guide&lt;br&gt;
&lt;img src="/images/rust-lifetime.webp" alt=""&gt;&lt;/h1&gt;
&lt;p&gt;Understanding lifetimes in Rust is crucial for any Rustacean aiming to write safe and efficient code. Lifetimes are Rust&amp;rsquo;s unique approach to managing memory without a garbage collector, ensuring memory safety and eliminating data races. This guide will take you from the basics to more nuanced aspects of lifetimes, with plenty of examples to solidify your understanding.&lt;/p&gt;
&lt;h2 id="part-1-the-foundations-of-lifetimes"&gt;Part 1: The Foundations of Lifetimes&lt;/h2&gt;
&lt;h3 id="what-are-lifetimes"&gt;What Are Lifetimes?&lt;/h3&gt;
&lt;p&gt;In Rust, every reference has a lifetime, which is the scope for which that reference is valid. Lifetimes ensure that references do not outlive the data they refer to, preventing dangling references and ensuring data race freedom.&lt;/p&gt;</description></item><item><title>Rust Ultimate: The Ultimate Rust Cheatsheet You'll Ever Need</title><link>/post/rust/rust-ultimate-the-ultimate-rust-cheatsheet-youll-ever-need/</link><pubDate>Tue, 20 Feb 2024 18:30:00 +0000</pubDate><guid>/post/rust/rust-ultimate-the-ultimate-rust-cheatsheet-youll-ever-need/</guid><description>&lt;p&gt;&lt;img src="/images/cheatsheet.webp" alt=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;
Whether you&amp;rsquo;re a seasoned developer juggling multiple programming languages or a newcomer to Rust, it&amp;rsquo;s not uncommon to hit a roadblock trying to recall specific syntax or optimize your code with Rust&amp;rsquo;s powerful features. This comprehensive Rust cheatsheet is designed to be your go-to reference, enabling you to harness Rust&amp;rsquo;s capabilities fully without getting slowed down by syntax uncertainties.&lt;/p&gt;
&lt;h2 id="vector--linkedlist-operations"&gt;Vector &amp;amp; LinkedList Operations&lt;/h2&gt;
&lt;p&gt;Rust&amp;rsquo;s Vec&amp;lt;T&amp;gt; and LinkedList&amp;lt;T&amp;gt; are versatile for handling collections. Here&amp;rsquo;s how to use them effectively:&lt;/p&gt;</description></item></channel></rss>