<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Predrag Gruevski&#x27;s blog and personal site.</title>
    <subtitle>Predrag Gruevski&#x27;s blog and personal site.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://predr.ag/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://predr.ag"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-04-01T00:00:00+00:00</updated>
    <id>https://predr.ag/atom.xml</id>
    <entry xml:lang="en">
        <title>The Self-Cancelling Subscription</title>
        <published>2026-04-01T00:00:00+00:00</published>
        <updated>2026-04-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/the-self-cancelling-subscription/"/>
        <id>https://predr.ag/blog/the-self-cancelling-subscription/</id>
        
        <summary type="html">&lt;p&gt;&lt;em&gt;Happy April 1st! This post is part of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aprilcools.club&#x2F;&quot;&gt;April Cools Club&lt;&#x2F;a&gt;: an April 1st effort to publish genuine essays on unexpected topics. Please enjoy this true story, and rest assured that the tech content will be back soon!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;One Friday night a few months ago, my family and I sat down to relax and enjoy a TV show on our streaming platform of choice. The subscription was a perk of one of our credit cards, and we had been satisfied customers for several months.&lt;&#x2F;p&gt;
&lt;p&gt;This time was different. Instead of a &quot;Continue watching&quot; button, we saw &quot;Start your free trial.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;Our streaming subscription had been deactivated.&amp;hellip;
&lt;&#x2F;p&gt;
</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Exponential growth continued — cargo-semver-checks 2025 Year in Review</title>
        <published>2026-01-11T00:00:00+00:00</published>
        <updated>2026-01-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/cargo-semver-checks-2025-year-in-review/"/>
        <id>https://predr.ag/blog/cargo-semver-checks-2025-year-in-review/</id>
        
        <summary type="html">&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;cargo-semver-checks-2024-year-in-review&#x2F;&quot;&gt;Last year&#x27;s annual review post&lt;&#x2F;a&gt; observed that &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt;&#x27; lint library is undergoing exponential growth, doubling each year: 30 lints at the end of 2022, 57 lints in 2023, and 120 at the end of 2024. We bring 2025 to a close with 242 lints, more than doubling last year&#x27;s total — and that&#x27;s just one facet of what we accomplished. Let&#x27;s look at the full picture, and the path for 2026 and beyond!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Ghosts in the Compilation</title>
        <published>2025-10-31T00:00:00+00:00</published>
        <updated>2025-10-31T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/ghosts-in-the-compilation/"/>
        <id>https://predr.ag/blog/ghosts-in-the-compilation/</id>
        
        <summary type="html">&lt;p&gt;&lt;em&gt;Recently, a &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; user reached out with a conundrum: &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; reported being unable to build their crate, but when they ran &lt;code&gt;cargo check&lt;&#x2F;code&gt; themselves, it always completed successfully. Something cursed was going on! The fix is part of the upcoming &lt;code&gt;cargo-semver-checks v0.45&lt;&#x2F;code&gt; release—what better day than Halloween to talk about this ghost story!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Unsoundness and accidental features in the &lt;nobr&gt;&lt;code&gt;#[target_feature]&lt;&#x2F;code&gt;&lt;&#x2F;nobr&gt; attribute</title>
        <published>2025-07-05T00:00:00+00:00</published>
        <updated>2025-07-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/unsoundness-and-accidental-features-in-target-feature/"/>
        <id>https://predr.ag/blog/unsoundness-and-accidental-features-in-target-feature/</id>
        
        <summary type="html">&lt;p&gt;&lt;em&gt;Researching the SemVer hazards of the &lt;code&gt;#[target_feature]&lt;&#x2F;code&gt; attribute led to finding unexpected unsoundness, discovering an &quot;accidental feature&quot; in Rust, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;issues&#x2F;142655&quot;&gt;finding bugs&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;issues&#x2F;142952&quot;&gt;in rustdoc&lt;&#x2F;a&gt;, the creation of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rfcs&#x2F;pull&#x2F;3820&quot;&gt;an RFC that evolves the Rust language&lt;&#x2F;a&gt;, and the addition of a dozen new SemVer lints. My work on &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; benefits the Rust ecosystem in more ways than just preventing breakage!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>XORry Not Sorry: The Most Amusing Security Flaws I&#x27;ve Discovered</title>
        <published>2025-04-01T00:00:00+00:00</published>
        <updated>2025-04-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/xorry-not-sorry-most-amusing-security-flaws-ive-discovered/"/>
        <id>https://predr.ag/blog/xorry-not-sorry-most-amusing-security-flaws-ive-discovered/</id>
        
        <summary type="html">&lt;p&gt;&lt;em&gt;Happy April 1st! This post is part of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aprilcools.club&#x2F;&quot;&gt;April Cools Club&lt;&#x2F;a&gt;: an April 1st effort to publish genuine essays on unexpected topics. Please enjoy these true stories, and rest assured that the tech content will be back soon!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The mouse started moving. Not the one on the desk, the pointer on the screen! First to the left, then down to the bottom corner.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;*Click!*&lt;&#x2F;em&gt; The Windows Start menu came up.&lt;&#x2F;p&gt;
&lt;p&gt;&quot;&lt;em&gt;cmd&lt;&#x2F;em&gt;&quot; wrote a silent hand on an invisible keyboard. It downloaded a file from a random-looking URL, then executed it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;*Pop*&lt;&#x2F;em&gt; went both the computer and the illusion of security.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>When is &quot;this trait can be implemented&quot; part of the trait&#x27;s public API?</title>
        <published>2025-03-08T00:00:00+00:00</published>
        <updated>2025-03-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/when-is-trait-can-be-implemented-public-api/"/>
        <id>https://predr.ag/blog/when-is-trait-can-be-implemented-public-api/</id>
        
        <summary type="html">&lt;p&gt;&lt;em&gt;&lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; v0.40 ships a massive upgrade to its system for detecting sealed traits. The new system is an all-around win-win: it improves the accuracy of a dozen existing lints, enables a new series of helpful lints, handles cyclic trait relationships, and is also faster than the old system. All that took a lot of work! Here&#x27;s a look at how we made it happen.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>&quot;We never update unless forced to&quot; — cargo-semver-checks 2024 Year in Review</title>
        <published>2025-01-21T00:00:00+00:00</published>
        <updated>2025-01-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/cargo-semver-checks-2024-year-in-review/"/>
        <id>https://predr.ag/blog/cargo-semver-checks-2024-year-in-review/</id>
        
        <summary type="html">&lt;p&gt;&lt;em&gt;&lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; ends 2024 having improved dramatically over the course of the year: 12 new releases featuring 63 new lints, with 1175 merged PRs from 57 authors across the many repos that make the project tick. Let&#x27;s recap what we learned, the biggest things we shipped, and the facets of the project that made it to the conference and podcast circuits.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Breakage! in the Cargo.toml — How Rust Package Features Work (And Break)</title>
        <published>2024-12-04T00:00:00+00:00</published>
        <updated>2024-12-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/breakage-in-the-cargo-toml-how-rust-package-features-work/"/>
        <id>https://predr.ag/blog/breakage-in-the-cargo-toml-how-rust-package-features-work/</id>
        
        <summary type="html">&lt;p&gt;&lt;em&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;releases&#x2F;tag&#x2F;v0.37.0&quot;&gt;&lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; v0.37&lt;&#x2F;a&gt; can now scan &lt;code&gt;Cargo.toml&lt;&#x2F;code&gt; files for breakage! In this post: a primer on Rust package features, and how innocuous-looking &lt;code&gt;Cargo.toml&lt;&#x2F;code&gt; changes can break your users.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Is this trait sealed, or not sealed — that is the question</title>
        <published>2024-09-03T00:00:00+00:00</published>
        <updated>2024-09-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/is-this-trait-sealed-or-not-sealed/"/>
        <id>https://predr.ag/blog/is-this-trait-sealed-or-not-sealed/</id>
        
        <summary type="html">&lt;p&gt;&lt;em&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;releases&#x2F;tag&#x2F;v0.35.0&quot;&gt;&lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; v0.35&lt;&#x2F;a&gt; can determine whether Rust traits are &quot;sealed&quot;, allowing it to catch many tricky new instances of SemVer breakage. Why is accurate sealed trait detection so important, and why is implementing it correctly so hard?&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>How to Query (Almost) Everything</title>
        <published>2024-07-22T00:00:00+00:00</published>
        <updated>2024-07-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/how-to-query-almost-everything-hytradboi/"/>
        <id>https://predr.ag/blog/how-to-query-almost-everything-hytradboi/</id>
        
        <summary type="html">&lt;p&gt;In 2022, I gave a talk at a virtual conference with an unforgettable name: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.hytradboi.com&#x2F;&quot;&gt;HYTRADBOI, which stands for &quot;Have You Tried Rubbing a Database On It?&quot;&lt;&#x2F;a&gt; Its goal was to discuss unconventional uses of database-like technology, and featured many excellent talks.&lt;&#x2F;p&gt;
&lt;p&gt;My talk &quot;How to Query (Almost) Everything&quot; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fnordig.de&#x2F;2022&#x2F;04&#x2F;30&#x2F;hytradboi&#x2F;&quot;&gt;received&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;stevekrouse&#x2F;status&#x2F;1520144870605656064&quot;&gt;copious&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;jeremyjkun&#x2F;status&#x2F;1580329307381321728&quot;&gt;praise&lt;&#x2F;a&gt;. It describes the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;trustfall&quot;&gt;Trustfall&lt;&#x2F;a&gt; query engine&#x27;s architecture, and includes real-world examples of how my (now-former) employer relies on it to statically catch and prevent cross-domain bugs across a monorepo with hundreds of services and shared libraries. For example:&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>The Wi-Fi only works when it&#x27;s raining</title>
        <published>2024-04-01T00:00:00+00:00</published>
        <updated>2024-04-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/wifi-only-works-when-its-raining/"/>
        <id>https://predr.ag/blog/wifi-only-works-when-its-raining/</id>
        
        <content type="html" xml:base="https://predr.ag/blog/wifi-only-works-when-its-raining/">&lt;p&gt;&lt;em&gt;Happy April 1st! This post is part of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aprilcools.club&#x2F;&quot;&gt;April Cools Club&lt;&#x2F;a&gt;: an April 1st effort to publish genuine essays on unexpected topics. Please enjoy this true story, and rest assured that the tech content will be back soon!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s what my dad said when I asked what was wrong with our home internet connection. &quot;The Wi-Fi only works when it&#x27;s raining.&quot;&lt;&#x2F;p&gt;
&lt;figure&gt;
  
  

  

  

  
  
  
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;predr.ag&amp;#x2F;processed_images&amp;#x2F;wifi-router-on-balcony-cropped.e51ec22e71a4e3ef.jpg&quot; alt=&quot;Illustration of a Wi-Fi antenna attached to the exterior of an upper floor of an apartment building. It&amp;#x27;s currently raining, and the Wi-Fi is working flawlessly.&quot;&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Let&#x27;s back up a few steps, so we&#x27;re all on the same page about the &lt;em&gt;utter ridiculousness&lt;&#x2F;em&gt; of this situation.&lt;&#x2F;p&gt;
&lt;p&gt;At the time, I was still a college student — this was over 10 years ago. I had come back home to spend a couple of weeks with my parents before the fall semester kicked off. I hadn&#x27;t been back home in almost a full year, because home and school were on different continents.&lt;&#x2F;p&gt;
&lt;p&gt;My dad is an engineer who had already been tinkering with networking gear longer than I&#x27;d been alive. Through the company he started, he had designed and deployed all sorts of complex network systems at institutions across the country — everything from gigabit Ethernet for an office building, to inter-city connections over &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Line-of-sight_propagation&quot;&gt;line-of-sight&lt;&#x2F;a&gt; microwave links.&lt;&#x2F;p&gt;
&lt;p&gt;He is &lt;em&gt;the last person on Earth&lt;&#x2F;em&gt; who would say a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Magical_thinking&quot;&gt;&quot;magical thinking&quot;&lt;&#x2F;a&gt; phrase like that.&lt;&#x2F;p&gt;
&lt;p&gt;&quot;What?&quot; I uttered, stunned. &quot;The Wi-Fi only works while it&#x27;s raining,&quot; he repeated patiently. &quot;It started a couple of weeks ago, and I haven&#x27;t had a chance to look into it yet.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;&quot;No way,&quot; I said. If anything, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.microwave-link.com&#x2F;microwave&#x2F;rain-fade-on-microwave-links&#x2F;&quot;&gt;rain makes wireless signal quality &lt;em&gt;worse&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;, not &lt;em&gt;better&lt;&#x2F;em&gt;. Never &lt;em&gt;better&lt;&#x2F;em&gt;!&lt;&#x2F;p&gt;
&lt;p&gt;Two weeks without reliable internet? I started a speed-run through the stages of grief...&lt;&#x2F;p&gt;
&lt;h2 id=&quot;denial&quot;&gt;Denial&lt;&#x2F;h2&gt;
&lt;p&gt;I pulled open my laptop and started poking at the network.&lt;&#x2F;p&gt;
&lt;p&gt;Pinging any website had a 98% packet loss rate. The internet connection was still up, but only in the most annoying &quot;technically accurate&quot; sense. Nothing loads when you have a 98% packet loss rate! The network may as well have been dead.&lt;&#x2F;p&gt;
&lt;p&gt;I was upset. I had just started dating someone a few months prior, and she was currently on the other side of the planet! How was I to explain that I couldn&#x27;t stay in touch because it wasn&#x27;t raining?&lt;label for=&quot;sn-mobile-internet&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-mobile-internet&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-mobile-internet&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;Mobile data at the time was &lt;em&gt;exorbitantly&lt;&#x2F;em&gt; expensive, so much so that I didn&#x27;t have a data plan at all for my cell service at home. I couldn&#x27;t just use my phone&#x27;s data plan to work around the problem, like one might do today in a similar situation.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;I was pacing around the house, fuming. Grief, stage two!&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s when the rain started.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bargaining&quot;&gt;Bargaining&lt;&#x2F;h2&gt;
&lt;p&gt;Like a miracle, within 5 minutes of the rain starting, the packet loss rate was down to 0%!&lt;&#x2F;p&gt;
&lt;p&gt;I couldn&#x27;t believe my eyes! I was ready for the connection to die at any second, so I opened a million tabs at once — as if I don&#x27;t normally do that anyway...&lt;&#x2F;p&gt;
&lt;p&gt;The rain held up for about an hour, and so did the internet connection.&lt;&#x2F;p&gt;
&lt;p&gt;Then, 15 minutes or so after the rain stopped, the packet loss rate shot back up to 90%+. The internet connection went back to being unusable.&lt;&#x2F;p&gt;
&lt;p&gt;I was ready to do just about anything to get more rain.&lt;&#x2F;p&gt;
&lt;p&gt;Thankfully, the weather stayed grey and murky for the next few days. Each time, the pattern stayed the same:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The rain starts, and not even a few minutes later the internet connection is crisp and fast.&lt;&#x2F;li&gt;
&lt;li&gt;The rain stops, and within 15 minutes the internet connection is unusable again.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;As much as I hated to admit it, the evidence was solid. The Wi-Fi only works when it&#x27;s raining!&lt;&#x2F;p&gt;
&lt;p&gt;At this point, I had a choice to make.&lt;&#x2F;p&gt;
&lt;p&gt;I could keep going through the stages of grief: I could sulk and plan my calls with my girlfriend around the weather forecast.&lt;&#x2F;p&gt;
&lt;p&gt;Or, I could break out of that downward spiral and get to the bottom of what was going on.&lt;&#x2F;p&gt;
&lt;p&gt;&quot;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Magical_thinking&quot;&gt;Magical thinking&lt;&#x2F;a&gt; be damned! Am I an engineer or what?&quot; I told myself.&lt;&#x2F;p&gt;
&lt;p&gt;That settled it. I wasn&#x27;t going to take this lying down.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;determination&quot;&gt;Determination&lt;&#x2F;h2&gt;
&lt;p&gt;Some context on our home networking setup is in order.&lt;&#x2F;p&gt;
&lt;p&gt;Remember how my dad&#x27;s company had extensive experience with networking solutions? Well, we had a fancy networking setup at home too — and it had worked flawlessly for the best part of 10 years!&lt;&#x2F;p&gt;
&lt;p&gt;My dad&#x27;s office had a very expensive, very fast&lt;label for=&quot;sn-contemporaneous&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-contemporaneous&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-contemporaneous&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;For the time, of course.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
 commercial internet connection. The home internet options, meanwhile, weren&#x27;t great! In my family, we are often stubbornly against settling for less unless there&#x27;s absolutely no other choice.&lt;&#x2F;p&gt;
&lt;p&gt;The office and our apartment were a few blocks away from each other along a small hill, with our second-floor apartment holding the higher ground. With a bit of work, my dad set up a line-of-sight Wi-Fi bridge — &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Long-range_Wi-Fi&quot;&gt;a couple of high-gain directional Wi-Fi antennas pointed at each other&lt;&#x2F;a&gt; — between the office and our apartment. This let us enjoy the faster commercial internet connection at home!&lt;&#x2F;p&gt;
&lt;p&gt;I started poking around the network to figure out where the connection was breaking down.&lt;&#x2F;p&gt;
&lt;p&gt;The local Wi-Fi router at home was working well — no packets lost. The local end of the Wi-Fi bridge was fine too.&lt;&#x2F;p&gt;
&lt;p&gt;But pinging the remote end of the Wi-Fi bridge was showing a 90%+ packet loss rate — and so did pinging any other network device behind it. Aha, there&#x27;s something wrong with the Wi-Fi bridge!&lt;&#x2F;p&gt;
&lt;p&gt;But &lt;em&gt;what&lt;&#x2F;em&gt;? And why &lt;em&gt;now&lt;&#x2F;em&gt;, when the system had been working fine for almost 10 years, rain or shine?&lt;label for=&quot;sn-work-experience&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-work-experience&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-work-experience&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;Maybe &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;mediocrity-can-be-a-sign-of-excellence&#x2F;#years-of-experience-vs-experience-in-those-years&quot;&gt;years of work experience isn&#x27;t a good metric&lt;&#x2F;a&gt; here either 😄&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;How&lt;&#x2F;em&gt; can a rain storm &lt;em&gt;fix&lt;&#x2F;em&gt; a Wi-Fi bridge, anyway?&lt;&#x2F;p&gt;
&lt;p&gt;So many confusing questions. Time to get some answers!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;debugging&quot;&gt;Debugging&lt;&#x2F;h2&gt;
&lt;p&gt;Like any experienced engineer, the first thing I tried was turning everything off and then on again. It didn&#x27;t work.&lt;&#x2F;p&gt;
&lt;p&gt;Then I checked all the devices on the network individually:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Maybe one of the devices has gone bad with age? Nope.&lt;label for=&quot;sn-diagnostics&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-diagnostics&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-diagnostics&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;I physically connected my laptop to each device&#x27;s local Ethernet, then ran diagnostics, pinged the devices over the wired connection, etc.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Maybe a cable got unseated or came loose? Nope.&lt;&#x2F;li&gt;
&lt;li&gt;Maybe a power brick has become faulty over time? Nope.&lt;&#x2F;li&gt;
&lt;li&gt;Maybe an automatic firmware update failed and broke something? Nope.&lt;&#x2F;li&gt;
&lt;li&gt;Maybe an antenna connector has corroded from spending years outdoors? Nope.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Unlike debugging software, a lot of this hardware debugging was annoyingly &lt;em&gt;physical&lt;&#x2F;em&gt;. I had to climb up ladders, trace cables that hadn&#x27;t been touched in 10 years, and do a lot of walking back and forth between our home and my dad&#x27;s office.&lt;&#x2F;p&gt;
&lt;p&gt;On my umpteenth back-and-forth walk, as I was bored and exasperated, I started noticing how much our neighborhood had changed in the many years I hadn&#x27;t been living at home full-time.&lt;label for=&quot;sn-boarding-school&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-boarding-school&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-boarding-school&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;Before college, I spent four years at a boarding high school. I was on our national math and programming teams for the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.imo-official.org&#x2F;&quot;&gt;IMO&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ioinformatics.org&#x2F;&quot;&gt;IOI&lt;&#x2F;a&gt;), so I even spent most of each summer away from home at prep camps and at the competitions themselves.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
 Many of the little neighborhood shops were new. Many houses had gotten a fresh coat of paint. Trees that used to be barely more than saplings had grown tall and strong.&lt;&#x2F;p&gt;
&lt;p&gt;Then it hit me.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;realization&quot;&gt;Realization&lt;&#x2F;h2&gt;
&lt;p&gt;I ran home and climbed up onto the scaffolding holding up the Wi-Fi bridge&#x27;s antenna. I was hanging precariously off the side of our apartment building, two stories up in the air.&lt;label for=&quot;sn-safety-harness&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-safety-harness&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-safety-harness&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;In retrospect, a safety harness would have been a good idea... Things people do for internet! Don&#x27;t forget, a girl was involved too — I wasn&#x27;t doing this merely for Netflix or Twitter.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Then I looked downhill, at the antenna that formed the second half of the Wi-Fi bridge.&lt;&#x2F;p&gt;
&lt;p&gt;Or at least, &lt;em&gt;toward&lt;&#x2F;em&gt; the antenna, because I couldn&#x27;t see it — a tree in a neighbor&#x27;s yard was in the way! Its topmost branches were swaying back and forth in the line-of-sight between the antenna pair.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Bingo!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-problem-and-the-fix&quot;&gt;The Problem and the Fix&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s what was going on.&lt;&#x2F;p&gt;
&lt;p&gt;Many years ago, we installed the Wi-Fi bridge. For a long time, everything was great!&lt;&#x2F;p&gt;
&lt;p&gt;But every year, our neighbor&#x27;s tree grew taller and taller. Shortly before when I came back home that summer, its topmost branches had managed to reach high enough to interfere with our Wi-Fi signal.&lt;&#x2F;p&gt;
&lt;p&gt;It was &lt;em&gt;only barely&lt;&#x2F;em&gt; tall enough to interfere with the signal, though!&lt;&#x2F;p&gt;
&lt;p&gt;Every time it rained, the rain collected on its leaves and branches and &lt;em&gt;weighed them down&lt;&#x2F;em&gt;. The extra weight bent them out of the way of the Wi-Fi line-of-sight!&lt;label for=&quot;sn-fresnel&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-fresnel&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-fresnel&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;Interestingly, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Line-of-sight_propagation#Impairments_to_line-of-sight_propagation&quot;&gt;objects &lt;em&gt;outside&lt;&#x2F;em&gt; the straight line between antennas can still cause interference&lt;&#x2F;a&gt;! For best signal quality, the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Fresnel_zone&quot;&gt;Fresnel zone&lt;&#x2F;a&gt; between the antennas should be clear of obstructions. But perfection isn&#x27;t achievable in practice, so RF equipment like Wi-Fi uses techniques like error-correcting codes so that it can still work without a perfectly clear Fresnel zone.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Each time the rain stopped, the rainwater would continue to drip off the tree. Slowly, over the course of 15ish minutes, that would unburden the tree — letting it rise back up into the path of our bits and bytes. That&#x27;s when the Wi-Fi would stop working.&lt;&#x2F;p&gt;
&lt;p&gt;The fix was easy: upgrade our hardware. We replaced &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;IEEE_802.11g-2003&quot;&gt;our old 802.11g devices&lt;&#x2F;a&gt; with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;IEEE_802.11n-2009&quot;&gt;new 802.11n ones&lt;&#x2F;a&gt;, which took advantage of new &lt;del&gt;magic&lt;&#x2F;del&gt; math and physics to make signals more resistant to interference.&lt;label for=&quot;sn-beamforming&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-beamforming&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-beamforming&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;One such piece of magic new to 802.11n Wi-Fi is called &quot;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.oreilly.com&#x2F;library&#x2F;view&#x2F;80211ac-a-survival&#x2F;9781449357702&#x2F;ch04.html&quot;&gt;beamforming&lt;&#x2F;a&gt;&quot; — it&#x27;s when a transmitter can use multiple antennas transmitting on the same frequency to shape and steer the signal in a way that improves the effective range and signal quality. Modern Wi-Fi does beamforming with only a few antenna elements, but if we scale that number way up we get &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Phased_array&quot;&gt;a phased array antenna&lt;&#x2F;a&gt;. Ever wondered how come &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.pcmag.com&#x2F;news&#x2F;spacex-reveals-next-gen-starlink-dish-for-residential-users&quot;&gt;Starlink antennas are flat&lt;&#x2F;a&gt; and not a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Satellite_dish#Gallery&quot;&gt;&quot;dish&quot; like old satellite TV antennas&lt;&#x2F;a&gt;? They use phased arrays to aim their signal at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.space.com&#x2F;starlink-satellite-train-how-to-see-and-track-it&quot;&gt;the Starlink satellites streaking across the sky&lt;&#x2F;a&gt; — without any moving parts. &lt;del&gt;Magic!&lt;&#x2F;del&gt; Physics!&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;A few days later, the new gear arrived and I eagerly climbed back up the scaffolding to install the new antennas.&lt;&#x2F;p&gt;
&lt;p&gt;A few screws, zip ties, and cable connections later, the Wi-Fi&#x27;s &quot;link established&quot; lights flashed green once again.&lt;&#x2F;p&gt;
&lt;p&gt;This time, &lt;em&gt;it wasn&#x27;t raining.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;All was well once again.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Hope you enjoyed this true story! &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aprilcools.club&#x2F;&quot;&gt;April Cools&lt;&#x2F;a&gt; is about surprising our readers with fun posts on topics outside our usual beat. Check out the other April Cools posts on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aprilcools.club&#x2F;&quot;&gt;our website&lt;&#x2F;a&gt;, and consider making your own blog part of April Cools Club next year!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;If you liked this post, consider &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;subscribe&#x2F;&quot;&gt;subscribing or following me on social media&lt;&#x2F;a&gt;.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Thanks to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hillelwayne.com&#x2F;&quot;&gt;Hillel Wayne&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jeremykun.com&#x2F;&quot;&gt;Jeremy Kun&lt;&#x2F;a&gt; for reading drafts of this post. All mistakes are my own.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>SemVer in Rust: Tooling, Breakage, and Edge Cases — FOSDEM 2024</title>
        <published>2024-03-18T00:00:00+00:00</published>
        <updated>2024-03-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/semver-in-rust-tooling-breakage-and-edge-cases/"/>
        <id>https://predr.ag/blog/semver-in-rust-tooling-breakage-and-edge-cases/</id>
        
        <summary type="html">&lt;p&gt;Last month, I gave a talk titled &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fosdem.org&#x2F;2024&#x2F;schedule&#x2F;event&#x2F;fosdem-2024-2682-semver-in-the-rust-ecosystem-breakage-tooling-and-edge-cases&#x2F;&quot;&gt;&quot;SemVer in Rust: Breakage, Tooling, and Edge Cases&quot;&lt;&#x2F;a&gt; at the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fosdem.org&#x2F;2024&#x2F;&quot;&gt;FOSDEM 2024&lt;&#x2F;a&gt; conference.&lt;&#x2F;p&gt;
&lt;p&gt;The talk is a practical look at what semantic versioning (SemVer) buys us, why SemVer goes wrong in practice, and how the &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; linter can help prevent the damage caused by SemVer breakage.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;&#x2F;strong&gt; SemVer is impossibly hard for humans, but automated tools can cover our greatest weaknesses.&amp;hellip;
&lt;&#x2F;p&gt;
</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Four challenges cargo-semver-checks has yet to tackle</title>
        <published>2024-01-23T00:00:00+00:00</published>
        <updated>2024-01-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/four-challenges-cargo-semver-checks-has-yet-to-tackle/"/>
        <id>https://predr.ag/blog/four-challenges-cargo-semver-checks-has-yet-to-tackle/</id>
        
        <summary type="html">&lt;p&gt;&lt;em&gt;My &lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;highlights-of-2023-for-cargo-semver-checks&#x2F;&quot;&gt;last post&lt;&#x2F;a&gt; covered the key &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; achievements from 2023. Here are the biggest challenges that lie ahead!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Many of the remaining challenges in cargo-semver-checks are obvious: we all want more lints, fewer false-positives, etc. etc. Let&#x27;s set those aside.&lt;&#x2F;p&gt;
&lt;p&gt;Instead, let&#x27;s talk about four non-obvious challenges we have yet to tackle:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;four-challenges-cargo-semver-checks-has-yet-to-tackle&#x2F;#obvious-in-retrospect-project-edge-cases&quot;&gt;The Obvious in Retrospect&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;four-challenges-cargo-semver-checks-has-yet-to-tackle&#x2F;#blocked-upstream-cross-crate-analysis&quot;&gt;The Blocked Upstream&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;four-challenges-cargo-semver-checks-has-yet-to-tackle&#x2F;#surprising-limitation-no-checking-of-types&quot;&gt;The Surprising Limitation&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;four-challenges-cargo-semver-checks-has-yet-to-tackle&#x2F;#existential-threat-sustainable-project-funding&quot;&gt;The Existential Threat&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Highlights of 2023 for cargo-semver-checks</title>
        <published>2024-01-16T00:00:00+00:00</published>
        <updated>2024-01-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/highlights-of-2023-for-cargo-semver-checks/"/>
        <id>https://predr.ag/blog/highlights-of-2023-for-cargo-semver-checks/</id>
        
        <summary type="html">&lt;p&gt;&lt;em&gt;2023 was a big year for &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt;! We saw ecosystem-wide adoption in projects of all shapes and sizes: the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tokio-rs&#x2F;tokio&quot;&gt;&lt;code&gt;tokio&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;pyo3&#x2F;pyo3&quot;&gt;&lt;code&gt;PyO3&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; ecosystems, company-backed OSS projects from companies like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;awslabs&#x2F;aws-sdk-rust&quot;&gt;Amazon&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;google&#x2F;zerocopy&quot;&gt;Google&lt;&#x2F;a&gt;, and even in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;cargo&#x2F;blob&#x2F;9d3473c727b776c165b03c3be26ae77c148474d6&#x2F;.github&#x2F;workflows&#x2F;main.yml#L96-L102&quot;&gt;&lt;code&gt;cargo&lt;&#x2F;code&gt; itself&lt;&#x2F;a&gt;. Here&#x27;s a look back at the highlights of 2023!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Checking semver in the presence of doc(hidden) items</title>
        <published>2023-11-18T00:00:00+00:00</published>
        <updated>2023-11-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/checking-semver-for-doc-hidden-items/"/>
        <id>https://predr.ag/blog/checking-semver-for-doc-hidden-items/</id>
        
        <summary type="html">&lt;p&gt;&lt;em&gt;&lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; v0.25 squashes nearly all bugs related to &lt;code&gt;doc(hidden)&lt;&#x2F;code&gt; items — its most common source of false-positives. What does &lt;code&gt;doc(hidden)&lt;&#x2F;code&gt; mean in Rust, and why was handling it correctly so hard?&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Semver violations are common, better tooling is the answer</title>
        <published>2023-09-07T00:00:00+00:00</published>
        <updated>2023-09-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/semver-violations-are-common-better-tooling-is-the-answer/"/>
        <id>https://predr.ag/blog/semver-violations-are-common-better-tooling-is-the-answer/</id>
        
        <summary type="html">&lt;p&gt;&lt;em&gt;This post is coauthored by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tonowak.com&#x2F;&quot;&gt;Tomasz Nowak&lt;&#x2F;a&gt; and Predrag Gruevski. It describes work the two of us did together with Bartosz Smolarczyk, Michał Staniewski, and Mieszko Grodzicki.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;PredragGruevski&#x2F;status&#x2F;1587877518018756609&#x2F;photo&#x2F;1&quot;&gt;Anecdotally&lt;&#x2F;a&gt;, &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; is a helpful tool for preventing the semver violations that every so often cause ecosystem-wide pain.
This is why it earned a spot in the CI pipelines of key Rust crates like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tokio-rs&#x2F;tokio&#x2F;blob&#x2F;37bb47c4a2aff8913e536767645772f15650e6cd&#x2F;.github&#x2F;workflows&#x2F;ci.yml#L393-L403&quot;&gt;&lt;code&gt;tokio&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, and also why &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;issues&#x2F;61&quot;&gt;the &lt;code&gt;cargo&lt;&#x2F;code&gt; team hopes to integrate it into &lt;code&gt;cargo&lt;&#x2F;code&gt; itself&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;While anedotal evidence is nice, we wanted to get concrete data across a large sample of real-world Rust code.&amp;hellip;
&lt;&#x2F;p&gt;
</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Breaking semver in Rust by adding a private type, or by adding an import</title>
        <published>2023-05-08T00:00:00+00:00</published>
        <updated>2023-05-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/breaking-semver-in-rust-by-adding-private-type-or-import/"/>
        <id>https://predr.ag/blog/breaking-semver-in-rust-by-adding-private-type-or-import/</id>
        
        <summary type="html">&lt;p&gt;A few days ago, I started polls on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hachyderm.io&#x2F;@predrag&#x2F;110307228646682326&quot;&gt;Mastodon&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;PredragGruevski&#x2F;status&#x2F;1653895888048214016&quot;&gt;Twitter&lt;&#x2F;a&gt; whether adding a new private type, or an import, can ever be a major breaking change. The consensus was that this should be impossible.&lt;&#x2F;p&gt;
&lt;p&gt;I agree with that. It &lt;em&gt;should&lt;&#x2F;em&gt; be impossible.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve discovered a way to cause a previously-public type or function to disappear from a crate&#x27;s public API by making innocuous-seeming changes like adding a private type or adding an import, etc.
It is not a hypothetical problem, either — I&#x27;ve found at least one real-world Rust project that has been affected by it.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>A definitive guide to sealed traits in Rust</title>
        <published>2023-04-05T00:00:00+00:00</published>
        <updated>2023-04-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/definitive-guide-to-sealed-traits-in-rust/"/>
        <id>https://predr.ag/blog/definitive-guide-to-sealed-traits-in-rust/</id>
        
        <summary type="html">&lt;p&gt;For the longest time, I thought that &quot;sealed trait&quot; in Rust was a singular concept implementable in one specific way. To prevent downstream crates from implementing your traits, you make the traits sealed — done, end of story. &lt;strong&gt;I was wrong!&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hachyderm.io&#x2F;@epage&#x2F;109820270237801122&quot;&gt;It turns out there are multiple ways to seal traits&lt;&#x2F;a&gt;, forming a pleasant spectrum of options:&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Mediocrity can be a sign of excellence, and other stories</title>
        <published>2023-04-01T00:00:00+00:00</published>
        <updated>2023-04-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/mediocrity-can-be-a-sign-of-excellence/"/>
        <id>https://predr.ag/blog/mediocrity-can-be-a-sign-of-excellence/</id>
        
        <summary type="html">&lt;p&gt;&lt;em&gt;Happy April 1st! This post is part of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aprilcools.club&#x2F;&quot;&gt;April Cools Club&lt;&#x2F;a&gt;: an effort to publish genuine posts on topics our usual audience would find unexpected. The tech content will be back soon!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Over the many years I spent heavily involved in intern and full-time recruiting at &lt;code&gt;$PREVIOUS_JOB&lt;&#x2F;code&gt;, multiple people have commented something to the effect of: &quot;How come Predrag always gets the best people?&quot;&lt;&#x2F;p&gt;
&lt;p&gt;This post is a series of vignettes showing three of the &lt;em&gt;less-obvious&lt;&#x2F;em&gt; ideas that gave us an edge,&amp;hellip;
&lt;&#x2F;p&gt;
</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Re-exporting an enum with a type alias is breaking, but not major</title>
        <published>2023-03-06T00:00:00+00:00</published>
        <updated>2023-03-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/re-exporting-enum-with-type-alias-breaking-change-not-major/"/>
        <id>https://predr.ag/blog/re-exporting-enum-with-type-alias-breaking-change-not-major/</id>
        
        <summary type="html">&lt;p&gt;We&#x27;ve already explored some of the dark corners of Rust semantic versioning on this blog:&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Speeding up Rust semver-checking by over 2000x</title>
        <published>2023-02-07T00:00:00+00:00</published>
        <updated>2023-02-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/speeding-up-rust-semver-checking-by-over-2000x/"/>
        <id>https://predr.ag/blog/speeding-up-rust-semver-checking-by-over-2000x/</id>
        
        <summary type="html">&lt;p&gt;&lt;em&gt;This post describes work in progress: how &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;cargo-semver-checks&quot;&gt;&lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; will benefit from the upcoming query optimization API in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;trustfall&quot;&gt;Trustfall query engine&lt;&#x2F;a&gt;. Read on to learn how a modern linter works under the hood, and how ideas from the world of databases can improve its performance.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Moving and re-exporting a Rust type can be a major breaking change</title>
        <published>2023-01-31T00:00:00+00:00</published>
        <updated>2023-01-31T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/moving-and-reexporting-rust-type-can-be-major-breaking-change/"/>
        <id>https://predr.ag/blog/moving-and-reexporting-rust-type-can-be-major-breaking-change/</id>
        
        <content type="html" xml:base="https://predr.ag/blog/moving-and-reexporting-rust-type-can-be-major-breaking-change/">&lt;p&gt;I recently embarked on a quest: revamp the &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; import-handling system so that moving and re-exporting an item stops being incorrectly flagged as a major breaking change. This is how crate authors can reorganize or rename items: just re-export the items in the original location under their original names, and downstream users shouldn&#x27;t notice.&lt;&#x2F;p&gt;
&lt;p&gt;Sounds simple, right?&lt;&#x2F;p&gt;
&lt;p&gt;I thought so too.&lt;&#x2F;p&gt;
&lt;p&gt;Then I started coming up with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hachyderm.io&#x2F;@predrag&#x2F;109700055807297291&quot;&gt;strange edge cases&lt;&#x2F;a&gt;, each more cursed than the last. But &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; has to handle them correctly, cursed or not! So off I went down yet another Rust rabbit hole...&lt;&#x2F;p&gt;
&lt;p&gt;After a few weeks of adventuring, I have emerged, seemingly&lt;label for=&quot;sn-seemingly&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-seemingly&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-seemingly&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;Only time will tell, won&#x27;t it? It&#x27;s looking good so far, though I did &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;trustfall-rustdoc-adapter&#x2F;pull&#x2F;52&quot;&gt;originally miss at least one edge case&lt;&#x2F;a&gt;.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
 victorious! Let me now regale you with some tales from my travels.&lt;label for=&quot;sn-travels&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-travels&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-travels&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;Although &#x27;travails&#x27; might be a more accurate word choice.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;moving-and-re-exporting-an-item&quot;&gt;Moving and re-exporting an item&lt;&#x2F;h2&gt;
&lt;p&gt;Say we have a crate called &lt;code&gt;example&lt;&#x2F;code&gt; containing the following code:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Foo&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Rust modules can include other modules&#x27; items in their own API: they can use &lt;code&gt;pub use&lt;&#x2F;code&gt; to re-export the other module&#x27;s item. Our &lt;code&gt;example&lt;&#x2F;code&gt; crate&#x27;s users won&#x27;t notice if we do the following change:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;crate&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; mod&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; inner&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Foo&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Users of this crate can import this&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; as `example::Foo` just as before.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; use&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; inner&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Foo&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The re-export is allowed to rename the item:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;crate&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; mod&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; inner&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Renamed; used to be called `Foo`.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Bar&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Users of this crate can import this&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; as `example::Foo` just as before.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; use&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; inner&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Bar&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; as&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Foo&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Re-exports can also use a &quot;glob&quot; pattern to select and re-export &lt;em&gt;all&lt;&#x2F;em&gt; public items in the target module:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;crate&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; mod&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; inner&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Foo&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Users of this crate can import&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; `example::Foo` just as before,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; because all public items in `inner`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; are selected for re-export.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; use&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; inner&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So far so good! But now the fun begins...&lt;&#x2F;p&gt;
&lt;h2 id=&quot;using-a-type-alias-to-re-export&quot;&gt;Using a type alias to re-export&lt;&#x2F;h2&gt;
&lt;p&gt;Rust&#x27;s &lt;code&gt;pub type&lt;&#x2F;code&gt; statement allows us to define a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;reference&#x2F;items&#x2F;type-aliases.html&quot;&gt;&lt;em&gt;type alias&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;, an &quot;equivalent name&quot; for some Rust type.&lt;&#x2F;p&gt;
&lt;p&gt;This isn&#x27;t the same thing as &lt;code&gt;pub use&lt;&#x2F;code&gt;, but in many cases&lt;label for=&quot;sn-foreshadowing&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-foreshadowing&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-foreshadowing&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;&lt;em&gt;Ominous music starts...&lt;&#x2F;em&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
 can be used equivalently to &lt;code&gt;pub use&lt;&#x2F;code&gt;.
For example, we could use a &lt;code&gt;pub type&lt;&#x2F;code&gt; to accomplish the same renaming that earlier used a &lt;code&gt;pub use&lt;&#x2F;code&gt; by doing the following:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;crate&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; mod&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; inner&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Renamed; used to be called `Foo`.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Bar&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Users of this crate can import this&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; as `example::Foo` just as before.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Foo&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; inner&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Bar&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Many crates use &lt;code&gt;pub type&lt;&#x2F;code&gt; instead of &lt;code&gt;pub use&lt;&#x2F;code&gt; to re-export items.
Some crates actively encourage using &lt;code&gt;pub type&lt;&#x2F;code&gt; instead of &lt;code&gt;pub use&lt;&#x2F;code&gt;, for reasons that are out of scope here.
They produce valid Rust code, and &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; should support that code.&lt;&#x2F;p&gt;
&lt;p&gt;Using &lt;code&gt;pub type&lt;&#x2F;code&gt; as if it were a &lt;code&gt;pub use&lt;&#x2F;code&gt; is fine ... &lt;em&gt;most of the time&lt;&#x2F;em&gt;. But every so often, it&#x27;s a ✨ major breaking change! ✨&lt;&#x2F;p&gt;
&lt;h2 id=&quot;accidental-major-breaking-change-via-pub-type&quot;&gt;Accidental major breaking change via &lt;code&gt;pub type&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Say our original crate had defined &lt;code&gt;Foo&lt;&#x2F;code&gt; to be a unit struct instead of a plain struct:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Previously, `Foo` was defined&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; with curly braces like so:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; `pub struct Foo {}`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Foo&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We now make the same completely innocuous-seeming change as before:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;crate&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; mod&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; inner&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Renamed; used to be called `Foo`.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; This one is also a unit struct:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; no curly braces this time.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Bar&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Is this `Foo` equivalent to the old one?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Foo&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; inner&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Bar&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;💥 Oops! 💥 What happened?&lt;&#x2F;p&gt;
&lt;p&gt;The idiomatic way to construct a unit struct is to just name it: &lt;code&gt;let _ = Foo&lt;&#x2F;code&gt;. But that &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=stable&amp;amp;mode=debug&amp;amp;edition=2021&amp;amp;gist=887f6acb84a1ad60f8db80cf84fc26da&quot;&gt;doesn&#x27;t work&lt;&#x2F;a&gt; if &lt;code&gt;Foo&lt;&#x2F;code&gt; is a type alias to a unit struct!&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust_errors&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;error&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;[E0423]&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; expected value, found type alias `Foo`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-italic&quot;&gt;  &amp;lt; ... &amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  = &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;note&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; can&amp;#39;t use a type alias as a constructor&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The same thing happens if &lt;code&gt;Foo&lt;&#x2F;code&gt; is a tuple struct: &lt;code&gt;Foo(42)&lt;&#x2F;code&gt; works fine on the struct itself but &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=stable&amp;amp;mode=debug&amp;amp;edition=2021&amp;amp;gist=6eae299dee41001d2c2c1ea7e83c1443&quot;&gt;doesn&#x27;t work on the type alias&lt;&#x2F;a&gt;.&lt;label for=&quot;sn-future-edition&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-future-edition&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-future-edition&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;There&#x27;s an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;issues&#x2F;73191&quot;&gt;open issue&lt;&#x2F;a&gt; suggesting that in a future Rust edition, this kind of &lt;code&gt;pub type&lt;&#x2F;code&gt; may become completely equivalent to a &lt;code&gt;pub use&lt;&#x2F;code&gt;. In that case, this would no longer be a breaking change. Thanks to this &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;rust&#x2F;comments&#x2F;10q0o4t&#x2F;comment&#x2F;j6plmrr&#x2F;&quot;&gt;r&#x2F;rust comment&lt;&#x2F;a&gt; for the link!&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;This seems like exactly the sort of thing one would find right &lt;em&gt;after&lt;&#x2F;em&gt; publishing a new version, when some poor user opens an issue saying their build is now broken.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-cargo-semver-checks-handles-this&quot;&gt;How &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; handles this&lt;&#x2F;h2&gt;
&lt;p&gt;The immediate goal (achieved in v0.17) was to stop flagging re-exports as major breaking changes. In our philosophy, it&#x27;s better to &lt;em&gt;miss&lt;&#x2F;em&gt; a real semver-major change than to &lt;em&gt;falsely report&lt;&#x2F;em&gt; a breaking change. Nobody likes it when CI turns red due to &lt;em&gt;a tool being wrong&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Prior to 0.17, &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; would simply miss many re-exports and would claim that the type had been entirely removed from the API. That lint was obviously not correct — not even in the case of a &lt;code&gt;pub type&lt;&#x2F;code&gt; of a unit or tuple struct as in the example above.&lt;&#x2F;p&gt;
&lt;p&gt;We plan to add &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;issues&#x2F;338&quot;&gt;a new lint&lt;&#x2F;a&gt; to catch this case and explain the specific problem in detail, since we expect most users would be quite surprised to see a major breaking change reported there.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Did you notice the major breaking change above? Probably not, right? You are not alone!&lt;&#x2F;p&gt;
&lt;p&gt;I only found it because &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;pull&#x2F;337#discussion_r1091341652&quot;&gt;someone pointed me in the correct general direction&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;tags&#x2F;semver&#x2F;&quot;&gt;Semver in Rust is hard to get right&lt;&#x2F;a&gt;, example number zillion and one, right here. This is why we use automated tools.&lt;&#x2F;p&gt;
&lt;p&gt;And I haven&#x27;t even gotten a chance to bring up any truly cursed Rust examples of re-exports yet.&lt;label for=&quot;sn-tests&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-tests&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-tests&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;I wrote approximately 30 test crates while implementing this. Nearly all of them are more cursed than any code in this post. They are split between &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;pull&#x2F;330&quot;&gt;itself&lt;&#x2F;a&gt; and its &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;trustfall-rustdoc-adapter&#x2F;pull&#x2F;34&quot;&gt;dependency&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;trustfall-rustdoc-adapter&#x2F;pull&#x2F;52&quot;&gt;crate&lt;&#x2F;a&gt; &lt;code&gt;trustfall-rustdoc-adapter&lt;&#x2F;code&gt;, which allows &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;play.predr.ag&#x2F;rustdoc#?f=1&amp;amp;q=IyBJdGVtcyB3aGVyZSBsaW50cyB3ZXJlIGFsbG93ZWQuIE5vdCBhbGwgY3JhdGVzIGhhdmUgdGhlc2UsCiMgdHJ5IG9uZSBvZjogYW55aG93LCBjbGFwLCBodHRwLCBodHRwYXJzZSwgaHlwZXIsIGl0ZXJ0b29scy4KcXVlcnkgewogIENyYXRlIHsKICAgIGl0ZW0gewogICAgICBuYW1lIEBvdXRwdXQKCiAgICAgIGF0dHJpYnV0ZSB7CiAgICAgICAgYXR0cjogdmFsdWUgQG91dHB1dAogICAgICAgICAgICAgICAgICAgIEBmaWx0ZXIob3A6ICJyZWdleCIsIHZhbHVlOiBbIiRwYXR0ZXJuIl0pCiAgICAgIH0KCiAgICAgIHNwYW4gewogICAgICAgIGZpbGVuYW1lIEBvdXRwdXQKICAgICAgICBiZWdpbl9saW5lIEBvdXRwdXQKICAgICAgfQogICAgfQogIH0KfQ%3D%3D&amp;amp;v=ewogICJwYXR0ZXJuIjogIiNcXFthbGxvd1xcKC4rXFwpXFxdIgp9&quot;&gt;declaratively query&lt;&#x2F;a&gt; rustdoc JSON via the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;trustfall&quot;&gt;Trustfall query engine&lt;&#x2F;a&gt;.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
 If my adventure into Rust re-exports is like Frodo&#x27;s journey in Lord of the Rings, then this post covers Frodo leaving his front yard.&lt;&#x2F;p&gt;
&lt;p&gt;If you liked this post, let me know! Then I&#x27;ll write up the rest of the trip to Mount Doom.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Some Rust breaking changes don&#x27;t require a major version</title>
        <published>2023-01-26T00:00:00+00:00</published>
        <updated>2023-01-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/some-rust-breaking-changes-do-not-require-major-version/"/>
        <id>https://predr.ag/blog/some-rust-breaking-changes-do-not-require-major-version/</id>
        
        <content type="html" xml:base="https://predr.ag/blog/some-rust-breaking-changes-do-not-require-major-version/">&lt;p&gt;I&#x27;ve &lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;toward-fearless-cargo-update&#x2F;&quot;&gt;been saying&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;cargo-semver-checks-today-and-in-2023&#x2F;&quot;&gt;for a while now&lt;&#x2F;a&gt; that &lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;turning-rust-struct-to-enum-is-not-always-breaking&#x2F;&quot;&gt;semantic versioning in Rust is tricky and full of unexpected edge cases&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;turning-rust-struct-to-enum-is-not-always-breaking&#x2F;&quot;&gt;My last post&lt;&#x2F;a&gt; mentioned that some Rust structs can be converted into enums without requiring a major version bump. It introduced a non-exhaustive struct called &lt;code&gt;Chameleon&lt;&#x2F;code&gt; that had no public fields, and claimed it was totally safe to turn it into an enum. But surely there was some sort of mistake, since syntax like &lt;code&gt;let Chameleon { .. } = value&lt;&#x2F;code&gt; would break if the &lt;code&gt;Chameleon&lt;&#x2F;code&gt; struct became an enum?&lt;&#x2F;p&gt;
&lt;p&gt;Yes, that statement would break.&lt;label for=&quot;sn-reddit&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-reddit&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-reddit&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;Thanks to the alert readers on r&#x2F;rust and Mastodon that pointed it out and even provided Rust Playground links! &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;old.reddit.com&#x2F;r&#x2F;rust&#x2F;comments&#x2F;10k0eox&#x2F;turning_a_rust_struct_into_an_enum_is_not_always&#x2F;j5t213a&#x2F;&quot;&gt;This discussion&lt;&#x2F;a&gt; was particularly nuanced and interesting.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
 And yet, this breaking change does not require a major version under Rust&#x27;s semantic versioning rules!&lt;&#x2F;p&gt;
&lt;p&gt;How could a breaking change not be a semver-major change? Let&#x27;s dig in and find out!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;all-major-changes-are-breaking-but-not-all-breaking-changes-are-major&quot;&gt;All major changes are breaking, but not all breaking changes are major&lt;&#x2F;h2&gt;
&lt;p&gt;There are two authoritative sources for semantic versioning in Rust: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;cargo&#x2F;reference&#x2F;semver.html&quot;&gt;the cargo semver reference&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rust-lang.github.io&#x2F;rfcs&#x2F;1105-api-evolution.html&quot;&gt;the API evolution RFC&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s what the API evolution RFC says about breaking changes:
&lt;blockquote class=&quot;own&quot; cite=&quot;https:&amp;#x2F;&amp;#x2F;rust-lang.github.io&amp;#x2F;rfcs&amp;#x2F;1105-api-evolution.html#detailed-design&quot;&gt;
  &lt;p&gt;
    What we will see is that in Rust today, almost any change is technically a breaking change. For example, given the way that globs currently work, adding any public item to a library can break its clients [...] But not all breaking changes are equal.&lt;br&gt;&lt;br&gt;So, this RFC proposes that all major changes are breaking, but not all breaking changes are major.
  &lt;&#x2F;p&gt;
  &lt;footer&gt;
    &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;rust-lang.github.io&amp;#x2F;rfcs&amp;#x2F;1105-api-evolution.html#detailed-design&quot; rel=&quot;noreferrer&quot;&gt;Rust API evolution RFC&lt;&#x2F;a&gt;
  &lt;&#x2F;footer&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;This feels ... strange. Running &lt;code&gt;cargo update&lt;&#x2F;code&gt; will by default update dependencies to the latest version in the same major version series, yet breaking changes are allowed without a new major version?&lt;&#x2F;p&gt;
&lt;p&gt;Ultimately, I feel&lt;label for=&quot;sn-non-official&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-non-official&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-non-official&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;I only recently got involved in Rust&#x27;s semver story by working on &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt;, so I wasn&#x27;t part of the discussion or decisions in the API evolution RFC. This post is on my personal blog, not the Rust blog, so you&#x27;re reading my own opinion and interpretation.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
 this is a case of Rust choosing pragmatism — and in my opinion, getting it right. I&#x27;ll try to convince you of this in the rest of this post.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s take a look at two examples where breaking changes are &lt;em&gt;explicitly not&lt;&#x2F;em&gt; semver-major.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;adding-a-new-public-item-is-technically-a-breaking-change&quot;&gt;Adding a new public item is technically a breaking change&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s pretend that in the below example, &lt;code&gt;first&lt;&#x2F;code&gt; and &lt;code&gt;second&lt;&#x2F;code&gt; are dependency crates of our library.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; mod&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; first&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Foo&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; mod&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; second&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; what happens if we uncomment this?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; pub struct Foo;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;use&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; first&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;use&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; second&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; process&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;foo&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Foo&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; do stuff with foo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Our library uses globs to import all public items from &lt;code&gt;first&lt;&#x2F;code&gt; and &lt;code&gt;second&lt;&#x2F;code&gt;. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=stable&amp;amp;mode=debug&amp;amp;edition=2021&amp;amp;gist=1f87403328f8b811ce0572b032c8aa04&quot;&gt;This works fine!&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Now imagine &lt;code&gt;second&lt;&#x2F;code&gt; adds some new functionality: uncomment its &lt;code&gt;pub struct Foo&lt;&#x2F;code&gt; line. This is a purely additive change: &lt;code&gt;second&lt;&#x2F;code&gt; can still do everything it could previously do, and has gained some new functionality via the new type &lt;code&gt;Foo&lt;&#x2F;code&gt;. Purely additive API changes are semver-minor, right?&lt;&#x2F;p&gt;
&lt;p&gt;Try &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=stable&amp;amp;mode=debug&amp;amp;edition=2021&amp;amp;gist=1f87403328f8b811ce0572b032c8aa04&quot;&gt;compiling the code&lt;&#x2F;a&gt; after uncommenting that line, though. 💥 Oops! 💥&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust_errors&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;error&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;[E0659]&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; `Foo` is ambiguous&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --&amp;gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;src&#x2F;lib.rs&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;13&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;18&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;13 | &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; process&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;foo&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Foo&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   |                  &lt;&#x2F;span&gt;&lt;span&gt;^^^&lt;&#x2F;span&gt;&lt;span&gt; ambiguous name&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   = &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;note&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; ambiguous because of multiple glob imports of a name in the same module&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-italic&quot;&gt;&amp;lt; ... fix suggestions omitted for brevity ... &amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The code that depends on both &lt;code&gt;first&lt;&#x2F;code&gt; and &lt;code&gt;second&lt;&#x2F;code&gt; was broken by &lt;code&gt;second&lt;&#x2F;code&gt;&#x27;s purely additive change. Additive or not, it was &lt;em&gt;unquestionably&lt;&#x2F;em&gt; a breaking change.&lt;label for=&quot;sn-rfc-example-1&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-rfc-example-1&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-rfc-example-1&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;If you read the API evolution RFC&#x27;s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rfcs&#x2F;blob&#x2F;master&#x2F;text&#x2F;1105-api-evolution.md#minor-change-adding-new-public-items&quot;&gt;section on adding public items&lt;&#x2F;a&gt;, you may have noticed that its example of breaking code by adding a public item is much shorter than the one here — and also that in today&#x27;s Rust, that example isn&#x27;t broken anymore! This is because Rust adopted another recommendation from that RFC: if a locally-defined item&#x27;s name conflicts with a glob-imported name, the local item &quot;wins&quot; and shadows the other one instead of breaking with an ambiguous resolution error.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;If Rust semver demanded that all breaking changes must be semver-major, here are a few ways this could work:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Option 1: Nearly all API additions are semver-major. This obviously doesn&#x27;t seem right.&lt;&#x2F;li&gt;
&lt;li&gt;Option 2: Glob imports are &quot;last definition wins&quot; (like in Python), or &quot;first definition wins.&quot; I think this makes the problem worse, not better: now it&#x27;s even less obvious which &lt;code&gt;Foo&lt;&#x2F;code&gt; is getting imported, and we&#x27;re setting ourselves up for even worse compilation errors than otherwise.&lt;&#x2F;li&gt;
&lt;li&gt;Option 3: Glob imports are removed from the language, since they play a part in causing this problem. That also means no more &lt;code&gt;prelude&lt;&#x2F;code&gt; modules designed for glob-importing, harming the ergonomics of awesome crates like &lt;code&gt;pyo3&lt;&#x2F;code&gt; and &lt;code&gt;futures&lt;&#x2F;code&gt;. This isn&#x27;t good either.&lt;&#x2F;li&gt;
&lt;li&gt;Option 4: When we write code like &lt;code&gt;&amp;amp;Foo&lt;&#x2F;code&gt;, a tool (say, &lt;code&gt;cargo&lt;&#x2F;code&gt; or &lt;code&gt;rustc&lt;&#x2F;code&gt;) immediately replaces &lt;code&gt;Foo&lt;&#x2F;code&gt; with its fully-qualified name: in this case, &lt;code&gt;first::Foo&lt;&#x2F;code&gt;. Glob imports serve only to tell that tool where to look while rewriting our code. This solution has way too many moving pieces, and doesn&#x27;t feel particularly ergonomic, either.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;None of these options are good. Rust opted to go in another direction:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;adding public items is semver-&lt;em&gt;minor&lt;&#x2F;em&gt;;&lt;&#x2F;li&gt;
&lt;li&gt;glob imports are discouraged, to minimize (but not &lt;em&gt;prevent&lt;&#x2F;em&gt;) breakage, and&lt;&#x2F;li&gt;
&lt;li&gt;maintainers of crates with &lt;code&gt;prelude&lt;&#x2F;code&gt; modules are encouraged to be mindful of what they add to the prelude, again to minimize but not prevent breakage.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A similar problem exists with trait methods: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rfcs&#x2F;blob&#x2F;master&#x2F;text&#x2F;1105-api-evolution.md#minor-change-implementing-any-non-fundamental-trait&quot;&gt;implementing a public trait for any existing type is also technically breaking&lt;&#x2F;a&gt;, and is also &lt;em&gt;explicitly defined&lt;&#x2F;em&gt; as semver-minor &lt;em&gt;despite&lt;&#x2F;em&gt; the breakage.&lt;label for=&quot;sn-existing-trait&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-existing-trait&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-existing-trait&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rfcs&#x2F;blob&#x2F;master&#x2F;text&#x2F;1105-api-evolution.md#minor-change-implementing-any-non-fundamental-trait&quot;&gt;RFC states that&lt;&#x2F;a&gt; the breakage occurs if the breakage-causing trait already existed prior to being implemented. But in the RFC&#x27;s example code, it&#x27;s actually sufficient for the trait to make its way into crate B&#x27;s scope. For example, crate B glob-importing of all of crate A&#x27;s public items would cause the same breakage even if the conflicting trait did not previously exist.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;breakage-of-patterns-is-not-always-semver-major&quot;&gt;Breakage of patterns is not always semver-major&lt;&#x2F;h2&gt;
&lt;p&gt;Pattern-matching on structs is always allowed in Rust, even if the struct being matched has no visible fields: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=stable&amp;amp;mode=debug&amp;amp;edition=2021&amp;amp;gist=5c8ad567eda2afde921b57769bc09c9b&quot;&gt;playground link&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; say this is in some other crate&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; mod&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; other&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Foo&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;i64&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; process&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;value&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;other&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Foo&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Foo&amp;#39;s field is not visible here!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; This `let` does nothing useful:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; - it can&amp;#39;t extract any fields, and&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; - can&amp;#39;t learn anything else about `value`.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Foo&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ..&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; value&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Some kinds of changes to &lt;code&gt;Foo&lt;&#x2F;code&gt; can cause &lt;code&gt;let Foo { .. } = value;&lt;&#x2F;code&gt; to break. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rfcs&#x2F;blob&#x2F;master&#x2F;text&#x2F;1105-api-evolution.md#minor-change-going-from-a-tuple-struct-with-all-private-fields-with-at-least-one-field-to-a-normal-struct-or-vice-versa&quot;&gt;The RFC is unambiguous here&lt;&#x2F;a&gt;: statements like &lt;code&gt;let Foo { .. } = value&lt;&#x2F;code&gt; serve no purpose other than to be broken if &lt;code&gt;Foo&lt;&#x2F;code&gt; changes, and its breakage is not sufficient to make this change semver-major.&lt;label for=&quot;sn-rfc-example-2&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-rfc-example-2&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-rfc-example-2&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;Again, the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rfcs&#x2F;blob&#x2F;master&#x2F;text&#x2F;1105-api-evolution.md#minor-change-going-from-a-tuple-struct-with-all-private-fields-with-at-least-one-field-to-a-normal-struct-or-vice-versa&quot;&gt;RFC&#x27;s example for this case&lt;&#x2F;a&gt; doesn&#x27;t quite work as written: Rust has evolved in the nearly 8 years since that RFC was written. But its point stands regardless.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;There are cases where the &lt;code&gt;Foo { .. }&lt;&#x2F;code&gt; pattern is useful to aid type inference, for example: &lt;code&gt;if let Some(x @ Foo { .. }) = x.downcast_ref()&lt;&#x2F;code&gt;.&lt;label for=&quot;sn-reddit-example&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-reddit-example&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-reddit-example&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;Thanks to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;rust&#x2F;comments&#x2F;10k0eox&#x2F;comment&#x2F;j5t213a&#x2F;&quot;&gt;this r&#x2F;rust comment&lt;&#x2F;a&gt; for this excellent example!&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
 However, those cases are specifically addressed in the RFC as well (original emphasis retained):
&lt;blockquote class=&quot;own&quot; cite=&quot;https:&amp;#x2F;&amp;#x2F;rust-lang.github.io&amp;#x2F;rfcs&amp;#x2F;1105-api-evolution.html#principles-of-the-policy&quot;&gt;
  &lt;p&gt;
    For example, changes that may require occasional type annotations or use of UFCS to disambiguate are not automatically &quot;major&quot; changes. [...] any breakage in a minor release must be very &quot;shallow&quot;: it must always be possible to locally fix the problem through some kind of disambiguation &lt;em&gt;that could have been done in advance&lt;&#x2F;em&gt; (by using more explicit forms) or other annotation (like disabling a lint).
  &lt;&#x2F;p&gt;
  &lt;footer&gt;
    &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;rust-lang.github.io&amp;#x2F;rfcs&amp;#x2F;1105-api-evolution.html#principles-of-the-policy&quot; rel=&quot;noreferrer&quot;&gt;Principles of the policy, Rust API evolution RFC&lt;&#x2F;a&gt;
  &lt;&#x2F;footer&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;This is why turning &lt;code&gt;Chameleon&lt;&#x2F;code&gt; from a struct into an enum &lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;turning-rust-struct-to-enum-is-not-always-breaking&#x2F;&quot;&gt;in the last post&lt;&#x2F;a&gt; did not require a new major version:
the only breakage that could happen was in type inference or in a statement that did not serve any purpose.
Barring some kind of exceptional situation (e.g., potential for ecosystem-wide breakage, definitely not the case here), the API evolution RFC &lt;em&gt;explicitly&lt;&#x2F;em&gt; disqualifies both of those categories from triggering a semver-major change.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Before reading this post, did you know that not all breaking changes require a new major version under Rust&#x27;s semantic versioning principles?&lt;&#x2F;p&gt;
&lt;p&gt;Semver in Rust is hard for many reasons. There are a zillion strange ways to cause major breaking changes: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;cargo&#x2F;reference&#x2F;semver.html#struct-add-private-field-when-public&quot;&gt;example&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;cargo&#x2F;reference&#x2F;semver.html#possibly-breaking-change-adding-any-inherent-items&quot;&gt;another example&lt;&#x2F;a&gt;. There&#x27;s even &quot;spooky action at a distance&quot; where &lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;toward-fearless-cargo-update&#x2F;&quot;&gt;adding a field to a type can cause traits to silently stop being implemented for that type&lt;&#x2F;a&gt;. And as we saw here, not all breaking changes are semver-major!&lt;&#x2F;p&gt;
&lt;p&gt;As if to prove my point, &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;issues&#x2F;317&quot;&gt;was recently broken&lt;&#x2F;a&gt; by a dependency crate&#x27;s semver-incompatible (and now &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;cargo&#x2F;commands&#x2F;cargo-yank.html&quot;&gt;yanked&lt;&#x2F;a&gt;) release.&lt;label for=&quot;sn-locked&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-locked&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-locked&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;This is why installing with &lt;code class=&quot;nobr&quot;&gt;cargo install --locked&lt;&#x2F;code&gt; is a good idea! Locked installs didn&#x27;t break.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
 Breaking semver is not shameful, and is not a sign of maintainers&#x27; carelessness, poor skill, or anything of the sort. It&#x27;s just another language ergonomics problem solvable by better tooling.&lt;&#x2F;p&gt;
&lt;p&gt;This is the raison d&#x27;être for &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Turning a Rust struct into an enum is not always a major breaking change</title>
        <published>2023-01-24T00:00:00+00:00</published>
        <updated>2023-01-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/turning-rust-struct-to-enum-is-not-always-breaking/"/>
        <id>https://predr.ag/blog/turning-rust-struct-to-enum-is-not-always-breaking/</id>
        
        <content type="html" xml:base="https://predr.ag/blog/turning-rust-struct-to-enum-is-not-always-breaking/">&lt;p&gt;&lt;em&gt;&lt;strong&gt;EDIT&lt;&#x2F;strong&gt;: The Rust &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rust-lang.github.io&#x2F;rfcs&#x2F;1105-api-evolution.html#detailed-design&quot;&gt;API evolution RFC&lt;&#x2F;a&gt; distinguishes between breaking changes and changes that require a new semver-major version (called major changes). All major changes are breaking, but not all breaking changes are major. Changing a struct to an enum is always breaking (as pointed out &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;rust&#x2F;comments&#x2F;10k0eox&#x2F;comment&#x2F;j5nrm6z&#x2F;&quot;&gt;on r&#x2F;rust&lt;&#x2F;a&gt;) but is not always major (equivalent to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rust-lang.github.io&#x2F;rfcs&#x2F;1105-api-evolution.html#minor-change-going-from-a-tuple-struct-with-all-private-fields-with-at-least-one-field-to-a-normal-struct-or-vice-versa&quot;&gt;this case&lt;&#x2F;a&gt;). In this post, we&#x27;re trying to avoid making a major change.&lt;&#x2F;em&gt;&lt;label for=&quot;sn-edit&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-edit&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-edit&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;The title and content has been slightly edited since the original publication for clarity on the &quot;major vs breaking&quot; point.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Upholding semantic versioning &lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;toward-fearless-cargo-update&#x2F;&quot;&gt;is critical in the Rust ecosystem&lt;&#x2F;a&gt;.
But semver has a million non-obvious edge cases.
It&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;cargo-semver-checks-today-and-in-2023&#x2F;&quot;&gt;unreasonable to expect&lt;&#x2F;a&gt; all Rustaceans to master semver trivia to be able to safely publish crates — and it doesn&#x27;t make for a welcoming, inclusive environment, either.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; cares about the edge cases of semver compliance so you won&#x27;t have to.&lt;&#x2F;p&gt;
&lt;p&gt;To do that, it must also correctly implement all those edge cases.
Every so often, my work on &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; leads me to a semver edge case that surprises me.
For example: &lt;em&gt;turning a Rust struct into an enum doesn&#x27;t necessarily require a major version!&lt;&#x2F;em&gt; Here&#x27;s how!&lt;&#x2F;p&gt;
&lt;p&gt;Our protagonist in this post is &lt;code&gt;pub struct Chameleon&lt;&#x2F;code&gt;.
Our goal is to turn it into &lt;code&gt;pub enum Chameleon&lt;&#x2F;code&gt; without needing to release v2.0 of our (fictional) crate.
Not every struct is capable of this feat, so we&#x27;ll show how our little camouflaged friend manages to blend into the environment so well.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;implemented-methods-and-traits&quot;&gt;Implemented methods and traits&lt;&#x2F;h2&gt;
&lt;p&gt;One possible major breaking change is removing the implementation of a method or trait on &lt;code&gt;pub enum Chameleon&lt;&#x2F;code&gt; when that method or trait were implemented for the old &lt;code&gt;pub struct Chameleon&lt;&#x2F;code&gt;.
Any code that relies on that method or trait will be broken when the method or trait disappears.&lt;&#x2F;p&gt;
&lt;p&gt;In fact, many kinds of major breaking changes are possible within &lt;code&gt;impl&lt;&#x2F;code&gt; blocks.
To make things easy on ourselves:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;We won&#x27;t change or remove any &lt;code&gt;#[derive(...)]&lt;&#x2F;code&gt; or similar derive-macro attributes that already existed on the struct.&lt;&#x2F;li&gt;
&lt;li&gt;We&#x27;ll be careful when altering existing &lt;code&gt;impl Chameleon&lt;&#x2F;code&gt; or &lt;code&gt;impl SomeTrait for Chameleon&lt;&#x2F;code&gt; blocks.
Removing or altering methods, changing implemented traits&#x27; associated types, or changing the bounds on a trait implementation can all be major breaking changes.&lt;&#x2F;li&gt;
&lt;li&gt;We&#x27;ll keep an eye on our type&#x27;s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;reference&#x2F;special-types-and-traits.html#auto-traits&quot;&gt;auto-traits&lt;&#x2F;a&gt; — we don&#x27;t want to find out our new type is not &lt;code&gt;Send&lt;&#x2F;code&gt; or &lt;code&gt;Sync&lt;&#x2F;code&gt; &lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;toward-fearless-cargo-update&#x2F;&quot;&gt;like in a prior post&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;As of v0.16.0, &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; can automatically check &lt;em&gt;some of this&lt;&#x2F;em&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;blob&#x2F;main&#x2F;src&#x2F;lints&#x2F;inherent_method_missing.ron&quot;&gt;The &lt;code&gt;inherent_method_missing&lt;&#x2F;code&gt; lint&lt;&#x2F;a&gt; ensure that all inherent methods (ones in &lt;code&gt;impl Chameleon&lt;&#x2F;code&gt;, not &lt;code&gt;impl Foo for Chameleon&lt;&#x2F;code&gt;) continue to exist.&lt;&#x2F;li&gt;
&lt;li&gt;Other lints check that methods &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;blob&#x2F;main&#x2F;src&#x2F;lints&#x2F;method_parameter_count_changed.ron&quot;&gt;did not change their number of parameters&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;blob&#x2F;main&#x2F;src&#x2F;lints&#x2F;inherent_method_const_removed.ron&quot;&gt;did not stop being &lt;code&gt;const&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;blob&#x2F;main&#x2F;src&#x2F;lints&#x2F;inherent_method_unsafe_added.ron&quot;&gt;did not become &lt;code&gt;unsafe&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, etc.&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;blob&#x2F;main&#x2F;src&#x2F;lints&#x2F;auto_trait_impl_removed.ron&quot;&gt;&lt;code&gt;auto_trait_impl_removed&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;blob&#x2F;main&#x2F;src&#x2F;lints&#x2F;sized_impl_removed.ron&quot;&gt;&lt;code&gt;sized_impl_removed&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;blob&#x2F;main&#x2F;src&#x2F;lints&#x2F;derive_trait_impl_removed.ron&quot;&gt;&lt;code&gt;derive_trait_impl_removed&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; lints check that &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;reference&#x2F;special-types-and-traits.html#auto-traits&quot;&gt;auto-traits&lt;&#x2F;a&gt;, the special &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;reference&#x2F;special-types-and-traits.html#sized&quot;&gt;&lt;code&gt;Sized&lt;&#x2F;code&gt; marker trait&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;reference&#x2F;attributes&#x2F;derive.html&quot;&gt;built-in traits used in &lt;code&gt;#[derive(...)]&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; did not stop being implemented.&lt;&#x2F;li&gt;
&lt;li&gt;As of v0.16, &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; is not able to check method parameter or return types, trait associated types, or generic bounds. Here&#x27;s our &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;issues&#x2F;5&quot;&gt;tracking issue for not-yet-implemented lints&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;public-fields&quot;&gt;Public fields&lt;&#x2F;h2&gt;
&lt;p&gt;Rust structs may have publicly-visibile fields:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; NotChameleon&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; name&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Given such a struct, code in another crate is allowed to read or mutate that field directly:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; rename&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;value&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; NotChameleon&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    println!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Current name: &lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; value&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;name&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;   &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; reading the field&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    value&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;name &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Not a chameleon&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; mutating the field&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Rust enums cannot have fields — &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;rust-by-example&#x2F;custom_types&#x2F;enum.html&quot;&gt;they only have variants&lt;&#x2F;a&gt;.
If &lt;code&gt;pub struct NotChameleon&lt;&#x2F;code&gt; here became an enum, all accesses of the &lt;code&gt;name&lt;&#x2F;code&gt; field would be broken since that field no longer exists!&lt;&#x2F;p&gt;
&lt;p&gt;This is why our &lt;code&gt;pub struct Chameleon&lt;&#x2F;code&gt; must not expose any fields directly.
Instead, it uses accessor methods to provide both immutable and mutable access to its contents:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Chameleon&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    name&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Chameleon&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; name&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;name&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; one option: an explicit setter&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; set_name&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language&quot;&gt; self&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; name&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;name &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; name&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; another option, useful to allow appending&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; to the existing string without copying first&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; name_mut&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language&quot;&gt; self&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;name&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The implementation of these accessor methods may need to change as a result of the switch from struct to enum, but those changes are internal implementation details.
The methods&#x27; callers will remain blissfully ignorant of the change.&lt;&#x2F;p&gt;
&lt;p&gt;Since the &lt;code&gt;NotChameleon&lt;&#x2F;code&gt; struct has public fields, attempting to turn it into an enum without incrementing the major version will trigger &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;blob&#x2F;main&#x2F;src&#x2F;lints&#x2F;struct_with_pub_fields_changed_type.ron&quot;&gt;a lint in &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.
Our friend &lt;code&gt;Chameleon&lt;&#x2F;code&gt; has no public fields, and won&#x27;t have that problem.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;surprise-one-more-thing-to-consider&quot;&gt;Surprise! One more thing to consider&lt;&#x2F;h2&gt;
&lt;p&gt;Just for fun, let&#x27;s now say that &lt;code&gt;Chameleon&lt;&#x2F;code&gt; has &lt;em&gt;no fields whatsoever&lt;&#x2F;em&gt; — not even private fields.
Perhaps it&#x27;s a unit struct like &lt;code&gt;pub struct Chameleon&lt;&#x2F;code&gt; or an empty tuple struct like &lt;code&gt;pub struct Chameleon()&lt;&#x2F;code&gt;, or even an empty plain struct like &lt;code&gt;pub struct Chameleon {}&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Quick aside: did you know that changing between those three struct kinds can be a major breaking change?
Don&#x27;t worry, &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;blob&#x2F;main&#x2F;src&#x2F;lints&#x2F;struct_with_pub_fields_changed_type.ron&quot;&gt;has you covered&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;blob&#x2F;main&#x2F;src&#x2F;lints&#x2F;unit_struct_changed_kind.ron&quot;&gt;with its lints&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway...
Since the struct has no fields, there are no field accesses to worry about when changing it to an enum.
We&#x27;ll leave all  the &lt;code&gt;impl&lt;&#x2F;code&gt; blocks and &lt;code&gt;#[derive(...)]&lt;&#x2F;code&gt; attributes untouched, so all the methods and traits will be fine.
Looks good to me, ship it! 🚀&lt;&#x2F;p&gt;
&lt;p&gt;💥 &lt;strong&gt;Oops!!&lt;&#x2F;strong&gt; 💥 We broke semver 😬&lt;&#x2F;p&gt;
&lt;p&gt;Thankfully &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;oli-obk&#x2F;cargo_metadata&#x2F;blob&#x2F;main&#x2F;.github&#x2F;workflows&#x2F;release.yml#L51-L61&quot;&gt;runs in our release pipeline before &lt;code&gt;cargo publish&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;figure class=&quot;fullwidth&quot;&gt;
  
  

  

  
  
  

  
  
  
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;predr.ag&amp;#x2F;processed_images&amp;#x2F;caught-semver-violation.fc7581d36d730a17.png&quot; alt=&quot;Terminal output from cargo-semver-checks showing a &amp;#x27;constructible_struct_changed_type&amp;#x27; lint. The lint&amp;#x27;s description says &amp;quot;A struct became an enum or union, and is no longer publicly constructible with a struct literal.&amp;quot; The lint specifies the offending code as &amp;quot;struct semver_example::Chameleon became enum in file src&amp;#x2F;lib.rs:1&amp;quot;&quot;&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Here are examples of struct literal syntax for unit, tuple, and plain structs:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; _&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; UnitStruct&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; _&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; EmptyTupleStruct&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; _&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; EmptyPlainStruct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If the &lt;code&gt;Chameleon&lt;&#x2F;code&gt; struct was usable like this, converting it to an enum would be a major breaking change.
We couldn&#x27;t create an enum value like this — we have to specify a particular enum variant, not just supply the enum&#x27;s name with parens or curly braces.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, Rust&#x27;s built-in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;reference&#x2F;attributes&#x2F;type_system.html&quot;&gt;&lt;code&gt;#[non_exhaustive]&lt;&#x2F;code&gt; attribute&lt;&#x2F;a&gt; can prevent other crates from using the literal syntax to directly create &lt;code&gt;Chameleon&lt;&#x2F;code&gt; values, instead requiring them to use a constructor like &lt;code&gt;Chameleon::new()&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;But be careful: adding &lt;code&gt;#[non_exhaustive]&lt;&#x2F;code&gt; to an existing type or enum variant is itself a major breaking change — one that &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;blob&#x2F;main&#x2F;src&#x2F;lints&#x2F;struct_marked_non_exhaustive.ron&quot;&gt;will&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;blob&#x2F;main&#x2F;src&#x2F;lints&#x2F;enum_marked_non_exhaustive.ron&quot;&gt;always&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;blob&#x2F;main&#x2F;src&#x2F;lints&#x2F;variant_marked_non_exhaustive.ron&quot;&gt;catch&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;So for the &lt;code&gt;Chameleon&lt;&#x2F;code&gt; struct with no fields to be able to become an enum without a major change, it must have been marked &lt;code&gt;#[non_exhaustive]&lt;&#x2F;code&gt; &lt;em&gt;at the time it was originally added&lt;&#x2F;em&gt; to the public API.
One day, &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; might even start suggesting that you consider adding &lt;code&gt;#[non_exhaustive]&lt;&#x2F;code&gt; when you add a type like this to your crate&#x27;s public API — but that&#x27;s a topic for a future post.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Semver violations are much like memory safety violations:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;They can happen to anyone, even if you&#x27;re extremely careful.&lt;&#x2F;li&gt;
&lt;li&gt;There&#x27;s an overwhelming amount of real-world evidence of the above.&lt;&#x2F;li&gt;
&lt;li&gt;Automated tools can prevent semver violations, or at least significantly reduce their likelihood.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; is not yet a perfect tool.
For example, it catches &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;issues?q=is%3Aissue+is%3Aopen+label%3AC-enhancement&quot;&gt;an ever-growing yet still incomplete list of semver issues&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;At the same time, adopting &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; v0.16 today means 40 fewer possible kinds of semver violations in your crate&#x27;s API.
Specifically, it will prevent exactly the semver violations that have already happened &lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;toward-fearless-cargo-update&#x2F;&quot;&gt;in past versions of &lt;code&gt;clap&lt;&#x2F;code&gt;, &lt;code&gt;pyo3&lt;&#x2F;code&gt;, and many other top crates&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;What&#x27;s stopping your crate from adopting &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; today?
I&#x27;d love to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hachyderm.io&#x2F;@predrag&quot;&gt;hear about it&lt;&#x2F;a&gt; and resolve it!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>cargo-semver-checks today and in 2023</title>
        <published>2022-12-23T00:00:00+00:00</published>
        <updated>2022-12-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/cargo-semver-checks-today-and-in-2023/"/>
        <id>https://predr.ag/blog/cargo-semver-checks-today-and-in-2023/</id>
        
        <content type="html" xml:base="https://predr.ag/blog/cargo-semver-checks-today-and-in-2023/">&lt;p&gt;&lt;em&gt;&lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; ends 2022 with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;cargo-semver-checks&quot;&gt;40,000 downloads from crates.io&lt;&#x2F;a&gt;, able to prevent 30 different kinds of semver issues, and having done so &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;PredragGruevski&#x2F;status&#x2F;1587877518018756609&quot;&gt;in real-world use cases&lt;&#x2F;a&gt;.
Inspired by Yoshua Wuyts&#x27; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blog.yoshuawuyts.com&#x2F;rust-2023&#x2F;&quot;&gt;&quot;Rust in 2023 (by Yosh)&quot;&lt;&#x2F;a&gt; post, here are my thoughts on &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; in 2022, and what I look forward to in 2023 and beyond.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Following semver in Rust is a perfect example of a workflow worth automating:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Important to get right, painful if done wrong:&lt;&#x2F;strong&gt; &lt;code&gt;cargo&lt;&#x2F;code&gt; requires all crates to follow semver, so breaking semver in one crate can have a ripple effect across the ecosystem.&lt;label for=&quot;sn-re-learned&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-re-learned&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-re-learned&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;I recently &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;issues&#x2F;210&quot;&gt;re-learned this lesson&lt;&#x2F;a&gt; myself, for the umpteenth time.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;

But if done right, semver is completely invisible.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Countless complex rules:&lt;&#x2F;strong&gt; There are &lt;em&gt;hundreds&lt;&#x2F;em&gt; of ways to cause a breaking change, many of them non-obvious.&lt;label for=&quot;sn-countless-ways-to-break&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-countless-ways-to-break&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-countless-ways-to-break&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;issues&#x2F;5&quot;&gt;The tracking issue&lt;&#x2F;a&gt; for not-yet-implemented lints in &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; lists 60+ ways, and is far from an exhaustive list. I&#x27;m currently reading &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rust-for-rustaceans.com&#x2F;&quot;&gt;Rust for Rustaceans&lt;&#x2F;a&gt; and discovering new ways to break semver, each more surprising than the last. For a quick taste, check out my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;toward-fearless-cargo-update&#x2F;#breaking-semver-with-auto-traits&quot;&gt;previous blog post&lt;&#x2F;a&gt;.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Code that violates semver doesn&#x27;t look wrong&lt;&#x2F;strong&gt;: No code reviewer can be expected to reliably flag most of the semver issues, &lt;em&gt;even assuming&lt;&#x2F;em&gt; they are well-versed in all the semver rules.
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;PyO3&#x2F;pyo3&#x2F;issues&#x2F;285&quot;&gt;The evidence&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;clap-rs&#x2F;clap&#x2F;issues&#x2F;3876&quot;&gt;on this&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;RustCrypto&#x2F;utils&#x2F;issues&#x2F;22&quot;&gt;point is&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;PredragGruevski&#x2F;status&#x2F;1587877518018756609&quot;&gt;particularly&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;pdf&#x2F;2201.11821.pdf&quot;&gt;overwhelming&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Some might say the solution is to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wiktionary.org&#x2F;wiki&#x2F;git_gud&quot;&gt;&quot;git gud&quot;&lt;&#x2F;a&gt;.
I deeply &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;PredragGruevski&#x2F;status&#x2F;1289949333626986496&quot;&gt;respect operational excellence&lt;&#x2F;a&gt;, but this is not the way.&lt;&#x2F;p&gt;
&lt;p&gt;Civilization advances at the rate at which we develop robust abstractions.
I am writing this on a computer I cannot build, under a blanket I cannot weave, having enjoyed a meal with ingredients I cannot grow.
I dedicated &lt;em&gt;ten years&lt;&#x2F;em&gt; to math competitions,&lt;label for=&quot;sn-test-taking-strategies&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-test-taking-strategies&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-test-taking-strategies&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;And developing test-taking strategies aimed at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;to-ace-exams-get-better-at-the-easy-questions&#x2F;&quot;&gt;getting a perfect score given limited time!&lt;&#x2F;a&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
 and I can&#x27;t even calculate a logarithm by hand! Can you?&lt;label for=&quot;sn-newton-raphson&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-newton-raphson&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-newton-raphson&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;If my life depended on it, I&#x27;d use the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Newton%27s_method&quot;&gt;Newton-Raphson method&lt;&#x2F;a&gt; to approximate my way to it, but there&#x27;s &lt;em&gt;zero chance&lt;&#x2F;em&gt; that&#x27;s actually the best way. My friends with aero-astro engineering degrees still find it hilarious that I once used &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;kosmos&#x2F;blob&#x2F;master&#x2F;maneuver_planning.ks#L56-L70&quot;&gt;binary search to calculate orbital maneuvers&lt;&#x2F;a&gt; for Kerbal Space Program, instead of the closed-form formula that apparently existed 😅&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Gatekeeping to only include people with a PhD in &quot;Semver in Rust&quot; won&#x27;t cut it.&lt;&#x2F;p&gt;
&lt;p&gt;Yosh Wuyts &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blog.yoshuawuyts.com&#x2F;rust-2023&#x2F;&quot;&gt;quotes another Rust contributor&lt;&#x2F;a&gt; as saying: &quot;The job of an expert is to learn everything about a field there is to learn, and then distill it so that others don&#x27;t have to.&quot;&lt;label for=&quot;sn-quote&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-quote&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-quote&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;I&#x27;ll gladly put their name here if the quote is confirmed as coming from them. I wasn&#x27;t present when this was said, and didn&#x27;t want to risk misattributing.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
 I couldn&#x27;t agree more!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;2022-rust-semver-tedium-sparkling-heart&quot;&gt;2022: Rust + semver - tedium = 💖&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; was born in mid-July 2022, when I realized that building a semver linter boils down to only two things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;a list of machine-checkable rules, and&lt;&#x2F;li&gt;
&lt;li&gt;a system to check them.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;At a high level, that&#x27;s all &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; is: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;tree&#x2F;main&#x2F;src&#x2F;lints&quot;&gt;a checklist&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;blob&#x2F;4567eca9e1b9e957b2282140ca63e4a8c51349b3&#x2F;src&#x2F;check_release.rs#L142&quot;&gt;a for-loop over it&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;As is usually the case:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;I wasn&#x27;t the first person to realize this.
&lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; isn&#x27;t the first attempt at a semver linter for Rust.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; stands on the shoulders of giants: without rustdoc JSON and serde, the same work would have taken ten times as long.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The novel trick in &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; is that lint rules are written &lt;em&gt;declaratively&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Given the need to have hundreds of different lints defined over an ever-changing data format,&lt;label for=&quot;sn-rustdoc-json-unstable&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-rustdoc-json-unstable&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-rustdoc-json-unstable&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;The rustdoc JSON format is unstable and frequently has breaking changes — sometimes even multiple times per week in nightly Rust.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
 this is a huge win.&lt;&#x2F;p&gt;
&lt;p&gt;But creating a good declarative query language is a much harder problem than semver!
Generally one shouldn&#x27;t replace an easier problem with a harder one.
This is why linters rarely build their own query language.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, I spent the last 7+ years of my career working on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blog.kensho.com&#x2F;database-agnostic-querying-is-unavoidable-at-scale-18895f6df2f0&quot;&gt;high-performance query languages for heterogeneous data&lt;&#x2F;a&gt;, so I didn&#x27;t need to start from scratch.
Instead, I just plugged in my existing &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;trustfall&quot;&gt;Trustfall query engine&lt;&#x2F;a&gt; which is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.hytradboi.com&#x2F;2022&#x2F;how-to-query-almost-everything&quot;&gt;able to query any data source(s)&lt;&#x2F;a&gt; no matter whether they are local files, remote APIs, or a terabyte-scale SQL cluster.&lt;label for=&quot;sn-trustfall-examples&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-trustfall-examples&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-trustfall-examples&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;Ever wonder &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;play.predr.ag&#x2F;rustdoc#?f=1&amp;amp;q=IyBJdGVtcyB3aGVyZSBsaW50cyB3ZXJlIGFsbG93ZWQuIE5vdCBhbGwgY3JhdGVzIGhhdmUgdGhlc2UsCiMgdHJ5IG9uZSBvZjogYW55aG93LCBjbGFwLCBodHRwLCBodHRwYXJzZSwgaHlwZXIsIGl0ZXJ0b29scy4KcXVlcnkgewogIENyYXRlIHsKICAgIGl0ZW0gewogICAgICBuYW1lIEBvdXRwdXQKCiAgICAgIGF0dHJpYnV0ZSB7CiAgICAgICAgYXR0cjogdmFsdWUgQG91dHB1dAogICAgICAgICAgICAgICAgICAgIEBmaWx0ZXIob3A6ICJyZWdleCIsIHZhbHVlOiBbIiRwYXR0ZXJuIl0pCiAgICAgIH0KCiAgICAgIHNwYW4gewogICAgICAgIGZpbGVuYW1lIEBvdXRwdXQKICAgICAgICBiZWdpbl9saW5lIEBvdXRwdXQKICAgICAgfQogICAgfQogIH0KfQ%3D%3D&amp;amp;v=ewogICJwYXR0ZXJuIjogIiNcXFthbGxvd1xcKC4rXFwpXFxdIgp9&quot;&gt;which lints do popular crates like &lt;code&gt;itertools&lt;&#x2F;code&gt; allow in their code&lt;&#x2F;a&gt;? Or maybe you&#x27;re curious &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;play.predr.ag&#x2F;hackernews#?f=1&amp;amp;q=IyBDcm9zcyBBUEkgcXVlcnkgKEFsZ29saWEgKyBGaXJlYmFzZSk6CiMgRmluZCBjb21tZW50cyBvbiBzdG9yaWVzIGFib3V0ICJvcGVuYWkuY29tIiB3aGVyZQojIHRoZSBjb21tZW50ZXIncyBiaW8gaGFzIGF0IGxlYXN0IG9uZSBHaXRIdWIgb3IgVHdpdHRlciBsaW5rCnF1ZXJ5IHsKICAjIFRoaXMgaGl0cyB0aGUgQWxnb2xpYSBzZWFyY2ggQVBJIGZvciBIYWNrZXJOZXdzLgogICMgVGhlIHN0b3JpZXMvY29tbWVudHMvdXNlcnMgZGF0YSBpcyBmcm9tIHRoZSBGaXJlYmFzZSBITiBBUEkuCiAgIyBUaGUgdHJhbnNpdGlvbiBpcyBzZWFtbGVzcyAtLSBpdCBpc24ndCB2aXNpYmxlIGZyb20gdGhlIHF1ZXJ5LgogIFNlYXJjaEJ5RGF0ZShxdWVyeTogIm9wZW5haS5jb20iKSB7CiAgICAuLi4gb24gU3RvcnkgewogICAgICAjIEFsbCBkYXRhIGZyb20gaGVyZSBvbndhcmQgaXMgZnJvbSB0aGUgRmlyZWJhc2UgQVBJLgogICAgICBzdG9yeVRpdGxlOiB0aXRsZSBAb3V0cHV0CiAgICAgIHN0b3J5TGluazogdXJsIEBvdXRwdXQKICAgICAgc3Rvcnk6IHN1Ym1pdHRlZFVybCBAb3V0cHV0CiAgICAgICAgICAgICAgICAgICAgICAgICAgQGZpbHRlcihvcDogInJlZ2V4IiwgdmFsdWU6IFsiJHNpdGVQYXR0ZXJuIl0pCgogICAgICBjb21tZW50IHsKICAgICAgICByZXBseSBAcmVjdXJzZShkZXB0aDogNSkgewogICAgICAgICAgY29tbWVudDogdGV4dFBsYWluIEBvdXRwdXQKCiAgICAgICAgICBieVVzZXIgewogICAgICAgICAgICBjb21tZW50ZXI6IGlkIEBvdXRwdXQKICAgICAgICAgICAgY29tbWVudGVyQmlvOiBhYm91dFBsYWluIEBvdXRwdXQKCiAgICAgICAgICAgICMgVGhlIHByb2ZpbGUgbXVzdCBoYXZlIGF0IGxlYXN0IG9uZQogICAgICAgICAgICAjIGxpbmsgdGhhdCBwb2ludHMgdG8gZWl0aGVyIEdpdEh1YiBvciBUd2l0dGVyLgogICAgICAgICAgICBsaW5rCiAgICAgICAgICAgICAgQGZvbGQKICAgICAgICAgICAgICBAdHJhbnNmb3JtKG9wOiAiY291bnQiKQogICAgICAgICAgICAgIEBmaWx0ZXIob3A6ICI%2BPSIsIHZhbHVlOiBbIiRtaW5Qcm9maWxlcyJdKQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgY29tbWVudGVySURzOiB1cmwgQGZpbHRlcihvcDogInJlZ2V4IiwgdmFsdWU6IFsiJHNvY2lhbFBhdHRlcm4iXSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBAb3V0cHV0CiAgICAgICAgICAgIH0KICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIH0KICAgIH0KICB9Cn0%3D&amp;amp;v=ewogICJzaXRlUGF0dGVybiI6ICJodHRwW3NdOi8vKFteLl0qXFwuKSpvcGVuYWkuY29tLy4qIiwKICAibWluUHJvZmlsZXMiOiAxLAogICJzb2NpYWxQYXR0ZXJuIjogIihnaXRodWJ8dHdpdHRlcilcXC5jb20vIgp9&quot;&gt;which GitHub or Twitter users comment on HackerNews stories about OpenAI&lt;&#x2F;a&gt;? The answers are one browser-executed query away!&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Thanks to Trustfall, each cargo-semver-checks lint is a type-checked structured query in Trustfall&#x27;s GraphQL-like syntax.
(More on this in future blog posts!)
In practice, this means:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;New lints are super easy to add: writing a new lint takes only 1-2 minutes.
The vast majority of effort can then be spent on great test cases that reflect the diversity of use cases for each Rust language construct.&lt;&#x2F;li&gt;
&lt;li&gt;Lints are not tied to a specific rustdoc JSON format version.
Even though the rustdoc JSON format changes frequently, the changes are absorbed by the Trustfall adapter for rustdoc and are completely invisible to the lints — an airtight abstraction layer.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; benefits from the performance and correctness guarantees of Trustfall, whose optimizations and test suite are far more intricate than would be feasible to write for a semver-checker alone.
(If you&#x27;d like to hear more, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hachyderm.io&#x2F;@predrag&quot;&gt;tell me&lt;&#x2F;a&gt; and I&#x27;ll write more blog posts!)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;All this allowed us to go from zero to 30 different semver lints in just five months.&lt;&#x2F;p&gt;
&lt;p&gt;We are ending 2022 on a particularly high note: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;PredragGruevski&#x2F;status&#x2F;1584563200011382784&quot;&gt;four students&lt;&#x2F;a&gt; have begun contributing to &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; as part of their Bachelors&#x27; theses!
The pace of development has sped up dramatically thanks to their hard work, and the codebase is healthier than ever.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;looking-ahead-to-2023&quot;&gt;Looking ahead to 2023&lt;&#x2F;h2&gt;
&lt;p&gt;At RustConf 2022 I had the pleasure of meeting several &lt;code&gt;cargo&lt;&#x2F;code&gt; team members, and we decided that the end goal for &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;issues&#x2F;61&quot;&gt;merging into &lt;code&gt;cargo&lt;&#x2F;code&gt; itself&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Another goal for &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; is adding &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;issues&#x2F;5&quot;&gt;even more lints&lt;&#x2F;a&gt; to prevent more kinds of semver violations.&lt;&#x2F;p&gt;
&lt;p&gt;These goals are self-explanatory, and I won&#x27;t dig into them further. Instead, I&#x27;ll mention three of my personal favorite things I&#x27;d like to see in &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; in 2023.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;proactively-discover-and-prevent-false-positives&quot;&gt;Proactively discover and prevent false-positives&lt;&#x2F;h3&gt;
&lt;p&gt;A false-positive error in &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; is when the tool &lt;em&gt;incorrectly&lt;&#x2F;em&gt; claims it found a semver violation.
I consider false-positives extremely serious bugs&lt;label for=&quot;sn-false-negative&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-false-negative&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-false-negative&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;Much more serious than false-negatives! A false-&lt;em&gt;negative&lt;&#x2F;em&gt; means there &lt;em&gt;was&lt;&#x2F;em&gt; a semver violation but the tool &lt;em&gt;didn&#x27;t&lt;&#x2F;em&gt; find it. There are dozens of ways to break semver that &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; can&#x27;t yet detect, each of which is a false-negative.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
 because they give the user incorrect advice, confusing them and slowing them down while also hurting the credibility of &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; itself.&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately, in 2022 our users &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;issues&#x2F;147&quot;&gt;reported&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;issues&#x2F;193&quot;&gt;multiple&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;issues&#x2F;167&quot;&gt;false-positive&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;issues&#x2F;202&quot;&gt;errors&lt;&#x2F;a&gt;.
I am grateful to everyone that spent their precious time helping debug problems that shouldn&#x27;t have happened in the first place.&lt;&#x2F;p&gt;
&lt;p&gt;We have &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;issues&#x2F;225&quot;&gt;already begun strengthening&lt;&#x2F;a&gt; the &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; test systems to discover and prevent future false-positives, so our users won&#x27;t have to. In the process, we already discovered and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;pull&#x2F;222&quot;&gt;fixed three&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;pull&#x2F;220&quot;&gt;previously-unknown&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;cargo-semver-checks&#x2F;pull&#x2F;218&quot;&gt;false-positives&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In 2023, we plan to take a page from Rust&#x27;s book: testing &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; on the most popular crates on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;&quot;&gt;crates.io&lt;&#x2F;a&gt; as part of our release process.
This would have a dual benefit: in addition to proactively discovering false-positives, it would also ensure &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; is ready to be adopted by those crates at their maintainers&#x27; convenience.
And if we happen to discover more semver issues in the wild, that&#x27;ll be a nice bonus!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;faster-semver-checking-via-rustdoc-caching&quot;&gt;Faster semver-checking via rustdoc caching&lt;&#x2F;h3&gt;
&lt;p&gt;A &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; run consists of two steps: generating rustdoc JSON, and running lints over the generated JSON files.&lt;&#x2F;p&gt;
&lt;p&gt;The &quot;run the lints&quot; step is &lt;em&gt;much faster&lt;&#x2F;em&gt;&lt;label for=&quot;sn-much-faster&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-much-faster&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-much-faster&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;Even though we&#x27;ve put in negligible effort at optimizing them beyond what Trustfall provides out of the box.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
 than the process of generating the rustdoc, which can take a few minutes in CI environments with low core counts like GitHub Actions.&lt;&#x2F;p&gt;
&lt;p&gt;In 2023, we&#x27;ll implement rustdoc caching to limit how often the rustdoc has to be rebuilt.&lt;&#x2F;p&gt;
&lt;p&gt;We expect to cut rustdoc generation time in half: we&#x27;ll still have to generate the current version&#x27;s rustdoc, but we can avoid repeatedly rebuilding rustdoc for crate versions that are already published on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;&quot;&gt;crates.io&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;semver-check-prs-not-just-cargo-publish&quot;&gt;Semver-check PRs, not just &lt;code&gt;cargo publish&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Currently, &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; is most ergonomic when used right before &lt;code&gt;cargo publish&lt;&#x2F;code&gt;: it checks whether the publish step with the specified version&lt;label for=&quot;sn-assumed-version&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-assumed-version&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-assumed-version&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;If the version in &lt;code&gt;Cargo.toml&lt;&#x2F;code&gt; is already on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;crates.io&quot;&gt;crates.io&lt;&#x2F;a&gt;, it assumes a patch version bump.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
 would result in a semver-compliant release.&lt;&#x2F;p&gt;
&lt;p&gt;But wouldn&#x27;t it be nice to know about breaking changes in a pull request &lt;em&gt;before&lt;&#x2F;em&gt; merging it and committing to a major version bump?
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;libp2p&#x2F;rust-libp2p&quot;&gt;Multiple&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;pest-parser&#x2F;pest&quot;&gt;projects&lt;&#x2F;a&gt; have already begun running &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; like this, generally via custom scripts they&#x27;ve adapted specifically for that purpose.&lt;&#x2F;p&gt;
&lt;p&gt;In 2023, I hope we&#x27;re able to make this an officially-supported mode of operation, complete with a GitHub Action.
Bonus points if the Action reports semver issues as inline PR comments using the lints&#x27; span information!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;onwards&quot;&gt;Onwards!&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m thrilled and humbled by the response that &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; has received in the Rust community.
I&#x27;ve never been more excited about building the future with Rust, and I&#x27;m excited to see what 2023 has in store for &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt; and the Rust ecosystem as a whole.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Falsehoods programmers believe about undefined behavior</title>
        <published>2022-11-27T00:00:00+00:00</published>
        <updated>2022-11-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/falsehoods-programmers-believe-about-undefined-behavior/"/>
        <id>https://predr.ag/blog/falsehoods-programmers-believe-about-undefined-behavior/</id>
        
        <content type="html" xml:base="https://predr.ag/blog/falsehoods-programmers-believe-about-undefined-behavior/">&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Undefined_behavior&quot;&gt;Undefined behavior (UB)&lt;&#x2F;a&gt; is a tricky concept in programming languages and compilers.
Over the many years I&#x27;ve been an industry mentor for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;playlist?list=PLUl4u3cNGP63VIBQVWguXxZZi0566y7Wf&quot;&gt;MIT&#x27;s 6.172 Performance Engineering course&lt;&#x2F;a&gt;,&lt;label for=&quot;sn-class-6172&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-class-6172&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-class-6172&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;An &lt;em&gt;excellent&lt;&#x2F;em&gt; class that I &lt;em&gt;highly&lt;&#x2F;em&gt; recommend. It&#x27;s very thorough and hands-on, at the expense of also requiring a lot of work at a very fast pace. When I took it as an undergrad, that was a great tradeoff, but YMMV.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
 I&#x27;ve heard many misconceptions about what the compiler guarantees in the presence of UB.
This is unfortunate but not surprising!&lt;&#x2F;p&gt;
&lt;p&gt;For a primer on undefined behavior and why we can&#x27;t just &quot;define all the behaviors,&quot; I highly recommend &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=yG1OZ69H_-o&quot;&gt;Chandler Carruth&#x27;s talk&lt;&#x2F;a&gt; &quot;Garbage In, Garbage Out: Arguing about Undefined Behavior with Nasal Demons.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;You might also be familiar with my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;tags&#x2F;compiler-adventures&#x2F;&quot;&gt;Compiler Adventures blog series&lt;&#x2F;a&gt; on how compiler optimizations work.
An upcoming episode is about implementing optimizations that take advantage of undefined behavior like dividing by zero, where we&#x27;ll see UB &quot;from the other side.&quot;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;undefined-behavior-implementation-defined-behavior&quot;&gt;Undefined behavior != implementation-defined behavior&lt;&#x2F;h2&gt;
&lt;p&gt;Undefined behavior is not the same as implementation-defined behavior.&lt;label for=&quot;sn-unspec-behavior&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-unspec-behavior&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-unspec-behavior&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;Undefined behavior is also not the same as &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Unspecified_behavior&quot;&gt;&lt;em&gt;unspecified behavior&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;, which is similar to implementation-defined behavior minus the requirement that the implementation document its choices and stick to them. Here we&#x27;re focusing on &lt;em&gt;undefined&lt;&#x2F;em&gt; behavior, not &lt;em&gt;unspecified&lt;&#x2F;em&gt; behavior, so we&#x27;ll lump &lt;em&gt;unspecified&lt;&#x2F;em&gt; behavior and implementation-defined behavior together.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;

Program behaviors fall into &lt;em&gt;three&lt;&#x2F;em&gt; buckets, not two:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Specification-defined:&lt;&#x2F;strong&gt; The programming language itself defines what happens. This is the vast majority of every program.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Implementation-defined:&lt;&#x2F;strong&gt; The exact behavior is defined by your compiler, operating system, or hardware. For example: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;C_data_types#Basic_types&quot;&gt;how many bits exactly&lt;&#x2F;a&gt; are in a &lt;code&gt;char&lt;&#x2F;code&gt; or &lt;code&gt;int&lt;&#x2F;code&gt; in C++.&lt;label for=&quot;sn-char-width&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-char-width&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-char-width&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;The specification guarantees &lt;em&gt;at least&lt;&#x2F;em&gt; 8 bits for &lt;code&gt;char&lt;&#x2F;code&gt; and at least 16 bits for &lt;code&gt;int&lt;&#x2F;code&gt;. The rest is implementation-defined.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Undefined behavior&lt;&#x2F;strong&gt;: &lt;em&gt;Anything&lt;&#x2F;em&gt; is allowed to happen, and you might no longer have a computer left after it all happens. No outcome is a bug if caused by UB. For example: signed integer overflow in C, or using &lt;code&gt;unsafe&lt;&#x2F;code&gt; to create two &lt;code&gt;&amp;amp;mut&lt;&#x2F;code&gt; references to the same data in Rust.&lt;label for=&quot;sn-ub-in-c-cpp&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-ub-in-c-cpp&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-ub-in-c-cpp&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;Wikipedia has &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Undefined_behavior#Examples_in_C_and_C++&quot;&gt;an excellent list of examples&lt;&#x2F;a&gt; if you&#x27;d like to see more.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Here&#x27;s the list of guarantees compilers make about the outcomes of undefined behavior:&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s the whole list. No, I didn&#x27;t forget any items. Yes, seriously.&lt;&#x2F;p&gt;
&lt;p&gt;It is possible to analyze how UB affects &lt;em&gt;a specific program&lt;&#x2F;em&gt; when compiled by a &lt;em&gt;specific compiler&lt;&#x2F;em&gt; or executed on &lt;em&gt;a specific target platform&lt;&#x2F;em&gt;.
For example, there exist exotic compilers, operating systems, and hardware that offer additional guarantees&lt;label for=&quot;sn-cheri&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-cheri&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-cheri&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;Like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.cl.cam.ac.uk&#x2F;research&#x2F;security&#x2F;ctsrd&#x2F;cheri&#x2F;cheri-faq.html&quot;&gt;CHERI&lt;&#x2F;a&gt;, with awesome powers around pointer safety.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
 relative to most common platforms, which only guarantee OS-level &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Process_isolation&quot;&gt;process isolation&lt;&#x2F;a&gt;.
We aren&#x27;t talking about those in this post.&lt;&#x2F;p&gt;
&lt;p&gt;The mindset for this post is this: &quot;If my program contains UB, and the compiler produced a binary that does X, is that a compiler bug?&quot;&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s not a compiler bug.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;all-of-the-following-assumptions-are-wrong&quot;&gt;All of the following assumptions are wrong&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;falsehoods-about-when-ub-happens&quot;&gt;Falsehoods about when UB &quot;happens&quot;&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;Undefined behavior only &quot;happens&quot; at high optimization levels like &lt;span class=&quot;nobr&quot;&gt;&lt;code&gt;-O2&lt;&#x2F;code&gt;&lt;&#x2F;span&gt; or &lt;span class=&quot;nobr&quot;&gt;&lt;code&gt;-O3&lt;&#x2F;code&gt;&lt;&#x2F;span&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;If I turn off optimizations with a flag like &lt;span class=&quot;nobr&quot;&gt;&lt;code&gt;-O0&lt;&#x2F;code&gt;&lt;&#x2F;span&gt;, then there&#x27;s no UB.&lt;&#x2F;li&gt;
&lt;li&gt;If I include debug symbols in the build, there&#x27;s no UB.&lt;&#x2F;li&gt;
&lt;li&gt;If I run the program under a debugger, there&#x27;s no UB.&lt;&#x2F;li&gt;
&lt;li&gt;Okay there&#x27;s still UB with all of these, but my code will &quot;do the right thing&quot; regardless.&lt;&#x2F;li&gt;
&lt;li&gt;It will either &quot;do the right thing&quot; or crash with a &lt;code&gt;Segmentation Fault&lt;&#x2F;code&gt; (&lt;code&gt;SIGSEGV&lt;&#x2F;code&gt; signal).&lt;&#x2F;li&gt;
&lt;li&gt;It will either &quot;do the right thing&quot; or crash &lt;em&gt;somehow&lt;&#x2F;em&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;It will either &quot;do the right thing&quot; or crash or infinite-loop or deadlock.&lt;&#x2F;li&gt;
&lt;li&gt;At least it won&#x27;t run some unrelated code from elsewhere in the program.&lt;&#x2F;li&gt;
&lt;li&gt;At least it won&#x27;t &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kristerw.blogspot.com&#x2F;2017&#x2F;09&#x2F;why-undefined-behavior-may-call-never.html&quot;&gt;run any unreachable code&lt;&#x2F;a&gt; the program might contain.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;falsehoods-around-the-behavior-of-executing-ub&quot;&gt;Falsehoods around the behavior of executing UB&lt;&#x2F;h3&gt;
&lt;ol start=&quot;11&quot;&gt;
&lt;li&gt;If a line with UB previously &quot;did the right thing,&quot; then it will continue to &quot;do the right thing&quot; the next time we run the program.&lt;&#x2F;li&gt;
&lt;li&gt;The UB line will at least continue to &quot;do the right thing&quot; while the program is still running.&lt;&#x2F;li&gt;
&lt;li&gt;It&#x27;s possible to determine if a previous line was UB and prevent it from causing problems.&lt;&#x2F;li&gt;
&lt;li&gt;At least the impact of the UB is limited to code which uses values produced from the UB.&lt;&#x2F;li&gt;
&lt;li&gt;At least the impact of the UB is limited to code which is in the same compilation unit as the line with UB.&lt;&#x2F;li&gt;
&lt;li id=&quot;falsehood-16&quot;&gt;Okay, but at least the impact of the UB is limited to code which runs after the line with UB.&lt;label for=&quot;sn-runs-after-ub&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-runs-after-ub&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-runs-after-ub&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;UB is explicitly allowed to alter the behavior of other code, even including operations preceding it! &quot;Alter&quot; here encompasses corrupting, undoing, or altogether preventing (as if it never happened) the outcomes of that other code. To learn more and see examples of UB causing &quot;time travel,&quot; check out &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;devblogs.microsoft.com&#x2F;oldnewthing&#x2F;20140627-00&#x2F;?p=633&quot;&gt;this blog post&lt;&#x2F;a&gt;.&lt;br&gt;&lt;br&gt;Thanks to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;rust&#x2F;comments&#x2F;z7115a&#x2F;comment&#x2F;iy4w557&#x2F;&quot;&gt;these two&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;rust&#x2F;comments&#x2F;z7115a&#x2F;comment&#x2F;iy51rtl&#x2F;&quot;&gt;Reddit posts&lt;&#x2F;a&gt; for suggesting better wording for these items. For the original text, see the Errata section at the end of this post.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;falsehoods-about-the-possible-outcomes-of-ub&quot;&gt;Falsehoods about the possible outcomes of UB&lt;&#x2F;h3&gt;
&lt;ol start=&quot;17&quot;&gt;
&lt;li&gt; At least it won&#x27;t corrupt the memory of the program.&lt;&#x2F;li&gt;
&lt;li&gt; At least it won&#x27;t corrupt the memory of the program other than where the UB-affected data was located.&lt;&#x2F;li&gt;
&lt;li&gt; At least it won&#x27;t corrupt the heap.&lt;&#x2F;li&gt;
&lt;li&gt; At least it won&#x27;t corrupt the stack.&lt;&#x2F;li&gt;
&lt;li&gt; At least it won&#x27;t corrupt the current stack frame. (My name for this is the &quot;local variables are safely in registers&quot; fallacy.)&lt;&#x2F;li&gt;
&lt;li&gt; At least it won&#x27;t corrupt the stack pointer.&lt;&#x2F;li&gt;
&lt;li&gt; At least it won&#x27;t corrupt the CPU flags register &#x2F; any other CPU state.&lt;&#x2F;li&gt;
&lt;li&gt; At least it won&#x27;t corrupt the &lt;em&gt;executable&lt;&#x2F;em&gt; memory of the program.&lt;label for=&quot;sn-w-x-bits&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-w-x-bits&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-w-x-bits&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;OS and hardware security features like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;W%5EX&quot;&gt;W^X&lt;&#x2F;a&gt; can make this unlikely, but self-modifying programs can be built so it&#x27;s in principle possible through UB as well. Certainly there&#x27;s no guarantee that UB &lt;em&gt;won&#x27;t&lt;&#x2F;em&gt; do this!&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;li&gt;
&lt;li&gt; At least it won&#x27;t corrupt streams like stdout or stderr.&lt;&#x2F;li&gt;
&lt;li&gt; At least it won&#x27;t overwrite any files the program already had open.&lt;&#x2F;li&gt;
&lt;li&gt; At least it won&#x27;t open new files and overwrite them.&lt;&#x2F;li&gt;
&lt;li&gt; At least &lt;a href=&quot;https:&#x2F;&#x2F;kristerw.blogspot.com&#x2F;2017&#x2F;09&#x2F;why-undefined-behavior-may-call-never.html&quot;&gt;it won&#x27;t completely wipe the drive.&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt; At least it won&#x27;t damage or destroy any hardware components.&lt;label for=&quot;sn-ub-hardware-damage&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-ub-hardware-damage&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-ub-hardware-damage&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;Not all devices have the same level of self-protection against bad inputs written to their control registers. This is the kind of lesson one tends to learn the hard way.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;li&gt;
&lt;li&gt; At least it won&#x27;t start playing Doom if the program didn&#x27;t already have the Doom source code in it.&lt;label for=&quot;sn-ub-doom&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-ub-doom&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-ub-doom&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;I&#x27;d be quite impressed if you made a compiler that makes programs run Doom when they encounter UB. Consider it a challenge!&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;falsehoods-like-but-it-worked-fine-before&quot;&gt;Falsehoods like &quot;but it worked fine before&quot;&lt;&#x2F;h3&gt;
&lt;ol start=&quot;31&quot;&gt;
&lt;li&gt;If a UB-containing program &quot;worked fine&quot; previously, recompiling the program without any code changes will still produce a binary that &quot;works fine.&quot;&lt;&#x2F;li&gt;
&lt;li&gt;Recompiling without code changes and with the same compiler and flags will produce a binary that still &quot;works fine.&quot;&lt;&#x2F;li&gt;
&lt;li&gt;Recompiling as above + on the same machine will produce a binary that still &quot;works fine.&quot;&lt;&#x2F;li&gt;
&lt;li&gt;Recompiling as above + if you haven&#x27;t rebooted the machine since the last compilation will produce a binary that still &quot;works fine.&quot;&lt;&#x2F;li&gt;
&lt;li&gt;Recompiling as above + with the same environment variables will produce a binary that still &quot;works fine.&quot;&lt;&#x2F;li&gt;
&lt;li&gt;Recompiling as above + at the same time of day and day of week as before, during a Lunar eclipse, having first sacrificed a fresh stick of RAM to the binary gods, will produce a binary that still &quot;works fine.&quot;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;falsehoods-about-self-consistent-behavior-of-ub&quot;&gt;Falsehoods about self-consistent behavior of UB&lt;&#x2F;h3&gt;
&lt;ol start=&quot;37&quot;&gt;
&lt;li&gt;Multiple runs of the program compiled as above and with the same inputs will produce the same behavior in each run.&lt;&#x2F;li&gt;
&lt;li&gt;Those multiple runs will produce the same behavior if the program, ignoring the UB, is deterministic.&lt;&#x2F;li&gt;
&lt;li&gt;But they will if the program is also single-threaded.&lt;&#x2F;li&gt;
&lt;li&gt;But they will if the program also doesn&#x27;t read any external data (files, network, environment variables, etc.).&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;community-contributed-falsehoods-around-ub&quot;&gt;Community-contributed falsehoods around UB&lt;&#x2F;h3&gt;
&lt;ol start=&quot;41&quot;&gt;
&lt;li&gt;Using a debugger on a UB-containing program will show program state that corresponds to the source code.&lt;label for=&quot;sn-debugging&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-debugging&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-debugging&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;This is a corrolary of &lt;a href=&quot;#falsehood-16&quot;&gt;falsehood #16&lt;&#x2F;a&gt;, further explained in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;devblogs.microsoft.com&#x2F;oldnewthing&#x2F;20140627-00&#x2F;?p=633&quot;&gt;this post&lt;&#x2F;a&gt;. UB can corrupt the behavior of the program both before and after the UB, so the source code you see in your editor no longer matches the actual executing program. Of course, you can still use the debugger to step through assembly instructions and view register state. But highly optimized assembly isn&#x27;t easy to understand to begin with, and UB-induced weirdness will only make it harder. Overall, a situation that is best avoided. Contributed &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;rust&#x2F;comments&#x2F;z7115a&#x2F;comment&#x2F;iy51rtl&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Undefined behavior is purely a runtime phenomenon.&lt;label for=&quot;sn-runtime-only&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-runtime-only&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-runtime-only&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;In Rust, a counter-example is misusing &lt;span class=&quot;nobr&quot;&gt;&lt;code&gt;#[no_mangle]&lt;&#x2F;code&gt;&lt;&#x2F;span&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;old.reddit.com&#x2F;r&#x2F;rust&#x2F;comments&#x2F;z7115a&#x2F;falsehoods_programmers_believe_about_undefined&#x2F;iy4ztkm&#x2F;&quot;&gt;to overwrite a symbol with an incorrect type&lt;&#x2F;a&gt;. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=33776047&quot;&gt;A C++ counter-example&lt;&#x2F;a&gt; is violations of the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;One_Definition_Rule&quot;&gt;One Definition Rule (ODR)&lt;&#x2F;a&gt;, some of which the compiler is not required to report before causing havoc.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;false-expectations-around-ub-in-general&quot;&gt;False expectations around UB, in general&lt;&#x2F;h3&gt;
&lt;ol start=&quot;43&quot;&gt;
&lt;li&gt;Any kind of reasonable or unreasonable behavior happening with any consistency or any guarantee of any sort.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The moment your program contains UB, &lt;strong&gt;all bets are off&lt;&#x2F;strong&gt;.
Even if it&#x27;s just one little UB.
Even if it&#x27;s never executed.
Even if you don&#x27;t know it&#x27;s there at all.
Probably even if you wrote the language spec and compiler yourself.&lt;label for=&quot;sn-from-experience&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-from-experience&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-from-experience&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;Speaking from experience. Hopefully not one you have to relive to believe.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;This is not to say that all outcomes in the list above are equally likely, or even plausible.&lt;label for=&quot;sn-ub-doom2&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-ub-doom2&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-ub-doom2&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;Especially the one about running Doom.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;

But they are all allowed, valid, spec-compliant behavior.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s perfectly possible that your program has UB, and it&#x27;s been running fine for years without issues.
That&#x27;s great!
I&#x27;m happy to hear it!
I&#x27;m not even saying you need to go back and rewrite it to remove the UB.
But as you make your decisions, it&#x27;s good to know the full picture of what the compiler will or won&#x27;t guarantee for your program.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;honorable-mention-for-one-special-assumption&quot;&gt;Honorable mention for one special assumption&lt;&#x2F;h2&gt;
&lt;p&gt;&quot;If the program compiles without errors then it doesn&#x27;t have UB.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;This is 100% false in C and C++.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s also false as stated in Rust, but with one tweak it&#x27;s &lt;em&gt;almost&lt;&#x2F;em&gt; true.
If your Rust program never uses &lt;code&gt;unsafe&lt;&#x2F;code&gt;, then it &lt;em&gt;should&lt;&#x2F;em&gt; be free of UB.
In other words: causing UB without &lt;code&gt;unsafe&lt;&#x2F;code&gt; is considered
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;issues?q=is%3Aopen+is%3Aissue+label%3AI-unsound&quot;&gt;a bug in the Rust compiler&lt;&#x2F;a&gt;.
These are rare and you are quite unlikely to run into them.&lt;&#x2F;p&gt;
&lt;p&gt;When Rust &lt;code&gt;unsafe&lt;&#x2F;code&gt; is used, then all bets are off just as in C or C++.
But the assumption that &quot;Safe Rust programs that compile are free of UB&quot; is &lt;em&gt;mostly true&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This is not an easy feat.
We owe a debt of gratitude to the folks who cumulatively put engineer-centuries into making it so.
It&#x27;s Thanksgiving, and I thank you!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;errata-and-edit-history&quot;&gt;Errata and edit history&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;2022-11-29-items-13-16-corrected-and-updated&quot;&gt;2022-11-29: Items 13-16 corrected and updated&lt;&#x2F;h3&gt;
&lt;p&gt;The original version of this post contained the following items at positions 13-16 in the list:&lt;&#x2F;p&gt;
&lt;ol start=&quot;13&quot;&gt;
&lt;li&gt;But if the line with UB isn&#x27;t executed, then the program will work normally as if the UB wasn&#x27;t there.&lt;&#x2F;li&gt;
&lt;li&gt;Okay, but if the line with UB is &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Dead_code&quot;&gt;unreachable (dead) code&lt;&#x2F;a&gt;, then it&#x27;s as if the UB wasn&#x27;t there.&lt;label for=&quot;sn-dead-code&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-dead-code&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-dead-code&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;Surprising, right? It isn&#x27;t obvious why code that should be perfectly safe to delete would have any effect on the behavior of the program. But it turns out that sometimes &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.ralfj.de&#x2F;blog&#x2F;2020&#x2F;07&#x2F;15&#x2F;unused-data.html&quot;&gt;optimizations can make some dead code live again&lt;&#x2F;a&gt;. EDIT: This was originally footnote #6 before being moved down here.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;If the line with UB is unreachable code, then the program won&#x27;t crash because of the UB.&lt;&#x2F;li&gt;
&lt;li&gt;If the line with UB is unreachable code, then the program will at least stop running &lt;em&gt;somehow&lt;&#x2F;em&gt; and &lt;em&gt;at some point&lt;&#x2F;em&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This wording was not precise enough, and as a result the claims were arguably incorrect as stated.
I have updated the post near those claims to better capture the subtleties involved.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2022-11-29-added-community-contributed-items&quot;&gt;2022-11-29: Added community-contributed items&lt;&#x2F;h3&gt;
&lt;p&gt;The &quot;False expectations around UB, in general&quot; section now contains a selection of community-suggested items.
Previously it only contained a single item (the last one in the current list) at position number 41.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Thanks to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arriven&quot;&gt;arriven&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;conradludgate&quot;&gt;Conrad Ludgate&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sharnoff&quot;&gt;sharnoff&lt;&#x2F;a&gt;, Brian Graham, and a few folks who preferred to remain unnamed, for feedback on drafts of this post.&lt;&#x2F;em&gt;
&lt;em&gt;Any mistakes are mine alone.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>My HYTRADBOI&#x27;22 Jam</title>
        <published>2022-10-03T00:00:00+00:00</published>
        <updated>2022-10-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/my-hytradboi-22-jam/"/>
        <id>https://predr.ag/blog/my-hytradboi-22-jam/</id>
        
        <summary type="html">&lt;p&gt;I had a lot of fun spending nights-and-weekends time participating in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.hytradboi.com&#x2F;jam&quot;&gt;HYTRADBOI Jam&lt;&#x2F;a&gt;, a global hack week aimed at building &quot;exciting and weird&quot; data-centric solutions to familiar problems.
The name &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.hytradboi.com&#x2F;&quot;&gt;HYTRADBOI&lt;&#x2F;a&gt; might sound familiar: the jam is associated with the same conference where I gave my &lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;talks&#x2F;#how-to-query-almost-everything&quot;&gt;&quot;How to Query (Almost) Everything&quot; talk&lt;&#x2F;a&gt; talk in April this year.&lt;&#x2F;p&gt;
&lt;p&gt;I jammed on two projects: one solo and one with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;Bojan93112526&quot;&gt;a friend&lt;&#x2F;a&gt;. The projects ultimately were very successful and mostly-successful, respectively.</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Debugging Safari: If at first you succeed, don&#x27;t try again</title>
        <published>2022-09-19T00:00:00+00:00</published>
        <updated>2022-09-19T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/debugging-safari-if-at-first-you-succeed/"/>
        <id>https://predr.ag/blog/debugging-safari-if-at-first-you-succeed/</id>
        
        <summary type="html">&lt;p&gt;The saying usually goes: &quot;If at first you don&#x27;t succeed, try, try again.&quot;
But in the Safari web browser under the right conditions, trying again after succeeding once can get you in trouble.
This is my recent debugging adventure.&amp;hellip;
&lt;&#x2F;p&gt;
</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Toward fearless cargo update</title>
        <published>2022-08-25T00:00:00+00:00</published>
        <updated>2022-08-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/toward-fearless-cargo-update/"/>
        <id>https://predr.ag/blog/toward-fearless-cargo-update/</id>
        
        <summary type="html">&lt;p&gt;&lt;em&gt;I recently built &lt;code&gt;cargo-semver-checks&lt;&#x2F;code&gt;, a linter that ensures crates adhere to semantic versioning. This is why and how I built it.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Fearless development is a key theme throughout Rust.
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rustacean-principles.netlify.app&#x2F;how_rust_empowers&#x2F;reliable.html&quot;&gt;&quot;If it compiles, it works&quot;&lt;&#x2F;a&gt;,
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;book&#x2F;ch16-00-concurrency.html&quot;&gt;fearless concurrency&lt;&#x2F;a&gt;, etc.&lt;&#x2F;p&gt;
&lt;p&gt;But there&#x27;s one aspect of Rust (and nearly all other languages) that isn&#x27;t entirely fearless yet: &lt;code&gt;cargo update&lt;&#x2F;code&gt;, upgrading the versions of the project&#x27;s dependencies.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Compiler Adventures, part 3: Value Numbering</title>
        <published>2022-05-17T00:00:00+00:00</published>
        <updated>2022-05-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/compiler-adventures-part3-value-numbering/"/>
        <id>https://predr.ag/blog/compiler-adventures-part3-value-numbering/</id>
        
        <content type="html" xml:base="https://predr.ag/blog/compiler-adventures-part3-value-numbering/">&lt;p&gt;&lt;em&gt;A beginner-friendly introduction to compilers: follow along as we build a compiler from scratch, or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&quot;&gt;fork the code on GitHub&lt;&#x2F;a&gt; and add your own optimizations too! In this episode: value numbering helps track how values are used in the program.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;compiler-adventures-part2-constant-propagation&#x2F;&quot;&gt;Last time on Compiler Adventures&lt;&#x2F;a&gt;, we implemented constant propagation: if &lt;code&gt;x = 2&lt;&#x2F;code&gt; and &lt;code&gt;y = 3&lt;&#x2F;code&gt; then &lt;code&gt;add x y&lt;&#x2F;code&gt; produces &lt;code&gt;5&lt;&#x2F;code&gt;. While it did help detect a few no-ops, we saw the optimization quickly become ineffective as it got overwhelmed by too many &lt;code&gt;Unknown&lt;&#x2F;code&gt; values.&lt;&#x2F;p&gt;
&lt;p&gt;In this episode, we&#x27;ll equip our compiler with the key tool for reasoning about those &lt;code&gt;Unknown&lt;&#x2F;code&gt; values. The starting code for this episode is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;tree&#x2F;part3&quot;&gt;on GitHub&lt;&#x2F;a&gt;, on branch &lt;code&gt;part3&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;figure&gt;
    
    
    
    

    
    
    

    
    
    

    
    
    
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;predr.ag&amp;#x2F;processed_images&amp;#x2F;VLAArrayNiteClouds.e267f208e1bf36e1.jpg&quot; alt=&quot;Dark wispy clouds over a row of massive radio telescope dishes stretching toward the horizon, their white paint standing out against the background in the fading light.&quot;&gt;
    &lt;label for=&quot;mn-lead&quot; class=&quot;margin-toggle&quot;&gt;&amp;#8853;&lt;&#x2F;label&gt;
    &lt;input type=&quot;checkbox&quot; id=&quot;mn-lead&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;
    &lt;span class=&quot;marginnote figcaption&quot;&gt;
        A cloudy twilight over the giant radio telescopes of the Very Large Array, which has been helping humanity peer into the depths of the unknown for more than 40 years. In this episode, we&#x27;ll also do some peering into &lt;code&gt;Unknown&lt;&#x2F;code&gt;.
        Source:
        &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;public.nrao.edu&amp;#x2F;gallery&amp;#x2F;a-cloudy-twilight-at-the-vla&quot;&gt;NRAO&amp;#x2F;AUI&amp;#x2F;NSF&lt;&#x2F;a&gt;, CC BY 3.0
    &lt;&#x2F;span&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Let&#x27;s begin by solving the challenge from &lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;compiler-adventures-part2-constant-propagation&#x2F;&quot;&gt;the last episode&lt;&#x2F;a&gt;. When examining the following program, our compiler claimed that the program ends with an &lt;code&gt;Unknown&lt;&#x2F;code&gt; value in the &lt;code&gt;w&lt;&#x2F;code&gt; register:&lt;label for=&quot;sn-1&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-1&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-1&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;I&#x27;m introducing a small change in notation to avoid confusion: instead of &lt;code&gt;Input(i)&lt;&#x2F;code&gt;, from now on I&#x27;ll write &lt;code&gt;Input_i&lt;&#x2F;code&gt;. This will help us avoid confusing &quot;i-th program input&quot; with &quot;input to the program with value &lt;code&gt;i&lt;&#x2F;code&gt;.&quot; When we know a program value represents the number &lt;code&gt;i&lt;&#x2F;code&gt;, we&#x27;ll continue to use the notation &lt;code&gt;Exact(i)&lt;&#x2F;code&gt;.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;div class=&quot;outer-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;5aa15ad19c0fe98723902fb70e96acf3.js?file=ep2_challenge.log&quot;&gt;&lt;&#x2F;script&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Let&#x27;s simulate running this program:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;When the program starts, all registers are set to zero: &lt;code&gt;w = x = y = z = 0&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;For &lt;code&gt;inp w&lt;&#x2F;code&gt;, say the input number is 5, so now &lt;code&gt;w = 5&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;add x w&lt;&#x2F;code&gt; computes &lt;code&gt;x = x + w&lt;&#x2F;code&gt; with &lt;code&gt;x = 0, w = 5&lt;&#x2F;code&gt;, so we get &lt;code&gt;x = 5&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;eql w x&lt;&#x2F;code&gt; checks &lt;code&gt;w&lt;&#x2F;code&gt; and &lt;code&gt;x&lt;&#x2F;code&gt; for equality. They are both set to 5, so we get &lt;code&gt;w = 1&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Try simulating again with a different number as an input for &lt;code&gt;inp w&lt;&#x2F;code&gt;. Does the final value of &lt;code&gt;w&lt;&#x2F;code&gt; change? &lt;em&gt;No — it&#x27;s always 1!&lt;&#x2F;em&gt; What&#x27;s going on here?&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s zoom in on the second-to-last instruction: &lt;code&gt;add x w&lt;&#x2F;code&gt;. The values that instruction operates on are &lt;code&gt;w = Input_0&lt;&#x2F;code&gt; i.e. &quot;the first program input value,&quot; and &lt;code&gt;x = Exact(0)&lt;&#x2F;code&gt; i.e. the number zero. After performing its operation &lt;code&gt;x = x + w&lt;&#x2F;code&gt;, our code from the previous episode decides the result is &lt;code&gt;x = Unknown&lt;&#x2F;code&gt;. This is technically true! We can&#x27;t know what value &lt;code&gt;x&lt;&#x2F;code&gt; has exactly. But we can do better than &lt;code&gt;Unknown&lt;&#x2F;code&gt;!&lt;&#x2F;p&gt;
&lt;p&gt;We know &lt;code&gt;x&lt;&#x2F;code&gt; was zero before &lt;code&gt;w&lt;&#x2F;code&gt; got added to it: &lt;code&gt;x = 0 + w&lt;&#x2F;code&gt;, so it must be the case that &lt;code&gt;x = w = Input_0&lt;&#x2F;code&gt; after the instruction. Imagine our compiler code was able to determine this as well, and thus computed &lt;code&gt;x = Input_0&lt;&#x2F;code&gt; as the outcome of the addition instead of &lt;code&gt;x = Unknown&lt;&#x2F;code&gt;. Continuing to the next instruction: &lt;code&gt;eql w x&lt;&#x2F;code&gt; then compares &lt;code&gt;w = Input_0&lt;&#x2F;code&gt; and &lt;code&gt;x = Input_0&lt;&#x2F;code&gt;. Those are clearly the same value! So the result of &lt;code&gt;eql w x&lt;&#x2F;code&gt; would be &lt;code&gt;w = Exact(1)&lt;&#x2F;code&gt;, just as our hand-simulation of the program showed!&lt;&#x2F;p&gt;
&lt;div class=&quot;outer-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;f2c1b7c206e17654ac20f706a85364e3.js?file=ep2_challenge_solved.log&quot;&gt;&lt;&#x2F;script&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;The key observation: &lt;code&gt;Input_0&lt;&#x2F;code&gt; represents a &lt;em&gt;specific fixed value&lt;&#x2F;em&gt;. We can&#x27;t know what number it represents until we run the program, but for a given execution of the program, it&#x27;s always going to be the same fixed number: the first number in the input. This is why we can know that &lt;code&gt;w = Input_0&lt;&#x2F;code&gt; and &lt;code&gt;x = Input_0&lt;&#x2F;code&gt; are equal to each other, and therefore &lt;code&gt;eql w x&lt;&#x2F;code&gt; results in an output of &lt;code&gt;w = Exact(1)&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We managed to evaluate the &lt;code&gt;eql&lt;&#x2F;code&gt; instruction over values we don&#x27;t know exactly! Neat, right?&lt;&#x2F;p&gt;
&lt;p&gt;Could we do the same thing with &lt;code&gt;Unknown&lt;&#x2F;code&gt; values as well?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-known-about-unknown&quot;&gt;What is known about Unknown?&lt;&#x2F;h2&gt;
&lt;p&gt;Let’s set up an equivalent example to the last episode’s challenge, but with an &lt;code&gt;eql w x&lt;&#x2F;code&gt; instruction that evaluates two &lt;code&gt;Unknown&lt;&#x2F;code&gt; values instead of two inputs.&lt;&#x2F;p&gt;
&lt;p&gt;Say we have some long program that’s been running for a while, and all the registers currently have &lt;code&gt;Unknown&lt;&#x2F;code&gt; values. The next instructions in the program do the following:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;zero out register &lt;code&gt;x&lt;&#x2F;code&gt; by multiplying its value by 0, then&lt;&#x2F;li&gt;
&lt;li&gt;make &lt;code&gt;x&lt;&#x2F;code&gt; also hold whatever value was in &lt;code&gt;w&lt;&#x2F;code&gt; by running &lt;code&gt;add x w&lt;&#x2F;code&gt;, and finally&lt;&#x2F;li&gt;
&lt;li&gt;compare &lt;code&gt;w&lt;&#x2F;code&gt; and &lt;code&gt;x&lt;&#x2F;code&gt; for equality using &lt;code&gt;eql w x&lt;&#x2F;code&gt; as before.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;div class=&quot;outer-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;3db71d95fd0fc709f5820fd1a9b7f2bd.js?file=ep3_eql_of_unknowns.log&quot;&gt;&lt;&#x2F;script&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Using the same reasoning as earlier in the post, we see that &lt;code&gt;x&lt;&#x2F;code&gt; and &lt;code&gt;w&lt;&#x2F;code&gt; must again have the same value at the point where they get compared, so the result of the equality check instruction should be &lt;code&gt;w = Exact(1)&lt;&#x2F;code&gt;. And yet, looking at the last row of the printout, our compiler claims that comparing &lt;code&gt;w = Unknown&lt;&#x2F;code&gt; to &lt;code&gt;x = Unknown&lt;&#x2F;code&gt; produces an output of &lt;code&gt;Unknown&lt;&#x2F;code&gt;. What’s up with that?&lt;&#x2F;p&gt;
&lt;p&gt;Right now, &lt;code&gt;Unknown&lt;&#x2F;code&gt; in our compiler means “we know absolutely nothing about this value.” It does not refer to a specific-yet-unknown value, so we can’t say for sure if two &lt;code&gt;Unknown&lt;&#x2F;code&gt; values are equal or not equal to each other. This is unlike input values, where &lt;code&gt;Input_0&lt;&#x2F;code&gt; refers specifically to the first program input, &lt;code&gt;Input_1&lt;&#x2F;code&gt; refers to the second program input, etc.&lt;&#x2F;p&gt;
&lt;p&gt;The registers &lt;code&gt;w = Input_0&lt;&#x2F;code&gt; and &lt;code&gt;x = Input_0&lt;&#x2F;code&gt; compare equal to each other because they both refer to the same fixed but still-unknown value. When our compiler evaluated &lt;code&gt;add x w&lt;&#x2F;code&gt; for &lt;code&gt;x = Exact(0)&lt;&#x2F;code&gt; and &lt;code&gt;w = Unknown&lt;&#x2F;code&gt;, it found the result was &lt;code&gt;x = Unknown&lt;&#x2F;code&gt; — but it had no way to represent that this is &lt;em&gt;the same&lt;&#x2F;em&gt; &lt;code&gt;Unknown&lt;&#x2F;code&gt; &lt;em&gt;that is also currently in&lt;&#x2F;em&gt; &lt;code&gt;w&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Just like we currently number program input values as &lt;code&gt;Input_0, Input_1&lt;&#x2F;code&gt; and so on, what if we tried numbering &lt;em&gt;every value&lt;&#x2F;em&gt; we encounter in the program, including &lt;code&gt;Unknown&lt;&#x2F;code&gt; ones? Then, we could express that two registers hold the same &lt;code&gt;Unknown&lt;&#x2F;code&gt; value: e.g. “the fifth value in the program, which is &lt;code&gt;Unknown&lt;&#x2F;code&gt;.” For convenience, we’ll use the notation &lt;code&gt;5: Unknown&lt;&#x2F;code&gt; to describe that “fifth program value, which is &lt;code&gt;Unknown&lt;&#x2F;code&gt;.”&lt;&#x2F;p&gt;
&lt;p&gt;We’ll apply this numbering scheme to all three kinds of values our compiler may encounter in the program: &lt;code&gt;Exact, Input, Unknown&lt;&#x2F;code&gt;. We’ll use a simple counter to ensure each new value gets its own unique number — let’s call this identifier a &lt;code&gt;Vid&lt;&#x2F;code&gt;. Whenever we know that an operation preserves the value of one of its operands (like adding a number to zero), we’ll keep the same value with the same &lt;code&gt;Vid&lt;&#x2F;code&gt; instead of making a new value with a new &lt;code&gt;Vid&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;implementing-our-idea&quot;&gt;Implementing our idea&lt;&#x2F;h2&gt;
&lt;p&gt;To help us skip straight to the interesting parts, I&#x27;ve added &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part3_finished&#x2F;src&#x2F;program.rs#L7-L49&quot;&gt;about 40 lines of boilerplate and helper code behind the scenes&lt;&#x2F;a&gt;. We now have a &lt;code&gt;Program&lt;&#x2F;code&gt; type that keeps track of which &lt;code&gt;Vid&lt;&#x2F;code&gt; numbers we&#x27;ve already used, and has three helper methods on it called &lt;code&gt;new_exact_value(), new_unknown_value(), new_input_value()&lt;&#x2F;code&gt; which ensure we correctly assign unique &lt;code&gt;Vid&lt;&#x2F;code&gt; numbers to newly-created values.&lt;&#x2F;p&gt;
&lt;p&gt;First, let&#x27;s implement the &lt;code&gt;Vid&lt;&#x2F;code&gt; type that represents the unique value IDs, and insert it into our existing &lt;code&gt;Value&lt;&#x2F;code&gt; enum&#x27;s variants.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;compiler-adventures-part2-constant-propagation&#x2F;&quot;&gt;In the previous episode&lt;&#x2F;a&gt;, we defined the &lt;code&gt;Value&lt;&#x2F;code&gt; enum like this:&lt;&#x2F;p&gt;
&lt;div class=&quot;captioned-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;1bd7da4a0d0ee1dc6e821148f6a5464c.js?file=value.rs&quot;&gt;&lt;&#x2F;script&gt;
  &lt;label for=&quot;mn-prevval&quot; class=&quot;margin-toggle&quot;&gt;&amp;#8853;&lt;&#x2F;label&gt;
  &lt;input type=&quot;checkbox&quot; id=&quot;mn-prevval&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;
  &lt;span class=&quot;marginnote&quot;&gt;Excerpt from &lt;code&gt;optimization.rs&lt;&#x2F;code&gt;. See this code in context &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part2_finished&#x2F;src&#x2F;optimization.rs#L101-L107&quot;&gt;in the GitHub repo&lt;&#x2F;a&gt;.&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Let&#x27;s change it to the following:&lt;&#x2F;p&gt;
&lt;div class=&quot;captioned-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;b71b2ea5dea55e8e14ca1e1a0352132d.js?file=value_and_vid.rs&quot;&gt;&lt;&#x2F;script&gt;
  &lt;label for=&quot;mn-nextval&quot; class=&quot;margin-toggle&quot;&gt;&amp;#8853;&lt;&#x2F;label&gt;
  &lt;input type=&quot;checkbox&quot; id=&quot;mn-nextval&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;
  &lt;span class=&quot;marginnote&quot;&gt;Excerpt from &lt;code&gt;values.rs&lt;&#x2F;code&gt;. See this code in context &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part3_finished&#x2F;src&#x2F;values.rs#L5-L49&quot;&gt;in the GitHub repo&lt;&#x2F;a&gt;.&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;When considering two &lt;code&gt;Value&lt;&#x2F;code&gt; entities in a program, when can our compiler know for sure that they must represent the same number? It&#x27;s in one of these cases:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;if both are &lt;code&gt;Value::Exact&lt;&#x2F;code&gt; variants representing the same known constant number, or&lt;&#x2F;li&gt;
&lt;li&gt;if both of them have the same &lt;code&gt;Vid&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Importantly, if two &lt;code&gt;Value&lt;&#x2F;code&gt; entities have different &lt;code&gt;Vid&lt;&#x2F;code&gt; fields, this &lt;em&gt;does not&lt;&#x2F;em&gt; mean they must always hold different numbers. Different &lt;code&gt;Vid&lt;&#x2F;code&gt; fields means that we &lt;em&gt;don&#x27;t know one way or the other&lt;&#x2F;em&gt; whether the values are equivalent. For example, &lt;code&gt;5: Input_0&lt;&#x2F;code&gt; and &lt;code&gt;6: Input_1&lt;&#x2F;code&gt; have different &lt;code&gt;Vid&lt;&#x2F;code&gt; fields: &lt;code&gt;5&lt;&#x2F;code&gt; and &lt;code&gt;6&lt;&#x2F;code&gt;. Depending on the inputs given to any run of the program, they may sometimes but not always happen to represent the same number.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s implement this logic in the &lt;code&gt;==&lt;&#x2F;code&gt; operator for &lt;code&gt;Value&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;div class=&quot;captioned-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;a23f80b0a1f5a9653ddc5dca55077244.js?file=value_eq.rs&quot;&gt;&lt;&#x2F;script&gt;
  &lt;label for=&quot;mn-valeq&quot; class=&quot;margin-toggle&quot;&gt;&amp;#8853;&lt;&#x2F;label&gt;
  &lt;input type=&quot;checkbox&quot; id=&quot;mn-valeq&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;
  &lt;span class=&quot;marginnote&quot;&gt;Excerpt from &lt;code&gt;values.rs&lt;&#x2F;code&gt;. See this code in context &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part3_finished&#x2F;src&#x2F;values.rs#L51-L58&quot;&gt;in the GitHub repo&lt;&#x2F;a&gt;.&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;We can now simplify our no-op detection code to take advantage of our new ability to check values for equality. Instead of looking for a hardcoded set of instructions and values, we can directly compare the register values before and after each instruction. If an instruction did not change the value of its target register and had no other side-effects, that instruction is a no-op by definition.&lt;label for=&quot;sn-2&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-2&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-2&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;We have to consider instruction side-effects here. For example, reading program input via an &lt;code&gt;inp&lt;&#x2F;code&gt; instruction affects state that is outside the &lt;code&gt;w, x, y, z&lt;&#x2F;code&gt; registers: the next number that will be read by the next &lt;code&gt;inp&lt;&#x2F;code&gt; instruction. Eliminating an &lt;code&gt;inp&lt;&#x2F;code&gt; instruction would cause subsequent &lt;code&gt;inp&lt;&#x2F;code&gt; to read different program inputs. Fortunately, our implementation already guarantees that &lt;code&gt;inp&lt;&#x2F;code&gt; instructions can never be considered no-ops: they are assigned a brand-new &lt;code&gt;Vid&lt;&#x2F;code&gt; which could not possibly have been shared with the value previously in the destination register. If a future extension to the MONAD language adds more instructions, we may have to update our no-op detection code. For example, consider a hypothetical &lt;code&gt;print x&lt;&#x2F;code&gt; instruction that outputs the value of the selected register &lt;code&gt;x&lt;&#x2F;code&gt;: it doesn&#x27;t change the value of any register, and yet it isn&#x27;t a no-op because of the side-effect of printing out a number.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;So we can completely remove the &lt;code&gt;is_instruction_no_op()&lt;&#x2F;code&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part2_finished&#x2F;src&#x2F;optimization.rs#L38-L67&quot;&gt;function we previously wrote&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part2_finished&#x2F;src&#x2F;optimization.rs#L88-L92&quot;&gt;replace its use inside&lt;&#x2F;a&gt; &lt;code&gt;constant_propagation()&lt;&#x2F;code&gt; with a simple equality check between the old and new values of the destination register of the current instruction:&lt;&#x2F;p&gt;
&lt;div class=&quot;captioned-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;fbc5e76563b7de409bf1051659ca9883.js?file=no_op_detection.rs&quot;&gt;&lt;&#x2F;script&gt;
  &lt;label for=&quot;mn-noop&quot; class=&quot;margin-toggle&quot;&gt;&amp;#8853;&lt;&#x2F;label&gt;
  &lt;input type=&quot;checkbox&quot; id=&quot;mn-noop&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;
  &lt;span class=&quot;marginnote&quot;&gt;Excerpt from &lt;code&gt;optimization.rs&lt;&#x2F;code&gt;. See this code in context &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part3_finished&#x2F;src&#x2F;optimization.rs#L123-L127&quot;&gt;in the GitHub repo&lt;&#x2F;a&gt;.&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;&lt;code&gt;constant_propagation()&lt;&#x2F;code&gt; will also require a small refactor to generate &lt;code&gt;Vid&lt;&#x2F;code&gt; data for each &lt;code&gt;Value&lt;&#x2F;code&gt;. This is straightforward and uses the convenience functions I earlier mentioned I added &quot;off screen.&quot; Here&#x27;s what our final &lt;code&gt;constant_propagation()&lt;&#x2F;code&gt; implementation looks like:&lt;&#x2F;p&gt;
&lt;div class=&quot;captioned-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;542e945a32eb1e7909060bc282586aeb.js?file=constant_propagation.rs&quot;&gt;&lt;&#x2F;script&gt;
  &lt;label for=&quot;mn-constprop&quot; class=&quot;margin-toggle&quot;&gt;&amp;#8853;&lt;&#x2F;label&gt;
  &lt;input type=&quot;checkbox&quot; id=&quot;mn-constprop&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;
  &lt;span class=&quot;marginnote&quot;&gt;Excerpt from &lt;code&gt;optimization.rs&lt;&#x2F;code&gt;. See this code in context &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part3_finished&#x2F;src&#x2F;optimization.rs#L102-L134&quot;&gt;in the GitHub repo&lt;&#x2F;a&gt;.&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Finally, we&#x27;ll need to update &lt;code&gt;evaluate_instruction()&lt;&#x2F;code&gt; to help it avoid generating new &lt;code&gt;Vid&lt;&#x2F;code&gt; entries whenever it&#x27;s possible to reuse one of the values on which the instruction operates. Recall the situation from the challenge program we&#x27;re solving this week:&lt;&#x2F;p&gt;
&lt;div class=&quot;outer-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;5aa15ad19c0fe98723902fb70e96acf3.js?file=ep2_challenge.log&quot;&gt;&lt;&#x2F;script&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;We want &lt;code&gt;evaluate_instruction()&lt;&#x2F;code&gt; applied to &lt;code&gt;add x w&lt;&#x2F;code&gt; to realize that the result in &lt;code&gt;x&lt;&#x2F;code&gt; is going to be that same &lt;code&gt;Input_0&lt;&#x2F;code&gt; value that is also in &lt;code&gt;w&lt;&#x2F;code&gt;, and not some new value for which nothing is known. Each of the possible instructions has different special cases, so we&#x27;ll split &lt;code&gt;evaluate_instruction()&lt;&#x2F;code&gt; into per-instruction cases:&lt;&#x2F;p&gt;
&lt;div class=&quot;captioned-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;1b857664558ccc59f3a6111622c3c136.js?file=evaluate_instruction.rs&quot;&gt;&lt;&#x2F;script&gt;
  &lt;label for=&quot;mn-evalinstr&quot; class=&quot;margin-toggle&quot;&gt;&amp;#8853;&lt;&#x2F;label&gt;
  &lt;input type=&quot;checkbox&quot; id=&quot;mn-evalinstr&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;
  &lt;span class=&quot;marginnote&quot;&gt;Excerpt from &lt;code&gt;optimization.rs&lt;&#x2F;code&gt;. See this code in context &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part3_finished&#x2F;src&#x2F;optimization.rs#L9-L23&quot;&gt;in the GitHub repo&lt;&#x2F;a&gt;.&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Then, we&#x27;ll implement the per-instruction functions, each with their own special cases. For example, here&#x27;s &lt;code&gt;evaluate_add()&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;div class=&quot;captioned-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;659c03a634921b58730c42408e22fdfc.js?file=evaluate_add.rs&quot;&gt;&lt;&#x2F;script&gt;
  &lt;label for=&quot;mn-evaladd&quot; class=&quot;margin-toggle&quot;&gt;&amp;#8853;&lt;&#x2F;label&gt;
  &lt;input type=&quot;checkbox&quot; id=&quot;mn-evaladd&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;
  &lt;span class=&quot;marginnote&quot;&gt;Excerpt from &lt;code&gt;optimization.rs&lt;&#x2F;code&gt;. See this code in context &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part3_finished&#x2F;src&#x2F;optimization.rs#L25-L35&quot;&gt;in the GitHub repo&lt;&#x2F;a&gt;.&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Here&#x27;s &lt;code&gt;evaluate_mul()&lt;&#x2F;code&gt;, which has a few more cases to consider:&lt;&#x2F;p&gt;
&lt;div class=&quot;captioned-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;dd37d7774e85e9b812f61c044df44bd2.js?file=evaluate_mul.rs&quot;&gt;&lt;&#x2F;script&gt;
  &lt;label for=&quot;mn-evalmul&quot; class=&quot;margin-toggle&quot;&gt;&amp;#8853;&lt;&#x2F;label&gt;
  &lt;input type=&quot;checkbox&quot; id=&quot;mn-evalmul&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;
  &lt;span class=&quot;marginnote&quot;&gt;Excerpt from &lt;code&gt;optimization.rs&lt;&#x2F;code&gt;. See this code in context &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part3_finished&#x2F;src&#x2F;optimization.rs#L37-L52&quot;&gt;in the GitHub repo&lt;&#x2F;a&gt;.&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Most of the other instructions follow the same pattern, so I won&#x27;t include all of them in this post — you can find them all &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part3_finished&#x2F;src&#x2F;optimization.rs#L5-L93&quot;&gt;in the GitHub repo&lt;&#x2F;a&gt;. The exception is &lt;code&gt;evaluate_equal()&lt;&#x2F;code&gt; which has just a touch more nuance:&lt;&#x2F;p&gt;
&lt;div class=&quot;captioned-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;791a1164c61fcf11320e4a78d8afde4d.js?file=evaluate_equal.rs&quot;&gt;&lt;&#x2F;script&gt;
  &lt;label for=&quot;mn-evaleql&quot; class=&quot;margin-toggle&quot;&gt;&amp;#8853;&lt;&#x2F;label&gt;
  &lt;input type=&quot;checkbox&quot; id=&quot;mn-evaleql&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;
  &lt;span class=&quot;marginnote&quot;&gt;Excerpt from &lt;code&gt;optimization.rs&lt;&#x2F;code&gt;. See this code in context &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part3_finished&#x2F;src&#x2F;optimization.rs#L84-L100&quot;&gt;in the GitHub repo&lt;&#x2F;a&gt;.&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;And like that, we&#x27;ve implemented &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Value_numbering&quot;&gt;value numbering&lt;&#x2F;a&gt;.&lt;label for=&quot;sn-3&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-3&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-3&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;Specifically, we&#x27;ve implemented &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Value_numbering#Global_value_numbering&quot;&gt;&lt;em&gt;global&lt;&#x2F;em&gt; value numbering&lt;&#x2F;a&gt;. The difference between global and local value numbering is whether the numbered values apply to the entire program, or are valid only within a given function, if-statement, loop, or similar program component (called a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Basic_block&quot;&gt;&quot;basic block&quot;&lt;&#x2F;a&gt;). The MONAD language does not have any functions, if-statements, or loops — making the entire program a single basic block — so our value numbering is global because it applies to the entire program. This simplicity is part of why this series chooses MONAD as the language to compile and optimize: its simplicity makes it easier to explain compiler ideas one at a time without requiring a lot of prerequisite knowledge. Case in point: basic blocks are a fundamental idea in compilers and a prerequisite for building a compiler for any language like Python or Rust or C&#x2F;C++, yet three episodes into the blog series, the first mention of the term &quot;basic block&quot; is in this entirely-optional footnote.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;

As expected, our compiler now figures out that our challenge program ends with the number &lt;code&gt;1&lt;&#x2F;code&gt; in the &lt;code&gt;x&lt;&#x2F;code&gt; register instead of an &lt;code&gt;Unknown&lt;&#x2F;code&gt; value! 🎉&lt;&#x2F;p&gt;
&lt;div class=&quot;outer-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;323e8c8defaf9bd5bac423b1822309e4.js?file=value_numbering_outcome.log&quot;&gt;&lt;&#x2F;script&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;We&#x27;re again interested in the &quot;real-world&quot; impact of our optimization, as measured on the standard Advent of Code MONAD input program.&lt;&#x2F;p&gt;
&lt;p&gt;First, what kind of impact do we &lt;em&gt;expect&lt;&#x2F;em&gt; to find?&lt;&#x2F;p&gt;
&lt;p&gt;Without value numbering, in &lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;compiler-adventures-part2-constant-propagation&#x2F;&quot;&gt;our previous Compiler Adventure&lt;&#x2F;a&gt; we saw our compiler was completely unable to reason about any value that is not &lt;code&gt;Exact&lt;&#x2F;code&gt;. Operations on &lt;code&gt;Input&lt;&#x2F;code&gt; values would produce &lt;code&gt;Unknown&lt;&#x2F;code&gt; values as we saw in the challenge program, and &lt;code&gt;Unknown&lt;&#x2F;code&gt; represented a total absence of any information at all.&lt;&#x2F;p&gt;
&lt;p&gt;In fact, the lack of value numbering meant our compiler was incapable of even simple reasoning like &quot;registers don&#x27;t change their value unless they are written to.&quot; For example, say the value in register &lt;code&gt;w&lt;&#x2F;code&gt; was &lt;code&gt;Unknown&lt;&#x2F;code&gt; and the compiler was considering an instruction that did not use &lt;code&gt;w&lt;&#x2F;code&gt;, such as &lt;code&gt;mul x y&lt;&#x2F;code&gt;. The value of &lt;code&gt;w&lt;&#x2F;code&gt; after that instruction would remain &lt;code&gt;Unknown&lt;&#x2F;code&gt; — but since &lt;code&gt;Unknown&lt;&#x2F;code&gt; values had no way to be compared for equality, even the &quot;before&quot; and &quot;after&quot; &lt;code&gt;Unknown&lt;&#x2F;code&gt; values of &lt;code&gt;w&lt;&#x2F;code&gt; could not be considered equal.&lt;&#x2F;p&gt;
&lt;p&gt;This severely limited our compiler&#x27;s ability to optimize MONAD code in the previous episode. In this episode, we&#x27;ve helped our compiler gain more insight about how &lt;code&gt;Unknown&lt;&#x2F;code&gt; values are used, but there aren&#x27;t many cases where value numbering &lt;em&gt;by itself&lt;&#x2F;em&gt; allows the compiler to eliminate instructions and optimize a program. We haven&#x27;t yet implemented any of the powerful optimizations that value numbering makes possible, but we&#x27;ve laid all the groundwork we&#x27;ll rely on in the next episodes! So we don&#x27;t expect to see much if any speedup just yet, and we should find a better way to measure our work.&lt;&#x2F;p&gt;
&lt;p&gt;A good metric for our work would capture how much difference adding the compiler equivalent of object permanence makes. In other words, when our compiler sees a non-&lt;code&gt;Exact&lt;&#x2F;code&gt; value, how often does it know that value to be identical to another value used elsewhere in the program?&lt;&#x2F;p&gt;
&lt;p&gt;Without value numbering, the answer would be &quot;never&quot; i.e. 0% of the time. Let&#x27;s update our compiler to measure these new stats:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ cargo run registers sample_programs&#x2F;aoc_challenge.txt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt; ... snip ... &amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Total non-input instructions: 238&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;- with 1+ non-exact value:    218 (91.6%)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;- without any exact values:    53 (22.3%)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Total non-exact values uses: 462&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;- number of unique values: 191 (41.3%)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;- non-unique uses: 271 (58.7%)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In the Advent of Code MONAD program, instructions reference or produce an &lt;code&gt;Unknown&lt;&#x2F;code&gt; or &lt;code&gt;Input&lt;&#x2F;code&gt; value 462 times. Value numbering determined that there are no more than 191 (41.3%) unique &lt;code&gt;Unknown&lt;&#x2F;code&gt; or &lt;code&gt;Input&lt;&#x2F;code&gt; values in the program; the remaining 271 uses (58.7%) are guaranteed to be reuses of a value already in existence elsewhere. Each of these repeated uses is an opportunity for future optimizations! For now, that&#x27;s a job well done!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wrap-up-challenge-question&quot;&gt;Wrap up + challenge question&lt;&#x2F;h2&gt;
&lt;p&gt;The completed code from this post is on branch &lt;code&gt;part3_finished&lt;&#x2F;code&gt; on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;tree&#x2F;part3_finished&quot;&gt;GitHub&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In the meantime, consider the &lt;code&gt;eql_2_on_eql_result&lt;&#x2F;code&gt; program below, which is also &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part3_finished&#x2F;sample_programs&#x2F;eql_2_on_eql_result.txt&quot;&gt;available on GitHub&lt;&#x2F;a&gt; in the &lt;code&gt;sample_programs&lt;&#x2F;code&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;tree&#x2F;part3_finished&#x2F;sample_programs&quot;&gt;directory&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;inp w&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;inp x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;eql w x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;eql w 2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;What is the value of &lt;code&gt;w&lt;&#x2F;code&gt; at the end of the program? Our compiler says it&#x27;s &lt;code&gt;9: Unknown&lt;&#x2F;code&gt; — can you do better?&lt;&#x2F;p&gt;
&lt;div class=&quot;outer-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;04dc411433533b97af1d316f8a2955ee.js?file=eql_2_on_eql_result.log&quot;&gt;&lt;&#x2F;script&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;All this and more in the next Compiler Adventure — see you next time!&lt;&#x2F;p&gt;
&lt;p&gt;If you liked this post, please share it with friends — compilers are for everyone!
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;PredragGruevski&quot;&gt;Reach out to me on Twitter&lt;&#x2F;a&gt; or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=31412996&quot;&gt;join the discussion on HackerNews&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Thanks to Paul Hemberger, James Logan, Russell Cohen, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;vtjeng.com&#x2F;&quot;&gt;Vincent Tjeng&lt;&#x2F;a&gt; for their feedback on drafts of this post. Any mistakes are mine alone.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>To ace exams, get better at the easy questions</title>
        <published>2022-04-01T00:00:00+00:00</published>
        <updated>2022-04-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/to-ace-exams-get-better-at-the-easy-questions/"/>
        <id>https://predr.ag/blog/to-ace-exams-get-better-at-the-easy-questions/</id>
        
        <content type="html" xml:base="https://predr.ag/blog/to-ace-exams-get-better-at-the-easy-questions/">&lt;p&gt;&lt;em&gt;Happy April 1st! A group of folks and I decided to do something different this year: instead of publishing fake things, we’re publishing real posts on very different topics than our readers usually expect from our blogs. The tech content will be back soon! &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aprilcools.club&#x2F;&quot;&gt;Check out the other April Cools posts here.&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;An extraordinary amount of ink has already been dedicated to describing various test-taking strategies. Many strategies, especially for standardized multiple-choice exams, are well-known: don’t get stuck and spend too much time on one question; guessing is worth it if you can eliminate some of the multiple-choice options, etc. Most such advice is sound and will boost your scores — but none of it will help you absolutely &lt;em&gt;ace&lt;&#x2F;em&gt; an exam.&lt;&#x2F;p&gt;
&lt;figure&gt;
    
    
    
    

    
    
    

    
    
    

    
    
    
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;predr.ag&amp;#x2F;processed_images&amp;#x2F;Gold_medal_of_the_2018_Winter_Olympics_in_in_Pyeongchang.d8a11e2e7e14d293.jpg&quot; alt=&quot;Close-up of a gold medal from the 2018 PyeongChang Olympics, showing a ridge-textured golden surface upon which the Olympic rings are set.&quot;&gt;
    &lt;label for=&quot;mn-lead&quot; class=&quot;margin-toggle&quot;&gt;&amp;#8853;&lt;&#x2F;label&gt;
    &lt;input type=&quot;checkbox&quot; id=&quot;mn-lead&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;
    &lt;span class=&quot;marginnote figcaption&quot;&gt;
        A gold medal from the 2018 PyeongChang Olympics. Just like common advice on staying in shape like &quot;try to exercise at least twice a week&quot; won&#x27;t get an athlete to an Olympic gold medal, common test-taking advice is similarly insufficient for getting a perfect score on an exam. The advice is directionally correct but not useful: Olympic athletes definitely exercise at least twice a week, but that&#x27;s not the key to winning gold.
        Source:
        &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;en.wikipedia.org&amp;#x2F;wiki&amp;#x2F;2018_Winter_Olympics#&amp;#x2F;media&amp;#x2F;File:Gold_medal_of_the_2018_Winter_Olympics_in_in_Pyeongchang.jpg&quot;&gt;Korea.net &amp;#x2F; Korean Culture and Information Service&lt;&#x2F;a&gt;, CC BY-SA 2.0
    &lt;&#x2F;span&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;This is because &lt;em&gt;common advice&lt;&#x2F;em&gt; describes what to do in &lt;em&gt;common situations&lt;&#x2F;em&gt;. A perfect score is hardly a common situation, so common advice doesn’t tend to help. For example, to get a near-perfect score, you need to know how to solve nearly every question on the exam — and that means there’s not a lot of room for guessing. If forced to guess, you should still apply the usual “good guessing” advice, but good guessing alone won’t get you a perfect score.&lt;&#x2F;p&gt;
&lt;p&gt;If your goal is to get a near-perfect score but you haven’t gotten there yet, odds are you need more practice on the questions you find &lt;em&gt;easiest&lt;&#x2F;em&gt; in the exam. This sounds impossible, unrealistic, and unwise, but it’s true. To explain why that’s true in a way you’ll believe, I need to tell you a bit more about myself first.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;math-kangaroo&quot;&gt;Math Kangaroo&lt;&#x2F;h2&gt;
&lt;p&gt;Growing up, I took part in competitions in many subjects, but math and programming were my favorites. Between 3rd grade and senior year of high school, I literally competed in every math competition my parents and I managed to find.&lt;&#x2F;p&gt;
&lt;p&gt;One of those math competitions was &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mathkangaroo.org&#x2F;&quot;&gt;Math Kangaroo&lt;&#x2F;a&gt;, an intense multiple-choice exam where students have 75min to solve 30 problems&lt;label for=&quot;sn-1&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-1&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-1&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;The exam for grades 1 through 4 actually only has 24 problems, with 8 problems in each difficulty tier instead of 10 per tier. The allotted time is the same, so there’s still a significant amount of time pressure, if slightly less than at higher levels.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;

in three difficulty tiers of 10 problems each: easy (worth 3 points each), medium (4 points), and hard (5 points). Contestants may choose between 5 possible answers for each question, and incorrect answers deduct a quarter of the point value of the problem — so a correct answer in the hard tier adds 5 points to your score, but an incorrect answer subtracts 1.25 points instead.&lt;&#x2F;p&gt;
&lt;p&gt;Every year I was eligible to compete in Math Kangaroo — a 10 year stretch&lt;label for=&quot;sn-2&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-2&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-2&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;At the time, Math Kangaroo was only available to students between 3rd grade and senior year of high school. Today, there’s also a Math Kangaroo exam for 1st and 2nd graders, but that was first introduced several years after I had finished 2nd grade.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
 — I won the competition by posting the national top score for my grade level. Including the “base” 30 points everyone starts with (to avoid negative scores due to too many incorrect answers) a perfect score at Math Kangaroo is 150 points.&lt;label for=&quot;sn-3&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-3&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-3&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;For the 24-problem Math Kangaroo exams for grades 1 through 4, the perfect score is 120 instead, but I only did two years of that (3rd and 4th grade).&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;My usual score was around 140, meaning that I had correctly solved all but one or two problems.&lt;&#x2F;p&gt;
&lt;p&gt;People used to accuse me of cheating. They said scores that high were impossible. They said someone must have given me the answer key. They said the proctors must have given me extra time. They said I must have had help solving the problems: from proctors, from my math teachers, from other students, from the government(?!), from the competition organizers and sponsors.&lt;&#x2F;p&gt;
&lt;p&gt;I never cheated. I solved the same problems in the same 75min as everyone else, and yet I solved them differently than everyone else.&lt;&#x2F;p&gt;
&lt;p&gt;Everyone else spent by far the most time practicing on the hardest problems. I drilled the hardest on the easiest problems.&lt;&#x2F;p&gt;
&lt;p&gt;Everyone else tried to get faster at solving the hardest problems, and found that really hard. I found it hard too!&lt;&#x2F;p&gt;
&lt;p&gt;I couldn’t really get significantly faster at solving the hardest problems. But I could maximize the time I had available to spend on those problems, by making sure I could solve everything else as fast as possible with no mistakes.&lt;&#x2F;p&gt;
&lt;p&gt;My objective was “20 problems in 15 minutes or less” — solve all the easy (3-point) and medium (4-point) problems completely correctly in less than 15 minutes, leaving a full hour for the ten hard (5-point) problems. I drilled this repeatedly until I could solve all ten easy problems correctly in 3–4 minutes (~20s per problem), and all ten medium problems in 10 minutes (~1min per problem), without even a single mistake. That would leave me an average of 6 minutes per hard problem, and in practice I’d usually be able to dedicate about 10 minutes apiece to the one or two problems I found the most difficult.&lt;&#x2F;p&gt;
&lt;p&gt;I have no doubt that most if not all contestants with above-average scores could solve just about any question on the exam if given 10 minutes to spend on only that problem. But based on my conversations with them, it sounded like they almost never had the luxury of that much leftover time. Instead, most were just crossing over into the medium (4-point) problem section when I was already working on the 5-pointers.&lt;&#x2F;p&gt;
&lt;p&gt;To make matters worse, the data also showed that most of the above-average performers still made many mistakes in the easy and medium problem sections, which cost them too many precious points. This is critical: you can’t just solve the easier problems &lt;em&gt;quickly&lt;&#x2F;em&gt;, you also have to do it &lt;em&gt;correctly&lt;&#x2F;em&gt;. That’s why you need to practice so hard on the easy problems — you are working your correctness percentage up to 100% &lt;em&gt;while&lt;&#x2F;em&gt; driving your time taken as close to zero as you can. Not one or the other, but both at once!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;test-of-speed-or-test-of-knowledge&quot;&gt;Test of speed or test of knowledge?&lt;&#x2F;h2&gt;
&lt;p&gt;Almost everyone treated Math Kangaroo as a &lt;em&gt;test of knowledge&lt;&#x2F;em&gt;, when in fact beyond a certain level it became mostly a &lt;em&gt;test of speed&lt;&#x2F;em&gt;. The “master the easy problems” strategy is perfect for tests of speed, and completely useless for tests of knowledge. Before you get a perfect score on an exam, you must learn to tell the difference and adjust your approach accordingly.&lt;&#x2F;p&gt;
&lt;p&gt;How can you tell the difference? First, practice for the exam until you can consistently post above-average scores — this is mostly to ensure you have a sound understanding of the subject matter, which is initially the most effective way to boost your scores. Then, ask yourself if doubling the allotted time for the exam would change how much of it you could solve. If the answer is “yes” then you have a test of speed on your hands.&lt;&#x2F;p&gt;
&lt;p&gt;Based on that, the SAT and other similar exams are all tests of speed when aiming for above-average scores.&lt;label for=&quot;sn-4&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-4&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-4&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;Why are so many standardized tests designed to test for speed rather than knowledge on the high end of the score distribution? I find this an interesting question, and while I have some guesses, I am definitely not a professional test-writer. If you know the answer, please &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;PredragGruevski&quot;&gt;tweet it to me&lt;&#x2F;a&gt;.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
 Back when I took the SAT, it was still scored out of 2400 points and consisted of three sections: math, writing, and critical reading. I speak English as a second language, so I knew that the critical reading section would be my weak spot. I worked hard to improve my critical reading! But I also knew that math was my strongest side, so I also worked hard to make sure I could flawlessly finish every math section with 10–15min to spare — time I could use to rest and catch my breath during the grueling 4-hour SAT exam. Critical reading sections are a lot easier after an extra-long break!&lt;&#x2F;p&gt;
&lt;p&gt;Conversely, this strategy fails completely if time isn’t the limiting factor on your test performance. The best knowledge-based exams are specifically designed like that: MIT’s “open-book, open-note, open-laptop” take-home exams are notorious for this. Nothing quite convinces you that you don’t understand a concept as well as &lt;em&gt;four days of failing to solve a problem while re-reading the textbook and course notes&lt;&#x2F;em&gt;. The four days may as well have been a week and it wouldn’t have mattered one bit.&lt;&#x2F;p&gt;
&lt;p&gt;Real-world tasks can similarly be knowledge-limited or time-limited. Have you ever written a complex and satisfying piece of code only to waste a lot of time having it repeatedly rejected by a linter? Have you ever tried to schedule an important meeting and found yourself spending hours finding a time that works for everyone? Even in the real world, the easiest way to get better at tackling big challenges is to optimize and perfect the easy tasks first.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Thanks to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hillelwayne.com&#x2F;&quot;&gt;Hillel Wayne&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;jeremyjkun&quot;&gt;Jeremy Kun&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lars.hupel.info&#x2F;&quot;&gt;Lars Hupel&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;benbrubaker.com&#x2F;&quot;&gt;Ben Brubaker&lt;&#x2F;a&gt; for their support and feedback on early drafts of this post. All mistakes are mine alone.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Compiler Adventures, part 2: Constant Propagation</title>
        <published>2022-02-17T00:00:00+00:00</published>
        <updated>2022-02-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/compiler-adventures-part2-constant-propagation/"/>
        <id>https://predr.ag/blog/compiler-adventures-part2-constant-propagation/</id>
        
        <content type="html" xml:base="https://predr.ag/blog/compiler-adventures-part2-constant-propagation/">&lt;p&gt;&lt;em&gt;A beginner-friendly introduction to compilers: follow along as we build a compiler from scratch, or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&quot;&gt;fork the code on GitHub&lt;&#x2F;a&gt; and add your own optimizations too! In this episode: propagating constant values to eliminate more instructions.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;compiler-adventures-part1-no-op-instructions&#x2F;&quot;&gt;Last time on Compiler Adventures&lt;&#x2F;a&gt;, we wrote an optimization that eliminates no-op instructions like division by one: &lt;code&gt;div x 1&lt;&#x2F;code&gt;. At the end of the post, I challenged you to find other instructions that are always no-ops regardless of the register&#x27;s value. There are at least two more: &quot;add zero&quot; (e.g. &lt;code&gt;add x 0&lt;&#x2F;code&gt;) and &quot;multiply by one&quot; (e.g. &lt;code&gt;mul x 1&lt;&#x2F;code&gt;) — neither of those examples has any effect on register &lt;code&gt;x&lt;&#x2F;code&gt; regardless of its value.&lt;&#x2F;p&gt;
&lt;figure&gt;
    
    
    
    

    
    
    

    
    
    

    
    
    
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;predr.ag&amp;#x2F;processed_images&amp;#x2F;Improvement_in_Hubble_images_after_SMM1.48527a785b646a26.jpg&quot; alt=&quot;A blurry photo of a galaxy, with a bright blob in the middle surrounded by fuzzy swirling clouds of gas. Next to it, a crisp and sharp image of the same galaxy, showing many dots of light and and individual clouds where previously there was just a blur of color.&quot;&gt;
    &lt;label for=&quot;mn-lead&quot; class=&quot;margin-toggle&quot;&gt;&amp;#8853;&lt;&#x2F;label&gt;
    &lt;input type=&quot;checkbox&quot; id=&quot;mn-lead&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;
    &lt;span class=&quot;marginnote figcaption&quot;&gt;
        Dramatic improvement in the Hubble Space Telescope&#x27;s ability to see distant galaxies after corrective optics are installed.
        Source:
        &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;commons.wikimedia.org&amp;#x2F;wiki&amp;#x2F;File:Improvement_in_Hubble_images_after_SMM1.jpg&quot;&gt;NASA and ESA&lt;&#x2F;a&gt;, public domain
    &lt;&#x2F;span&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;In this episode, we&#x27;ll look at how we can use constants to find more no-op instructions that we can eliminate from our program. The starting code for this episode is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;tree&#x2F;part2&quot;&gt;on GitHub&lt;&#x2F;a&gt;, on branch &lt;code&gt;part2&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Recall that when a MONAD program starts, the values of &lt;code&gt;w, x, y, z&lt;&#x2F;code&gt; are all zero. Let&#x27;s look at the start of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part2&#x2F;sample_programs&#x2F;aoc_challenge.txt&quot;&gt;our MONAD input program&lt;&#x2F;a&gt; and simulate by hand what happens to the four registers:&lt;&#x2F;p&gt;
&lt;div class=&quot;outer-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;006faa594edfc3ec9985e47c0403ae46.js?file=simulated_registers.log&quot;&gt;&lt;&#x2F;script&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Wow! Other than reading an input number with &lt;code&gt;inp w&lt;&#x2F;code&gt;, pretty much nothing else happens until that &lt;code&gt;add x 11&lt;&#x2F;code&gt; instruction. The &lt;code&gt;add x z&lt;&#x2F;code&gt; and &lt;code&gt;mod x 26&lt;&#x2F;code&gt; instructions have no effect on the registers&#x27; values, so we&#x27;ve found more instructions we could optimize out!&lt;&#x2F;p&gt;
&lt;p&gt;In &lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;compiler-adventures-part1-no-op-instructions&#x2F;&quot;&gt;episode 1&lt;&#x2F;a&gt;, our compiler recognized that &lt;code&gt;div z 1&lt;&#x2F;code&gt; is a no-op because it divides by one. Well, why didn&#x27;t our compiler also notice that the adjacent instructions are no-ops?&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s because they aren&#x27;t &lt;em&gt;always&lt;&#x2F;em&gt; no-ops: &lt;code&gt;div z 1&lt;&#x2F;code&gt; is a no-op &lt;em&gt;regardless of the value of &lt;code&gt;z&lt;&#x2F;code&gt;&lt;&#x2F;em&gt;, but &lt;code&gt;add x z&lt;&#x2F;code&gt; is only a no-op if &lt;code&gt;z&lt;&#x2F;code&gt; is zero. Similarly, the &lt;code&gt;mul x 0&lt;&#x2F;code&gt; and &lt;code&gt;mod x 26&lt;&#x2F;code&gt; instructions here are no-ops only because &lt;code&gt;x = 0&lt;&#x2F;code&gt; before the instruction executes: the operation&#x27;s result is zero and is written to &lt;code&gt;x&lt;&#x2F;code&gt;, therefore leaving &lt;code&gt;x = 0&lt;&#x2F;code&gt; unchanged.&lt;label for=&quot;sn-1&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-1&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-1&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;The no-ops we&#x27;ve looked at so far are all of the &quot;math operation that had the same result as its input register already held&quot; flavor. There are also other ways in which an instruction might have no effect on the rest of the program. For example, an instruction might calculate a value that is never used, and therefore didn&#x27;t need to be computed in the first place. We&#x27;ll explore this idea in detail in an upcoming episode.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;

In order to optimize out instructions like these, we&#x27;ll need to teach the compiler to track the possible values registers can have.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tracking-register-values&quot;&gt;Tracking register values&lt;&#x2F;h2&gt;
&lt;p&gt;A register&#x27;s value can either be known exactly, or not known exactly. If a register is known to hold number &lt;code&gt;n&lt;&#x2F;code&gt;, let&#x27;s call that &lt;code&gt;Exact(n)&lt;&#x2F;code&gt;. If the register&#x27;s value is not known to be any specific number, let&#x27;s say the register holds &lt;code&gt;Unknown&lt;&#x2F;code&gt;. For our reading and debugging convenience, let&#x27;s also introduce a third kind of value a register can hold: &lt;code&gt;Input(i)&lt;&#x2F;code&gt;, representing the &lt;code&gt;i&lt;&#x2F;code&gt;th input to the program. (For folks not yet comfortable with Rust, quick reminder that &lt;code&gt;usize&lt;&#x2F;code&gt; is the unsigned integer type Rust prefers for counting things.)&lt;&#x2F;p&gt;
&lt;div class=&quot;captioned-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;1bd7da4a0d0ee1dc6e821148f6a5464c.js?file=value.rs&quot;&gt;&lt;&#x2F;script&gt;
  &lt;label for=&quot;mn-gist1&quot; class=&quot;margin-toggle&quot;&gt;&amp;#8853;&lt;&#x2F;label&gt;
  &lt;input type=&quot;checkbox&quot; id=&quot;mn-gist1&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;
  &lt;span class=&quot;marginnote&quot;&gt;Excerpt from &lt;code&gt;optimization.rs&lt;&#x2F;code&gt;. See this code in context &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part2_finished&#x2F;src&#x2F;optimization.rs#L101-L107&quot;&gt;in the GitHub repo&lt;&#x2F;a&gt;.&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;When the program starts, all four registers hold &lt;code&gt;Exact(0)&lt;&#x2F;code&gt;. Then, for each instruction, we evaluate a set of rules to determine what the next register state should be. For example, after reading the first program input with the &lt;code&gt;inp w&lt;&#x2F;code&gt; instruction, the &lt;code&gt;w&lt;&#x2F;code&gt; register&#x27;s value becomes &lt;code&gt;Input(0)&lt;&#x2F;code&gt;. Otherwise:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;If the instruction operates on two &lt;code&gt;Exact&lt;&#x2F;code&gt; values, we&#x27;ll evaluate the operation on those values and record an &lt;code&gt;Exact&lt;&#x2F;code&gt; result. For example, &lt;code&gt;add x 11&lt;&#x2F;code&gt; when &lt;code&gt;x = Exact(0)&lt;&#x2F;code&gt; would produce &lt;code&gt;Exact(11)&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;If the instruction is a multiplication and one of the operands is &lt;code&gt;Exact(0)&lt;&#x2F;code&gt;, the result is &lt;code&gt;Exact(0)&lt;&#x2F;code&gt; regardless of the other operand value.&lt;&#x2F;li&gt;
&lt;li&gt;Otherwise, we aren&#x27;t able to determine an exact result so we&#x27;ll record an &lt;code&gt;Unknown&lt;&#x2F;code&gt; instead. For example, &lt;code&gt;eql x w&lt;&#x2F;code&gt; below is comparing between &lt;code&gt;Input(0)&lt;&#x2F;code&gt; and &lt;code&gt;Exact(11)&lt;&#x2F;code&gt;, and produces an &lt;code&gt;Unknown&lt;&#x2F;code&gt; result. Similarly, the next instruction &lt;code&gt;eql x 0&lt;&#x2F;code&gt; compares &lt;code&gt;Unknown&lt;&#x2F;code&gt; to &lt;code&gt;Exact(0)&lt;&#x2F;code&gt;, and again produces an &lt;code&gt;Unknown&lt;&#x2F;code&gt; result.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Applying these rules, we get the following register values starting from the beginning of the program:&lt;&#x2F;p&gt;
&lt;div class=&quot;outer-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;c84797b4d8b8cae9b796cf9eaa0445cd.js?file=register_values.log&quot;&gt;&lt;&#x2F;script&gt;
&lt;&#x2F;div&gt;
&lt;h3 id=&quot;aside-rust-syntax-primer&quot;&gt;Aside: Rust syntax primer&lt;&#x2F;h3&gt;
&lt;p&gt;We’re about to use some syntax that may feel unfamiliar to folks not yet used to Rust, so let’s explain it first:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;match&lt;&#x2F;code&gt; is used for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;book&#x2F;ch06-02-match.html&quot;&gt;pattern-matching in Rust&lt;&#x2F;a&gt;, in the same way that it is used &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.python.org&#x2F;dev&#x2F;peps&#x2F;pep-0636&#x2F;#abstract&quot;&gt;in Python 3.10+&lt;&#x2F;a&gt; or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.scala-lang.org&#x2F;tour&#x2F;pattern-matching.html&quot;&gt;in functional languages like Scala&lt;&#x2F;a&gt;. It&#x27;s a handy concept, and I encourage you to learn about it if you haven&#x27;t encountered it before!&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;if let x = y { &amp;lt;code here&amp;gt; }&lt;&#x2F;code&gt; is shorthand for: &lt;code&gt;match y&lt;&#x2F;code&gt;, and if the pattern &lt;code&gt;x&lt;&#x2F;code&gt; matches, execute the given code block. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;book&#x2F;ch06-03-if-let.html&quot;&gt;The Rust book&#x27;s chapter on it&lt;&#x2F;a&gt; does a great job of explaining its benefits.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;implementing-the-register-value-rules&quot;&gt;Implementing the register value rules&lt;&#x2F;h3&gt;
&lt;p&gt;Since &lt;code&gt;inp&lt;&#x2F;code&gt; instructions only take one input instead of two, let&#x27;s make the code handle them separately. For all other instructions, we can handle them with a function that takes an instruction and two &lt;code&gt;Value&lt;&#x2F;code&gt; inputs, and returns the resulting &lt;code&gt;Value&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;div class=&quot;captioned-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;6d0ac4ae4a0fb4188a56b47d696c9f64.js?file=evaluate_instruction.rs&quot;&gt;&lt;&#x2F;script&gt;
  &lt;label for=&quot;mn-gist2&quot; class=&quot;margin-toggle&quot;&gt;&amp;#8853;&lt;&#x2F;label&gt;
  &lt;input type=&quot;checkbox&quot; id=&quot;mn-gist2&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;
  &lt;span class=&quot;marginnote&quot;&gt;Excerpt from &lt;code&gt;optimization.rs&lt;&#x2F;code&gt;. See this code in context &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part2_finished&#x2F;src&#x2F;optimization.rs#L5-L36&quot;&gt;in the GitHub repo&lt;&#x2F;a&gt;.&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Observant readers might have noticed this code returns &lt;code&gt;Unknown&lt;&#x2F;code&gt; in
some situations where it could have done better.
&lt;em&gt;Cue the foreshadowing music.&lt;&#x2F;em&gt;
We&#x27;ll improve it further in future episodes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;finding-more-no-op-instructions&quot;&gt;Finding more no-op instructions&lt;&#x2F;h2&gt;
&lt;p&gt;Earlier, we observed that to detect whether &lt;code&gt;add x z&lt;&#x2F;code&gt; is a no-op, we needed to know if &lt;code&gt;x&lt;&#x2F;code&gt; or &lt;code&gt;z&lt;&#x2F;code&gt; are zero — and now we can! In general, we see that:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;add x y&lt;&#x2F;code&gt; is a no-op if &lt;code&gt;y = Exact(0)&lt;&#x2F;code&gt;. However, it is not a no-op if &lt;code&gt;x = Exact(0)&lt;&#x2F;code&gt; since in that case &lt;code&gt;x&lt;&#x2F;code&gt; gets overwritten with &lt;code&gt;y&lt;&#x2F;code&gt;&#x27;s value.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;mul x y&lt;&#x2F;code&gt; is a no-op if &lt;code&gt;x = Exact(0)&lt;&#x2F;code&gt; (multiply zero by anything and it stays zero) or &lt;code&gt;y = Exact(1)&lt;&#x2F;code&gt; (multiply anything by one and it doesn’t change). The same no-op rule applies to &lt;code&gt;div x y&lt;&#x2F;code&gt; as well.&lt;&#x2F;li&gt;
&lt;li&gt;For &lt;code&gt;mod x y&lt;&#x2F;code&gt;, the MONAD language specifies that &lt;code&gt;x ≥ 0&lt;&#x2F;code&gt; and &lt;code&gt;y &amp;gt; 0&lt;&#x2F;code&gt;. Therefore, &lt;code&gt;mod x y&lt;&#x2F;code&gt; is a no-op whenever &lt;code&gt;x &amp;lt; y&lt;&#x2F;code&gt;, since the remainder when dividing a smaller number with a larger one is the smaller number itself.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;eql x y&lt;&#x2F;code&gt; can be a no-op in two ways. If &lt;code&gt;x = y&lt;&#x2F;code&gt;, we will get the result &lt;code&gt;x = Exact(1)&lt;&#x2F;code&gt; so we have a no-op if &lt;code&gt;x = y = Exact(1)&lt;&#x2F;code&gt;. If &lt;code&gt;x != y&lt;&#x2F;code&gt; then we’ll get the result &lt;code&gt;x = Exact(0)&lt;&#x2F;code&gt; so we have a no-op if &lt;code&gt;x = Exact(0)&lt;&#x2F;code&gt; and &lt;code&gt;y&lt;&#x2F;code&gt; is any &lt;code&gt;Exact&lt;&#x2F;code&gt; value &lt;em&gt;except&lt;&#x2F;em&gt; &lt;code&gt;Exact(0)&lt;&#x2F;code&gt;. So &lt;code&gt;eql x y&lt;&#x2F;code&gt; is a no-op if &lt;code&gt;x = y = Exact(1)&lt;&#x2F;code&gt; or if &lt;code&gt;x = Exact(0)&lt;&#x2F;code&gt; and &lt;code&gt;y != Exact(0)&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;For the forms of these instructions that involve a literal number instead of a second register, such as &lt;code&gt;add x 11&lt;&#x2F;code&gt;, we simply consider the literal number as its corresponding &lt;code&gt;Exact&lt;&#x2F;code&gt; value, then apply the same rules as above.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Let’s move forward in two steps. First, let’s write a function that accepts a (non-&lt;code&gt;inp&lt;&#x2F;code&gt;) &lt;code&gt;Instruction&lt;&#x2F;code&gt; and two &lt;code&gt;Value&lt;&#x2F;code&gt; inputs &lt;code&gt;left&lt;&#x2F;code&gt; and &lt;code&gt;right&lt;&#x2F;code&gt;, and returns &lt;code&gt;true&lt;&#x2F;code&gt; if the instruction is a no-op when executed on those values:&lt;&#x2F;p&gt;
&lt;div class=&quot;captioned-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;8b419b6164781175a5dc53f1de6edd89.js?file=is_instruction_no_op.rs&quot;&gt;&lt;&#x2F;script&gt;
  &lt;label for=&quot;mn-gist3&quot; class=&quot;margin-toggle&quot;&gt;&amp;#8853;&lt;&#x2F;label&gt;
  &lt;input type=&quot;checkbox&quot; id=&quot;mn-gist3&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;
  &lt;span class=&quot;marginnote&quot;&gt;Excerpt from &lt;code&gt;optimization.rs&lt;&#x2F;code&gt;. See this code in context &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part2_finished&#x2F;src&#x2F;optimization.rs#L39-L67&quot;&gt;in the GitHub repo&lt;&#x2F;a&gt;.&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;For convenience, I&#x27;ve &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;bfa9f9596c0e991e3cb2707b1d8c1e3dfffecb96&#x2F;src&#x2F;program.rs#L62&quot;&gt;already defined&lt;&#x2F;a&gt; some helper methods on &lt;code&gt;Instruction&lt;&#x2F;code&gt; that simply return the source&#x2F;destination register index (the &lt;code&gt;register()&lt;&#x2F;code&gt; function) and the literal or register operand, if the instruction has one (the &lt;code&gt;operand()&lt;&#x2F;code&gt; function).&lt;&#x2F;p&gt;
&lt;p&gt;We can now use &lt;code&gt;is_instruction_no_op&lt;&#x2F;code&gt; to find many more no-op instructions than the &lt;code&gt;remove_div_by_1&lt;&#x2F;code&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;bfa9f9596c0e991e3cb2707b1d8c1e3dfffecb96&#x2F;src&#x2F;optimization.rs#L3&quot;&gt;function&lt;&#x2F;a&gt; from &lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;compiler-adventures-part1-no-op-instructions&#x2F;&quot;&gt;the last episode&lt;&#x2F;a&gt; could find. Let&#x27;s update &lt;code&gt;remove_div_by_1&lt;&#x2F;code&gt; to track registers&#x27; values and take advantage of our new functions. It&#x27;ll still go through the list of instructions, compute how the register values change at each step, and eliminate instructions it discovers are no-ops.&lt;label for=&quot;sn-2&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-2&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-2&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;For experienced Rustaceans: this code isn&#x27;t idiomatic Rust. I&#x27;m trying to make it easier to understand for people of any background.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;And like that, we&#x27;ve implemented &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Constant_folding#Constant_propagation&quot;&gt;constant propagation&lt;&#x2F;a&gt;! Let&#x27;s rename &lt;code&gt;remove_div_by_1&lt;&#x2F;code&gt; to &lt;code&gt;constant_propagation&lt;&#x2F;code&gt; to celebrate our achievement!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;analyzing-our-optimizations-impact&quot;&gt;Analyzing our optimizations&#x27; impact&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s run our compiler on our Advent of Code input program and measure how well we did:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ cargo run analyze sample_programs&#x2F;aoc_challenge.txt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Finished dev [unoptimized + debuginfo] target(s) in 0.02s&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     Running `...&#x2F;monad_compiler analyze sample_programs&#x2F;aoc_challenge.txt`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Original vs optimized length:    252 vs 238 (-14)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Optimized is more efficient by:  5.88%&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Adding &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Constant_folding#Constant_propagation&quot;&gt;constant propagation&lt;&#x2F;a&gt; made our no-op detection twice as good as &lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;compiler-adventures-part1-no-op-instructions&#x2F;&quot;&gt;in part 1&lt;&#x2F;a&gt;! We managed to eliminate 7 more instructions compared to last time, for a total improvement of just under 6%.&lt;&#x2F;p&gt;
&lt;p&gt;On the other hand, maybe constant propagation should have had a larger impact? Let&#x27;s visualize our optimizations to figure out what&#x27;s going on.&lt;&#x2F;p&gt;
&lt;p&gt;We’ll use &lt;code&gt;cargo run registers &amp;lt;monad_program&amp;gt;&lt;&#x2F;code&gt; to generate a printout of the register values our optimizations determined at every instruction in the program, together with an indication whether that instruction was detected to be a no-op. I’ll include a few relevant snippets of the input program’s printout here — the full printout is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part2_finished&#x2F;printouts&#x2F;aoc_challenge_registers.txt&quot;&gt;available on GitHub&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The beginning of the program starts looking pretty good: in the first 10 instructions, most register values are &lt;code&gt;Exact&lt;&#x2F;code&gt;, and constant propagation helps eliminate 5 of these 10 instructions as no-ops. That’s a lot better than 6%!&lt;&#x2F;p&gt;
&lt;div class=&quot;outer-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;355fd4279e6f83880fd5f36dfc92437c.js?file=register_values.log&quot;&gt;&lt;&#x2F;script&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Unfortunately, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part2_finished&#x2F;printouts&#x2F;aoc_challenge_registers.txt#L51&quot;&gt;just a bit farther down&lt;&#x2F;a&gt;, the simulated register values quickly become a sea of &lt;code&gt;Unknown&lt;&#x2F;code&gt;, and few if any no-ops are found:&lt;&#x2F;p&gt;
&lt;div class=&quot;outer-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;78c782f96f7c40ed372e909e91fead7c.js?file=continued_register_values.log&quot;&gt;&lt;&#x2F;script&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Compiler optimizations use insight about the program&#x27;s behavior to make it more efficient. When the program starts, its initial state is fully known: all registers have &lt;code&gt;Exact(0)&lt;&#x2F;code&gt; values, and the only unknowns are values derived from input data read by &lt;code&gt;inp&lt;&#x2F;code&gt; instructions. It&#x27;s an insight-rich environment and our compiler performs nicely!&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately, our compiler isn&#x27;t yet sophisticated enough to continue generating insight about the rest of the program, so its performance drops accordingly. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part2_finished&#x2F;printouts&#x2F;aoc_challenge_registers.txt#L258&quot;&gt;The statistics at the end of the printout&lt;&#x2F;a&gt; summarize the situation: 91.6% of the time, at least one of the values used by the instruction was unknown, and 22.3% of the time, both values were unknown. With so few known values to work with, constant propagation unsurprisingly has a limited effect.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Total non-input instructions: 238&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;- with 1+ non-exact value:    218 (91.6%)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;- without any exact values:    53 (22.3%)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Shining a light on the patterns hiding within those &lt;code&gt;Unknown&lt;&#x2F;code&gt; values will be the focus of upcoming Compiler Adventures.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wrap-up-challenge-question&quot;&gt;Wrap up + challenge question&lt;&#x2F;h2&gt;
&lt;p&gt;Implementing one compiler optimization can open up new opportunities for another optimization to work its magic. We already included &lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;compiler-adventures-part1-no-op-instructions&#x2F;&quot;&gt;part 1&#x27;s &quot;eliminate no-ops&quot; idea&lt;&#x2F;a&gt; in this episode&#x27;s constant propagation code. In the next Compiler Adventure, we&#x27;ll use constant propagation as part of a fascinating new optimization that will become the foundation of most of our future work!&lt;&#x2F;p&gt;
&lt;p&gt;The completed code from this post is on branch &lt;code&gt;part2_finished&lt;&#x2F;code&gt; on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;tree&#x2F;part2_finished&quot;&gt;GitHub&lt;&#x2F;a&gt;. For a sneak peek of how we can improve our &lt;code&gt;Unknown&lt;&#x2F;code&gt; handling, consider the following program:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;inp w&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;add x w&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;eql w x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Right now, our compiler says that last &lt;code&gt;eql w x&lt;&#x2F;code&gt; instruction would evaluate to &lt;code&gt;Unknown&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;div class=&quot;outer-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;5aa15ad19c0fe98723902fb70e96acf3.js?file=ep2_challenge.log&quot;&gt;&lt;&#x2F;script&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Look carefully at the register values around &lt;code&gt;add x w&lt;&#x2F;code&gt; and &lt;code&gt;eql w x&lt;&#x2F;code&gt;. Can you figure out what the &lt;code&gt;eql w x&lt;&#x2F;code&gt; evaluates to? Is it really &lt;code&gt;Unknown&lt;&#x2F;code&gt;? Why isn&#x27;t our current &lt;code&gt;constant_propagation()&lt;&#x2F;code&gt; function able to do better?&lt;&#x2F;p&gt;
&lt;p&gt;All this and more in &lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;compiler-adventures-part3-value-numbering&#x2F;&quot;&gt;the next Compiler Adventure — see you next time!&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you liked this post, please share it with friends — compilers are for everyone!
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;PredragGruevski&quot;&gt;Reach out to me on Twitter&lt;&#x2F;a&gt; or join the discussion on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=30375051&quot;&gt;HackerNews&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Thanks to Hillel Wayne, Russell Cohen, Jonathan Paulson,&lt;&#x2F;em&gt; &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.jameskoppel.com&#x2F;&quot;&gt;&lt;em&gt;Jimmy Koppel&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;em&gt;, Aaron Brooks, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;vtjeng.com&#x2F;&quot;&gt;Vincent Tjeng&lt;&#x2F;a&gt;, James Logan, and Akshat Bubna for their feedback on drafts of this post. Any mistakes are mine alone.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Compiler Adventures, part 1: No-op Instructions</title>
        <published>2022-02-03T00:00:00+00:00</published>
        <updated>2022-02-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/compiler-adventures-part1-no-op-instructions/"/>
        <id>https://predr.ag/blog/compiler-adventures-part1-no-op-instructions/</id>
        
        <content type="html" xml:base="https://predr.ag/blog/compiler-adventures-part1-no-op-instructions/">&lt;p&gt;&lt;em&gt;A beginner-friendly introduction to compilers: follow along as we build a compiler from scratch, or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&quot;&gt;fork the code on GitHub&lt;&#x2F;a&gt; and add your own optimizations too! In this episode: eliminating no-op instructions.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;What part of computer science feels most like arcane magic? I&#x27;d say compilers. &quot;The magical incantation &lt;code&gt;gcc -O3 file.c&lt;&#x2F;code&gt; makes my program run 10x faster in ways I don&#x27;t understand&quot; sure sounds like arcane magic. Modern compilers are the product of engineer-centuries of work, so it&#x27;s unsurprising they feel intimidating.&lt;&#x2F;p&gt;
&lt;figure&gt;
    
    
    
    

    
    
    

    
    
    

    
    
    
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;predr.ag&amp;#x2F;processed_images&amp;#x2F;victoria_falls.4be473a146bccd17.jpg&quot; alt=&quot;A set of three massive waterfalls surrounded by lush plant life and large trees basking in the sunshine. Water spray causes a vivid rainbow to appear against the cliffs and the pristine blue sky.&quot;&gt;
    &lt;label for=&quot;mn-lead&quot; class=&quot;margin-toggle&quot;&gt;&amp;#8853;&lt;&#x2F;label&gt;
    &lt;input type=&quot;checkbox&quot; id=&quot;mn-lead&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;
    &lt;span class=&quot;marginnote figcaption&quot;&gt;
        View of the Victoria Falls of the Zambezi River, the largest sheet of falling water in the world. A scene of immense arcane power, perhaps similar to the arcane power of compilers.
        Source:
        &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;delso.photo&amp;#x2F;wp-content&amp;#x2F;uploads&amp;#x2F;2020&amp;#x2F;01&amp;#x2F;Cataratas_Victoria_Zambia-Zimbabue_2018-07-27_DD_30-34_PAN-1280x960.jpg&quot;&gt;Diego Delso, delso.photo&lt;&#x2F;a&gt;, CC BY-SA
    &lt;&#x2F;span&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;But there&#x27;s no law of the universe that says compilers &lt;em&gt;have to be&lt;&#x2F;em&gt; complex and difficult to understand.&lt;label for=&quot;sn-1&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-1&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-1&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;In fact, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;PredragGruevski&#x2F;status&#x2F;1470206964043071491&quot;&gt;simple compilers are everywhere&lt;&#x2F;a&gt;. We just don&#x27;t tend to notice that many tools fit the description of a compiler: a tool that takes input, analyzes it and performs some transformations on it, and produces output.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;

In this blog post series, we&#x27;ll build a compiler from scratch, one straightforward step at a time. Each blog post will tackle a facet of the compiler, and over time, our optimizations will add up to create arcane magic of our own.&lt;&#x2F;p&gt;
&lt;p&gt;Our compiler will optimize the simple MONAD programming language from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;adventofcode.com&#x2F;2021&#x2F;day&#x2F;24&quot;&gt;Advent of Code 2021 day 24&lt;&#x2F;a&gt;. However, this is not a guide to solving Advent of Code 2021 day 24, even though &lt;em&gt;there will definitely be spoilers&lt;&#x2F;em&gt;. Instead, our goal is to learn about compilers and have fun doing it. We’ll prioritize simple code and clear explanations of compiler ideas over everything else.&lt;&#x2F;p&gt;
&lt;p&gt;Even so, the techniques we cover here can generate some pretty impressive numbers! The Advent of Code challenge has &lt;strong&gt;22 trillion&lt;&#x2F;strong&gt; possible answers, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.mattkeeter.com&#x2F;blog&#x2F;2021-12-27-brute&#x2F;&quot;&gt;Matt Keeter&#x27;s blog&lt;&#x2F;a&gt; says that without optimizations, checking all of them would take more than &lt;strong&gt;3.5 years&lt;&#x2F;strong&gt;. The compiler techniques from this series can find the answer in &lt;strong&gt;0.16 seconds&lt;&#x2F;strong&gt;, more than &lt;strong&gt;700 million times faster&lt;&#x2F;strong&gt;. Not bad at all, right?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;&#x2F;h2&gt;
&lt;p&gt;We’ll write our compiler in Rust because of its excellent ergonomics, but knowing Rust isn’t a requirement for enjoying this series. Our compiler will be built from the most common data structures: tuples, maps (dictionaries), vectors (lists), etc. For example, Python&#x27;s &lt;code&gt;Tuple[int, int]&lt;&#x2F;code&gt; and &lt;code&gt;List[str]&lt;&#x2F;code&gt; type hints become &lt;code&gt;(i64, i64)&lt;&#x2F;code&gt; and &lt;code&gt;Vec&amp;lt;String&amp;gt;&lt;&#x2F;code&gt; in Rust, respectively. Another Rust type to know is &lt;code&gt;usize&lt;&#x2F;code&gt;: that&#x27;s Rust&#x27;s preferred unsigned integer type for counting and indexing operations.
When reading the code, feel free to mentally replace &lt;code&gt;usize&lt;&#x2F;code&gt; with &lt;code&gt;int&lt;&#x2F;code&gt; if coming from another programming language, like Python.&lt;label for=&quot;sn-2&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-2&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-2&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;If you aren&#x27;t familiar with Rust but are interested in learning it, the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;book&#x2F;ch01-00-getting-started.html&quot;&gt;Rust Book&lt;&#x2F;a&gt; is an excellent free online resource (also available in print), and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rustinaction.com&#x2F;&quot;&gt;Rust in Action&lt;&#x2F;a&gt; is an excellent book on learning Rust with an emphasis on systems programming in particular.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;All the code in this series is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&quot;&gt;available on GitHub&lt;&#x2F;a&gt; — feel free to fork the repo to follow along and make your own changes and optimizations as well! Each episode&#x27;s start and finish points will have an associated git branch. This episode starts on branch &lt;code&gt;part1&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;representing-a-monad-program&quot;&gt;Representing a MONAD program&lt;&#x2F;h2&gt;
&lt;p&gt;Here is a quick intro to the MONAD programming language from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;adventofcode.com&#x2F;2021&#x2F;day&#x2F;24&quot;&gt;Advent of Code 2021 day 24&lt;&#x2F;a&gt;. MONAD supports only four variables &lt;code&gt;w, x, y, z&lt;&#x2F;code&gt; (called &quot;registers&quot; in compiler terminology), and has six instructions that operate on them:&lt;&#x2F;p&gt;
&lt;figure&gt;
    
    
    
    

    
    
    

    
    
    

    
    
    
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;predr.ag&amp;#x2F;processed_images&amp;#x2F;MONAD language.e8caa8cbd15fdb6c.png&quot; alt=&quot;Screenshot of https:&amp;#x2F;&amp;#x2F;adventofcode.com&amp;#x2F;2021&amp;#x2F;day&amp;#x2F;24 explaining the MONAD language. Variables w, x, y, z all start with the value 0. There are 6 instructions:
        - &amp;quot;inp a&amp;quot; reads input and saves it in variable a.
        - &amp;quot;add a b&amp;quot; performs a += b
        - &amp;quot;mul a b&amp;quot; performs a *= b
        - &amp;quot;div a b&amp;quot; performs a &amp;#x2F;= b
        - &amp;quot;mod a b&amp;quot; stores the remainder of a &amp;#x2F; b into a
        - &amp;quot;eql a b&amp;quot; stores 1 in a if a == b, and 0 otherwise
        In all instructions, &amp;quot;a&amp;quot; can be any variable, and &amp;quot;b&amp;quot; can be any variable or any integer number.&quot;&gt;
    &lt;label for=&quot;mn-monad&quot; class=&quot;margin-toggle&quot;&gt;&amp;#8853;&lt;&#x2F;label&gt;
    &lt;input type=&quot;checkbox&quot; id=&quot;mn-monad&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;
    &lt;span class=&quot;marginnote figcaption&quot;&gt;
        The six instructions available in the MONAD programming language. Screenshot from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;adventofcode.com&#x2F;2021&#x2F;day&#x2F;24&quot;&gt;Advent of Code 2021 day 24&lt;&#x2F;a&gt;.
    &lt;&#x2F;span&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;We&#x27;ll represent &lt;code&gt;w, x, y, z&lt;&#x2F;code&gt; as &lt;code&gt;Register(0)&lt;&#x2F;code&gt; through &lt;code&gt;Register(3)&lt;&#x2F;code&gt;, which all have starting values of &lt;code&gt;0&lt;&#x2F;code&gt; at the beginning of a MONAD program. The six instructions can use any register, and sometimes take a second parameter which may be either a literal number or a register.&lt;&#x2F;p&gt;
&lt;div class=&quot;captioned-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;e8f9aea3ea2c14e3bb5bfd910ed5e379.js?file=program.rs&quot;&gt;&lt;&#x2F;script&gt;
  &lt;label for=&quot;mn-gist1&quot; class=&quot;margin-toggle&quot;&gt;&amp;#8853;&lt;&#x2F;label&gt;
  &lt;input type=&quot;checkbox&quot; id=&quot;mn-gist1&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;
  &lt;span class=&quot;marginnote&quot;&gt;Excerpt from &lt;code&gt;program.rs&lt;&#x2F;code&gt;. See this code in context &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part1&#x2F;src&#x2F;program.rs#L5-L52&quot;&gt;in the GitHub repo&lt;&#x2F;a&gt;.&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Based on this structure, MONAD instructions like &lt;code&gt;mul w 2&lt;&#x2F;code&gt; — &quot;multiply &lt;code&gt;w&lt;&#x2F;code&gt; by 2 and store the result in &lt;code&gt;w&lt;&#x2F;code&gt;&quot; — would become &lt;code&gt;Instruction::Mul(Register(0), Operand::Literal(2))&lt;&#x2F;code&gt;.
If the instruction specifies register &lt;code&gt;x&lt;&#x2F;code&gt; instead of the number &lt;code&gt;2&lt;&#x2F;code&gt;, the operand value would instead be &lt;code&gt;Operand::Register(Register(1))&lt;&#x2F;code&gt;: the &lt;code&gt;Operand::Register&lt;&#x2F;code&gt; enum variant with the struct &lt;code&gt;Register(1)&lt;&#x2F;code&gt; inside.&lt;label for=&quot;sn-3&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-3&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-3&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;The repetition of &lt;code&gt;Register&lt;&#x2F;code&gt; is a bit unfortunate. It is, however, idiomatic Rust to put structs inside enum variants of the same name. In principle, we could replace &lt;code&gt;Register&lt;&#x2F;code&gt; with &lt;code&gt;usize&lt;&#x2F;code&gt; everywhere, but then instructions would become a bit harder to read: &lt;code&gt;mul w 2&lt;&#x2F;code&gt; would become &lt;code&gt;Instruction::Mul(0, Operand::Literal(2))&lt;&#x2F;code&gt;, which reads dangerously close to &lt;code&gt;0 * 2&lt;&#x2F;code&gt; instead of &lt;code&gt;w * 2&lt;&#x2F;code&gt;.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;For this blog post, let&#x27;s assume we already have a way to parse a MONAD program into a list (&lt;code&gt;Vec&lt;&#x2F;code&gt;) of &lt;code&gt;Instruction&lt;&#x2F;code&gt; elements. This is a series on compilers, not parsers! But if you&#x27;re curious how the parser works, here is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;7b12dd8e425a161252f1b34580f80bf83e141189&#x2F;src&#x2F;parser.rs#L72&quot;&gt;a link to the function that will do our parsing&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;our-first-optimization&quot;&gt;Our first optimization&lt;&#x2F;h2&gt;
&lt;p&gt;The key to developing optimizations is looking at a program and thinking about how it might be improved without changing its meaning. Here are the first 5 instructions in the input program from Advent of Code:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;inp w&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;mul x 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;add x z&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;mod x 26&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;div z 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In our Rust representation, the parsed program would look like:&lt;&#x2F;p&gt;
&lt;div class=&quot;outer-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;e3f2d37663d3a234f8f795c7562455d1.js?file=parsed_program.rs&quot;&gt;&lt;&#x2F;script&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Take a look at that last instruction: &lt;code&gt;div z 1&lt;&#x2F;code&gt;. We are dividing a number by ... one. That doesn&#x27;t seem like it does much, does it? We don&#x27;t know the value of &lt;code&gt;z&lt;&#x2F;code&gt;, but that doesn&#x27;t matter: the value of &lt;code&gt;z&lt;&#x2F;code&gt; remains unchanged regardless of what it was.&lt;&#x2F;p&gt;
&lt;p&gt;Optimizing a program means finding more efficient ways to execute parts of the program &lt;em&gt;without the rest of the program noticing&lt;&#x2F;em&gt;. For example, imagine we are executing this program, and when &lt;code&gt;div z 1&lt;&#x2F;code&gt; is the next instruction to execute, we simply &lt;em&gt;skip it&lt;&#x2F;em&gt; instead of executing it. Will anything in the program behave differently as a result? Definitely not — all the &lt;code&gt;w, x, y, z&lt;&#x2F;code&gt; registers will still have correct values — so we can &lt;em&gt;optimize the program&lt;&#x2F;em&gt; by removing that instruction.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s formalize our idea: we plan on looking through our list of instructions, and every time we see an instruction that says &quot;divide by the value &lt;code&gt;1&lt;&#x2F;code&gt;&quot;, we simply discard it:&lt;&#x2F;p&gt;
&lt;div class=&quot;captioned-gist&quot;&gt;
  &lt;script src=&quot;https:&amp;#x2F;&amp;#x2F;gist.github.com&amp;#x2F;obi1kenobi&amp;#x2F;0f2967c4d7863b397ea7873d2900b459.js?file=optimization.rs&quot;&gt;&lt;&#x2F;script&gt;
  &lt;label for=&quot;mn-gist3&quot; class=&quot;margin-toggle&quot;&gt;&amp;#8853;&lt;&#x2F;label&gt;
  &lt;input type=&quot;checkbox&quot; id=&quot;mn-gist3&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;
  &lt;span class=&quot;marginnote&quot;&gt;Excerpt from &lt;code&gt;optimization.rs&lt;&#x2F;code&gt;. See this code in context &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&#x2F;blob&#x2F;part1_finished&#x2F;src&#x2F;optimization.rs#L3-L18&quot;&gt;in the GitHub repo&lt;&#x2F;a&gt;.&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Congratulations, we just created our first optimization pass:&lt;label for=&quot;sn-4&quot; class=&quot;margin-toggle sidenote-number&quot; id=&quot;snref-4&quot; role=&quot;button&quot; aria-label=&quot;Toggle sidenote&quot;&gt;&lt;sup class=&quot;sidenote-number-display&quot;&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-4&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;&lt;span class=&quot;sidenote&quot; role=&quot;note&quot; aria-label=&quot;Sidenote&quot;&gt;&lt;sup class=&quot;sidenote-prefix&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;sup&gt;&lt;span class=&quot;sidenote-content&quot;&gt;&lt;span class=&quot;sidenote-boundary&quot;&gt; [Sidenote: &lt;&#x2F;span&gt;A quick note for experienced Rustaceans: I know this code isn&#x27;t idiomatic Rust. I&#x27;m intentionally structuring the code to make it easier to understand for people of any background. Using &lt;code&gt;.into_iter().filter(...).collect()&lt;&#x2F;code&gt; would take the focus away from the compiler and move it onto Rust language features.&lt;span class=&quot;sidenote-boundary&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;

a flavor of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Redundant_code&quot;&gt;redundant code elimination&lt;&#x2F;a&gt;, called that because it eliminates code that has no influence over the rest of the program. This kind of &quot;do-nothing&quot; instruction is usually called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;english.stackexchange.com&#x2F;questions&#x2F;25993&#x2F;what-does-no-op-mean&quot;&gt;a &quot;no-op&quot;&lt;&#x2F;a&gt;. Let&#x27;s run the optimization on our Advent of Code input program and measure how well we did:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ cargo run analyze sample_programs&#x2F;aoc_challenge.txt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Finished dev [unoptimized + debuginfo] target(s) in 0.02s&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     Running `...&#x2F;monad_compiler analyze sample_programs&#x2F;aoc_challenge.txt`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Original vs optimized length:    252 vs 245 (-7)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Optimized is more efficient by:  2.86%&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With this simple 16-line optimization, we&#x27;ve already eliminated 7 no-op instructions and optimized the program by about 3% — and we&#x27;re just getting started!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-bigger-picture&quot;&gt;The bigger picture&lt;&#x2F;h2&gt;
&lt;p&gt;Right now, one might think: “What a silly little optimization, surely we didn’t need a compiler for this?” In a narrow sense, that&#x27;s correct: we could have found and removed all the “divide by 1” instructions by hand (or used a regex like &lt;code&gt;div [wxyz] 1&lt;&#x2F;code&gt; to remove them) and gotten the same 3% improvement.&lt;&#x2F;p&gt;
&lt;p&gt;However, even the most sophisticated compilers are just a collection of individually-simple rules like the one we just implemented. While each rule by itself may feel too trivial to be valuable, the combination of all the rules working together is the source of all compilers’ power. Running one optimization can unlock opportunities to perform other optimizations as well, and thus the whole becomes greater than the sum of its parts!&lt;&#x2F;p&gt;
&lt;p&gt;Most programs contain little if any obviously-redundant code like division by one. Our Advent of Code input program is no exception. But it&#x27;s good to get the easier wins first, especially when that makes our future work easier. Many of our future optimizations will be &lt;em&gt;much simpler&lt;&#x2F;em&gt; to implement knowing that any such redundant code they discover or create is not something they&#x27;ll have to worry about.&lt;&#x2F;p&gt;
&lt;p&gt;This optimization &lt;em&gt;currently&lt;&#x2F;em&gt; has only a 3% impact. But the next Compiler Adventure episode will already rely on redundant code elimination as a component of a more sophisticated optimization pass, making the overall effect much higher.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wrap-up-challenge-question&quot;&gt;Wrap up &amp;amp; challenge question&lt;&#x2F;h2&gt;
&lt;p&gt;The completed code at the end of this post is on branch &lt;code&gt;part1_finished&lt;&#x2F;code&gt; in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;obi1kenobi&#x2F;monad_compiler&quot;&gt;GitHub repo&lt;&#x2F;a&gt;. The next Compiler Adventure will pick up here and introduce another optimization.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s a little challenge in the meantime: there are two other instructions that have a no-op case like &lt;code&gt;div z 1&lt;&#x2F;code&gt;, where the instruction is a no-op regardless of the register&#x27;s value. Can you figure out what they are? We&#x27;ll solve this and more next time — see you in &lt;a href=&quot;https:&#x2F;&#x2F;predr.ag&#x2F;blog&#x2F;compiler-adventures-part2-constant-propagation&#x2F;&quot;&gt;the next Compiler Adventure&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;p&gt;If you liked this post, please share it with friends — compilers are for everyone!
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;PredragGruevski&quot;&gt;Reach out to me on Twitter&lt;&#x2F;a&gt; or join the discussion on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=30194571&quot;&gt;HackerNews&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Thanks to Hillel Wayne, Russell Cohen, Jonathan Paulson,&lt;&#x2F;em&gt; &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.jameskoppel.com&#x2F;&quot;&gt;&lt;em&gt;Jimmy Koppel&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;em&gt;, Aaron Brooks, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;vtjeng.com&#x2F;&quot;&gt;Vincent Tjeng&lt;&#x2F;a&gt;, James Logan, and Akshat Bubna for their feedback on drafts of this post. Any mistakes are mine alone.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>How Paxos and Two-Phase Commit Differ</title>
        <published>2021-01-26T00:00:00+00:00</published>
        <updated>2021-01-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Predrag Gruevski
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://predr.ag/blog/paxos-vs-2pc/"/>
        <id>https://predr.ag/blog/paxos-vs-2pc/</id>
        
        <content type="html" xml:base="https://predr.ag/blog/paxos-vs-2pc/">&lt;p&gt;Distributed systems courses frequently introduce
the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Paxos_(computer_science)&quot;&gt;Paxos&lt;&#x2F;a&gt; and
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Two-phase_commit_protocol&quot;&gt;two-phase commit (2PC)&lt;&#x2F;a&gt; protocols
in quick succession.
On one hand, this is a reasonable educational choice, as Paxos and 2PC are both examples
of consensus algorithms used in the real world.
On the other, it leads many students toward the dangerously incorrect conclusion
that Paxos and 2PC solve the same problem.
The best way to resolve this confusion is via an intuitive example &lt;em&gt;involving both algorithms&lt;&#x2F;em&gt;
where swapping their places &lt;em&gt;clearly does the wrong thing&lt;&#x2F;em&gt;.
Here we&#x27;ll dig into exactly such an example.&lt;&#x2F;p&gt;
&lt;figure&gt;
    
    
    
    

    
    
    

    
    
    

    
    
    
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;predr.ag&amp;#x2F;processed_images&amp;#x2F;paxos-island.3f4e6325d351ba6a.png&quot; alt=&quot;A small town at the edge of a blue sea, surrounded by thickly forested rolling hills going as far as the eye can see.&quot;&gt;
    &lt;label for=&quot;mn-paxos&quot; class=&quot;margin-toggle&quot;&gt;&amp;#8853;&lt;&#x2F;label&gt;
    &lt;input type=&quot;checkbox&quot; id=&quot;mn-paxos&quot; class=&quot;margin-toggle&quot;&#x2F;&gt;
    &lt;span class=&quot;marginnote figcaption&quot;&gt;
        The island of Paxos, after which the Paxos consensus algorithm is named.
        Source:
        &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;commons.wikimedia.org&amp;#x2F;wiki&amp;#x2F;File:%CE%93%CE%AC%CE%B9%CE%BF%CF%82..png&quot;&gt;Wikipedia&lt;&#x2F;a&gt;, CC BY-SA 4.0
    &lt;&#x2F;span&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Imagine you are booking a trip.
(I&#x27;ve been doing a lot of such imagining since March 2020.)
On your favorite travel website, you pick a hotel, some airline tickets, and a rental car,
then you enter your credit card information and click &quot;Book!&quot;
What happens next?&lt;&#x2F;p&gt;
&lt;p&gt;Needless to say, this operation in the real world is significantly more complex than
we could hope to cover here.
We&#x27;ll routinely gloss over many unimportant but nonetheless fascinating details, such as
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stripe.com&#x2F;blog&#x2F;payment-api-design&quot;&gt;how paying for things over the Internet actually works&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;two-phase-commit&quot;&gt;Two-phase commit&lt;&#x2F;h2&gt;
&lt;p&gt;You only want to pay the travel website if your choice of hotel, flight, and rental car
are all available.
Similarly, the companies involved don&#x27;t want to hold any reservations in your name
unless they can be sure that your bank will accept the credit card charges.
To realize your trip, multiple independent computer systems need to examine their
local information (bank account state, flight schedules, hotel room availability, etc.)
and agree that the transaction is safe to perform.&lt;&#x2F;p&gt;
&lt;p&gt;The travel website acts as the transaction coordinator of the two-phase commit.
First, it sends a &quot;prepare to commit&quot; message to the hotel, airline, rental agency, and your bank.
These messages ask the protocol participants to confirm that they have no objections
to the transaction based on their individual information.
For example, your airline will confirm that your selected seats are still available —
if someone else has booked them in the meantime, the airline will respond to the message
with an &quot;abort&quot; command, and the transaction will fail without charging your credit card.&lt;&#x2F;p&gt;
&lt;p&gt;Responses to these &quot;prepare to commit&quot; messages are binding: for example, once your bank
says &quot;the transfer is allowed and sufficient funds are available,&quot;
it can&#x27;t change its mind at a later time (in practice, at least not until a &lt;em&gt;very&lt;&#x2F;em&gt; long timeout,
usually 7–10 days).
This is commonly called &quot;authorizing the charge&quot; on your card — you have not been charged yet,
but your funds are held in reserve to ensure they remain available.&lt;&#x2F;p&gt;
&lt;p&gt;If your hotel, airline, rental agency, and bank all respond positively to
the &quot;prepare to commit&quot; messages, then you know your trip is viable, and the various other
parties know that you are able to pay for it.
The next step is to send a message to all participants confirming the transaction:
your card gets charged, your airline sends you a ticket confirmation, your hotel sends you
a room reservation, etc.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-can-t-this-be-paxos-instead&quot;&gt;Why can&#x27;t this be Paxos instead?&lt;&#x2F;h2&gt;
&lt;p&gt;There are many reasons, so let&#x27;s tackle just the most obvious one:
Paxos can&#x27;t represent the fact that in this algorithm,
&lt;em&gt;the different parties care about different things&lt;&#x2F;em&gt;.
The hotel doesn&#x27;t know or care about airline tickets, the rental agency can&#x27;t find you
a place to stay, and neither of them knows whether you have enough credit available from your bank.&lt;&#x2F;p&gt;
&lt;p&gt;To demonstrate the concrete problem here, let&#x27;s say you tried to set up a Paxos
instance where the travel website, your bank, the hotel, the airline, and
the rental agency each have 1 node in the Paxos group — a total of 5 nodes.&lt;&#x2F;p&gt;
&lt;p&gt;Per the rules of Paxos, if 3 of the 5 nodes (a majority) agree on a proposal,
that proposal is accepted — irrevocably so.
But this doesn&#x27;t make sense!
That would mean that the travel agency&#x27;s node, the airline&#x27;s node, and the hotel&#x27;s node
could decide to agree on the proposal and promise you that you can take your trip,
even though your bank won&#x27;t cover the payment and the rental agency has no cars available to rent!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;you-said-the-example-had-both-paxos-and-2pc-where-s-the-paxos&quot;&gt;You said the example had both Paxos and 2PC — where&#x27;s the Paxos?&lt;&#x2F;h2&gt;
&lt;p&gt;Remember how we said 2PC steps are binding?
For example, your bank must not forget that it promised to reserve some of your credit to
cover a transaction that is in progress, regardless of what failures might happen in its system.
In general, 2PC has a hard time recovering from failures of either the transaction coordinator
(the travel website in our example) or the transaction participants
(hotel &#x2F; airline &#x2F; bank &#x2F; rental agency).&lt;&#x2F;p&gt;
&lt;p&gt;Turning the 2PC transaction coordinator and each transaction participant
into a Paxos group is the key to &quot;not forgetting&quot; in the presence of failures.
For example, a Paxos-replicated bank system can continue to process your transaction
even if a minority of its servers crash, and will not forget to pay your travel agency
for your trip once the transaction commits.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;could-you-have-used-2pc-instead-of-paxos-for-the-replication-here&quot;&gt;Could you have used 2PC instead of Paxos for the replication here?&lt;&#x2F;h2&gt;
&lt;p&gt;Replication &lt;em&gt;isn&#x27;t the objective here&lt;&#x2F;em&gt; — fault tolerance is!
While you could successfully set up 2PC to replicate state across multiple identical machines,
recall that 2PC does not tolerate failures:
the first time a participant machine crashes in a 2PC setup, the protocol grinds to a halt.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;recap&quot;&gt;Recap&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Paxos and 2PC aim to solve different problems, and are not interchangeable for each other.&lt;&#x2F;li&gt;
&lt;li&gt;We use 2PC when multiple non-equivalent servers need to agree that a given operation is
safe to perform.
The &quot;non-equivalent&quot; part means Paxos doesn&#x27;t work for this use case.&lt;&#x2F;li&gt;
&lt;li&gt;In practice, each of the &quot;servers&quot; in the 2PC computation is actually a separate Paxos group.
This is how we ensure the &quot;2PC assumes all nodes never fail&quot; assumption holds up
in the real world — the Paxos groups provide fault tolerance.&lt;&#x2F;li&gt;
&lt;li&gt;Paxos provides fault tolerance through replication, and you could configure 2PC to
replicate data across multiple equivalent servers.
However, such 2PC replication cannot provide fault tolerance on its own because
2PC requires that &lt;em&gt;all nodes&lt;&#x2F;em&gt; continue to work — even a single failure will stop
the protocol&#x27;s progress.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
</feed>
