<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.1.1">Jekyll</generator><link href="https://kobzol.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://kobzol.github.io/" rel="alternate" type="text/html" /><updated>2026-08-06T14:28:23+00:00</updated><id>https://kobzol.github.io/feed.xml</id><title type="html">Kobzol’s blog</title><subtitle>Blog about programming stuff.</subtitle><entry><title type="html">Sovereign Tech Fellowship for Rust maintenance (June-July 2026 report)</title><link href="https://kobzol.github.io/rust/2026/08/03/stf-june-july-2026.html" rel="alternate" type="text/html" title="Sovereign Tech Fellowship for Rust maintenance (June-July 2026 report)" /><published>2026-08-03T13:00:00+00:00</published><updated>2026-08-03T13:00:00+00:00</updated><id>https://kobzol.github.io/rust/2026/08/03/stf-june-july-2026</id><content type="html" xml:base="https://kobzol.github.io/rust/2026/08/03/stf-june-july-2026.html"><![CDATA[<p>For the past few years, I was sponsored by Futurewei to work on upstream Rust. It was great to have the freedom to work on anything I wanted in Rust, be it new features, performance optimizations, bot/tooling/CI improvements or even non-technical work, such as mentoring, governance, surveys or preparing Rust talks. Alas, nothing lasts forever, and my open source funding ended this February.</p>

<p>While I did not have much time for Rust during the following months anyway, I have applied for the <a href="https://www.sovereign.tech/programs/fellowship">Sovereign Tech Fellowship</a> in the meantime, and I was very lucky to actually get into this year’s <a href="https://www.sovereign.tech/news/meet-the-2026-sovereign-tech-fellows">cohort</a> of funded open source maintainers! And what is even better is that another Rust maintainer, Denis Cornehl, who maintains <a href="https://docs.rs">docs.rs</a>, got into the same cohort too.</p>

<p>My funding started in June, and should last for a year, which is awesome, because now I can again fully focus on working on Rust. I thought that I would post periodic (bi-monthly) updates about the upstream Rust work that I do, similar to the one I shared <a href="/rust/rustc/2026/01/05/my-rust-contributions-in-2025.html">earlier this year</a>. Every two months, I’ll try to pick a few highlights, summarize the rest of interesting stuff that I worked on, and also provide contribution statistics and a raw list of opened PRs.</p>

<p>This post details my open source Rust work done in June and July 2026.</p>

<h2 id="compile-time-improvements">Compile time improvements</h2>

<p>After a relatively long time, I spent some effort on optimizing Rust compile times, which was fun, as this is one of my favourite activities. I approached it from a couple of angles.</p>

<h3 id="optimizing-cargo">Optimizing Cargo</h3>

<p>During my <a href="https://youtu.be/NhTwLwznok8?t=1319">bors talk</a> at RustWeek 2026, I noted that the compile times of tests in <a href="https://github.com/rust-lang/bors">bors</a> are pretty terrible. Recently, I finally spent some time analyzing the causes of it. I thought that I would be working on removing bottlenecks from the compiler (and I still plan to do that!), but by profiling, I actually found some bottlenecks in Cargo, so I took a detour and spent a few days to try to make it faster.</p>

<p>I sent a couple of pull requests to Cargo that removed unnecessary cloning (<a href="https://github.com/rust-lang/cargo/pull/17167">#17167</a>, <a href="https://github.com/rust-lang/cargo/pull/17176">#17176</a>, <a href="https://github.com/rust-lang/cargo/pull/17178">#17178</a>), switched to a better data structure (<a href="https://github.com/rust-lang/cargo/pull/17180">#17180</a>) or pre-allocated memory (<a href="https://github.com/rust-lang/cargo/pull/17177">#17177</a>). With the exception of <a href="https://github.com/rust-lang/cargo/pull/17167">#17167</a>, the perf. effect of those PRs was very small.</p>

<p>What helped much more was replacing the hashing algorithm used by hashmaps and sets in Cargo. Most of its associative data structures were using the default hasher from the standard library, which is <a href="https://github.com/rust-lang/cargo/issues/15649">quite slow</a>. I switched all such data structures to use <code class="language-plaintext highlighter-rouge">FxHash</code>, the hashing algorithm used by the Rust compiler, in <a href="https://github.com/rust-lang/cargo/pull/17169">#17169</a>. This resulted in ~8-10% wall-time improvement for no-op Cargo invocations on Zed, which was quite cool.</p>

<p>Finally, as discussed later in this post, I also configured Rust’s CI to optimize Cargo with <abbr title="Profile-Guided Optimizations">PGO</abbr> in <a href="https://github.com/rust-lang/rust/pull/159149">#159149</a>.</p>

<p>The changes I made were mostly only micro-optimizations, rather than algorithmic changes, which typically means that you won’t get that much speedup. That being said, the “final” results are still quite nice. I took nightly Cargo from 1st of July, which is a day before I started working on the optimizations, and compared it with 
<a href="https://github.com/rust-lang/rust/commit/5a8cd237d4fad99d862aed51fab8cb5345c97013"><code class="language-plaintext highlighter-rouge">5a8cd237d4fad99d862aed51fab8cb5345c97013</code></a><sup id="fnref:rustup-toolchain-install-master"><a href="#fn:rustup-toolchain-install-master" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>, which is the merge commit of the <a href="https://github.com/rust-lang/rust/pull/159149">PR</a> that added <abbr title="Profile-Guided Optimizations">PGO</abbr> to Cargo. I am not aware of other performance work happening in Cargo in the meantime and the Cargo submodule was since synced to <code class="language-plaintext highlighter-rouge">rust-lang/rust</code>, so this benchmark should mostly correspond to the improvements that I made.</p>

<p>I tested a no-op (also called “fresh”, meaning that all sources were up-to-date and the Rust compiler was not invoked) <code class="language-plaintext highlighter-rouge">cargo test --no-run</code> execution on <code class="language-plaintext highlighter-rouge">bors</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/pr/pe/ru/bors <span class="o">[</span>benchmark]<span class="nv">$ </span>hyperfine <span class="s2">"cargo +nightly-2026-07-01 test --no-run"</span> <span class="s2">"cargo +5a8cd237d4fad99d862aed51fab8cb5345c97013 test --no-run"</span> <span class="nt">--runs</span> 50
Benchmark 1: cargo +nightly-2026-07-01 <span class="nb">test</span> <span class="nt">--no-run</span>
Time <span class="o">(</span>mean ± σ<span class="o">)</span>:     227.6 ms ±   3.8 ms    <span class="o">[</span>User: 141.2 ms, System: 90.3 ms]
Range <span class="o">(</span>min … max<span class="o">)</span>:   222.1 ms … 239.2 ms    50 runs

Benchmark 2: cargo +5a8cd237d4fad99d862aed51fab8cb5345c97013 <span class="nb">test</span> <span class="nt">--no-run</span>
Time <span class="o">(</span>mean ± σ<span class="o">)</span>:     184.3 ms ±   6.6 ms    <span class="o">[</span>User: 104.2 ms, System: 84.3 ms]
Range <span class="o">(</span>min … max<span class="o">)</span>:   178.4 ms … 216.9 ms    50 runs

Summary
cargo +5a8cd237d4fad99d862aed51fab8cb5345c97013 <span class="nb">test</span> <span class="nt">--no-run</span> ran
1.24 ± 0.05 <span class="nb">times </span>faster than cargo +nightly-2026-07-01 <span class="nb">test</span> <span class="nt">--no-run</span>
</code></pre></div></div>

<p>I consistently saw a 20% wall-time improvement on <code class="language-plaintext highlighter-rouge">bors</code>. On <code class="language-plaintext highlighter-rouge">Zed</code>, I measured a similar 18% wall-time win (fresh <code class="language-plaintext highlighter-rouge">cargo check</code> time went from 1100ms to 930ms).</p>

<p>Since I was using the “Performance Optimizer Observation Platform” (<a href="https://github.com/andrewrk/poop">poop</a>) a lot while benchmarking Cargo during my optimization experiments, here are its results on <code class="language-plaintext highlighter-rouge">bors</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>poop <span class="s2">"cargo +nightly-2026-07-01 test --no-run"</span> <span class="s2">"cargo +5a8cd237d4fad99d862aed51fab8cb5345c97013 test --no-run"</span>

Benchmark 1 <span class="o">(</span>22 runs<span class="o">)</span>: cargo +nightly-2026-07-01 <span class="nb">test</span> <span class="nt">--no-run</span>
measurement          mean ± σ            min … max           outliers         delta
wall_time           228ms ± 12.8ms     219ms …  271ms          3 <span class="o">(</span>14%<span class="o">)</span>        0%
peak_rss            106MB ±  402KB     106MB …  107MB          0 <span class="o">(</span> 0%<span class="o">)</span>        0%
cpu_cycles          559M  ± 22.0M      540M  …  636M           2 <span class="o">(</span> 9%<span class="o">)</span>        0%
instructions       1.01G  ± 6.23M     1.01G  … 1.04G           1 <span class="o">(</span> 5%<span class="o">)</span>        0%
cache_references   35.3M  ±  461K     34.6M  … 37.1M           2 <span class="o">(</span> 9%<span class="o">)</span>        0%
cache_misses       9.29M  ±  112K     9.08M  … 9.64M           2 <span class="o">(</span> 9%<span class="o">)</span>        0%
branch_misses      4.37M  ± 55.4K     4.32M  … 4.58M           1 <span class="o">(</span> 5%<span class="o">)</span>        0%

Benchmark 2 <span class="o">(</span>28 runs<span class="o">)</span>: cargo +5a8cd237d4fad99d862aed51fab8cb5345c97013 <span class="nb">test</span> <span class="nt">--no-run</span>
measurement          mean ± σ            min … max           outliers         delta
wall_time           183ms ± 6.30ms     179ms …  214ms          1 <span class="o">(</span> 4%<span class="o">)</span>        ⚡- 19.7% ±  2.5%
peak_rss           90.6MB ±  167KB    90.2MB … 90.9MB          1 <span class="o">(</span> 4%<span class="o">)</span>        ⚡- 14.7% ±  0.2%
cpu_cycles          415M  ± 8.49M      408M  …  446M           2 <span class="o">(</span> 7%<span class="o">)</span>        ⚡- 25.7% ±  1.6%
instructions        736M  ± 5.41M      735M  …  764M           1 <span class="o">(</span> 4%<span class="o">)</span>        ⚡- 27.4% ±  0.3%
cache_references   26.1M  ±  489K     25.5M  … 28.2M           1 <span class="o">(</span> 4%<span class="o">)</span>        ⚡- 26.1% ±  0.8%
cache_misses       5.98M  ± 82.0K     5.84M  … 6.29M           1 <span class="o">(</span> 4%<span class="o">)</span>        ⚡- 35.6% ±  0.6%
branch_misses      3.47M  ± 29.5K     3.43M  … 3.58M           1 <span class="o">(</span> 4%<span class="o">)</span>        ⚡- 20.7% ±  0.6%
</code></pre></div></div>

<p>Around a quarter less executed instructions. Not bad for a few days of work! That being said, those were the low-hanging fruits, and I expect that further improvements using a similar approach would be difficult. Larger wins could be gained by parallelization or algorithmic changes, e.g. related to caching. And of course, those wins were only on <em>no-op</em> Cargo builds. In situations where the Rust compiler has to be invoked, it will typically dwarf any overhead coming from the execution of Cargo itself. But not always, and every win helps, so I’m nevertheless happy with the result.</p>

<p>Speaking of parallelization, it would help if Cargo was loading TOML manifests in parallel, as this takes a considerable chunk of its no-op execution time. Possible approaches to achieve this are described in <a href="https://github.com/rust-lang/cargo/issues/15676">this Cargo issue</a>. I spent a few days trying to perform this parallelization, but I don’t have much to show for it. I ran into <a href="https://github.com/rust-lang/cargo/issues/15676#issuecomment-4881170203">several problems</a> related to how the Cargo codebase is currently structured (some things are not <code class="language-plaintext highlighter-rouge">'static</code> or <code class="language-plaintext highlighter-rouge">Send/Sync</code>, which makes parallelization annoying, and Cargo doesn’t use tokio, so running async functions concurrently or in parallel is tricky).</p>

<p>It would probably require larger refactorings to Cargo to make this parallelization feasible. In the end, I managed to implement a very hacky version of a <a href="https://github.com/kobzol/cargo/commit/132bfc46d7320547eb01b3b94f80bd7faf780a3e">parallel worker queue</a>, which provided a 15% wall-time win on Zed with 8 threads (on top of the previous optimizations, except for <abbr title="Profile-Guided Optimizations">PGO</abbr>). But it actually involves <code class="language-plaintext highlighter-rouge">unsafe</code> to deal around one variable not being <code class="language-plaintext highlighter-rouge">'static</code>, and it is nowhere near being production ready. Maybe I will come back to this sometime later. If this sounds interesting to you, feel free to pick up this work (though I would suggest talking to the Cargo team on <a href="https://rust-lang.zulipchat.com/#narrow/channel/246057-t-cargo/topic/seeking.20guidance.20on.20cargo.20performance.20contributions/with/614184859">Zulip</a> first).</p>

<p>I would like to thank the Cargo team, especially Ed Page and Weihang Lo, for their quick reviews and fruitful discussions.</p>

<h3 id="macro-bloat-of-sqlxtest">Macro bloat of <code class="language-plaintext highlighter-rouge">#[sqlx::test]</code></h3>

<p>This is also related to the build times of bors. I already <a href="/rust/2026/06/21/optimizing-sqlx-test-rebuild-time.html">blogged</a> about this in detail recently, so I will keep this section short. Bors uses hundreds of <code class="language-plaintext highlighter-rouge">#[sqlx::test]</code> macro attributes, and it turns out that each such attribute was embedding all database migrations into the source code. There is a way to avoid it (described in the linked blog post), which I <a href="https://github.com/rust-lang/bors/pull/771">applied</a> to bors. And then I tried to <a href="https://github.com/transact-rs/sqlx/pull/4320">document this</a> and make it <a href="https://github.com/transact-rs/sqlx/pull/4326">easier to fix</a> directly in <code class="language-plaintext highlighter-rouge">sqlx</code>. This work is still ongoing.</p>

<h3 id="using-profile-guided-optimizations-pgo-for-more-rust-tools">Using Profile-Guided Optimizations (<abbr title="Profile-Guided Optimizations">PGO</abbr>) for more Rust tools</h3>

<p>A few years ago, I spent a <a href="/rust/rustc/2022/10/27/speeding-rustc-without-changing-its-code.html">lot of time</a> improving the way we use <abbr title="Profile-Guided Optimizations">PGO</abbr> and <a href="https://github.com/llvm/llvm-project/blob/main/bolt/README.md">BOLT</a> to optimize the Rust compiler and LLVM. Recently, I thought that it might be worth it to optimize also other parts of the toolchain (such as rustdoc, Cargo or Clippy) with <abbr title="Profile-Guided Optimizations">PGO</abbr> too.</p>

<p>First, I modified <code class="language-plaintext highlighter-rouge">bootstrap</code><sup id="fnref:bootstrap"><a href="#fn:bootstrap" class="footnote" rel="footnote" role="doc-noteref">2</a></sup> in <a href="https://github.com/rust-lang/rust/pull/158912">#158912</a> to allow specifying <abbr title="Profile-Guided Optimizations">PGO</abbr> profiles in a granular way, for individual tools and components. Then I used this new machinery to <abbr title="Profile-Guided Optimizations">PGO</abbr> optimize:</p>

<ul>
  <li>rustdoc (<a href="https://github.com/rust-lang/rust/pull/159091">#159091</a>), which resulted in an average ~4% wall-time <a href="https://perf.rust-lang.org/compare.html?start=31e4b8481fec5ae856d786ff3b75a5bccaf33725&amp;end=633dfd7c537f088ac1df67ea22f1d10215599cd7&amp;stat=wall-time&amp;check=false&amp;debug=false&amp;opt=false">improvement</a> on <code class="language-plaintext highlighter-rouge">doc</code> builds.</li>
  <li>Cargo (<a href="https://github.com/rust-lang/rust/pull/159149">#159149</a>), which improved the performance of a no-op cargo run on <code class="language-plaintext highlighter-rouge">bors</code> by ~5%<sup id="fnref:rustc-perf-cargo"><a href="#fn:rustc-perf-cargo" class="footnote" rel="footnote" role="doc-noteref">3</a></sup>.</li>
  <li>Clippy (<a href="https://github.com/rust-lang/rust/pull/159642">#159642</a>), which resulted in an average ~5% wall-time <a href="https://perf.rust-lang.org/compare.html?start=390279b302ca98ae270f434100ae3730531d1246&amp;end=caf67342a544d6eab7a40ebf0d02e802f62008a4&amp;stat=wall-time&amp;secondary=false">improvement</a> on <code class="language-plaintext highlighter-rouge">Clippy</code> builds.
    <ul>
      <li>This one is a bit on hold at the moment, because we need to make our CI faster first, so that we can afford the additional <abbr title="Profile-Guided Optimizations">PGO</abbr> training time. Making CI faster is one of efforts that I was also working on this month, more on that below.</li>
    </ul>
  </li>
</ul>

<p>While I was working on this, the newly introduced additional <abbr title="Profile-Guided Optimizations">PGO</abbr> training made our Linux CI job that produces these optimized artifacts (<code class="language-plaintext highlighter-rouge">dist-x86_64-linux</code>) too slow, so I took <a href="/rust/rustc/2023/07/30/optimizing-rust-ci-2023.html">another</a> look at optimizing it. By adding some parallelization in <a href="https://github.com/rust-lang/rust/pull/159524">#159524</a>, I made the job ~20 minutes faster, which more or less balanced out the additional time required to perform more <abbr title="Profile-Guided Optimizations">PGO</abbr>.</p>

<p>Perhaps more importantly, <code class="language-plaintext highlighter-rouge">@Mark-Simulacrum</code> started looking into throwing <a href="https://rust-lang.zulipchat.com/#narrow/channel/242791-t-infra/topic/Making.20dist-x86_64-linux.20job.20faster/near/611458465">more hardware</a> at this slow job, so in the near future, it might actually get <em>much faster</em> than it is today! That would be really cool, as it would allow us to get performance results on PRs very quickly.</p>

<h3 id="investigating-performance-of-the-new-build-dir-layout-in-cargo">Investigating performance of the new build dir layout in Cargo</h3>

<p>The Cargo team is planning to stabilize a new layout of the <a href="https://blog.rust-lang.org/2026/03/13/call-for-testing-build-dir-layout-v2">build directory</a>, which would unlock a lot of cool improvements. In fact, the Cargo team <a href="https://github.com/rust-lang/cargo/pull/16807">already stabilized</a> this recently, but it since had to be <a href="https://github.com/rust-lang/cargo/pull/17187">reverted</a>; I’ll describe why below.</p>

<p>The new build dir layout has one property that is a bit annoying. Before, Cargo used to put all artifacts of dependencies (mainly <code class="language-plaintext highlighter-rouge">.rlib</code> and <code class="language-plaintext highlighter-rouge">.rmeta</code> files) into a single directory, and then passed a single flag to <code class="language-plaintext highlighter-rouge">rustc</code> (<code class="language-plaintext highlighter-rouge">-L &lt;dir&gt;</code>), so that the compiler could look them up in this directory. With the new build dir layout, Cargo instead creates a separate directory per dependency, and then passes each directory to <code class="language-plaintext highlighter-rouge">rustc</code> with a separate <code class="language-plaintext highlighter-rouge">-L</code> argument. This might not sound so bad, until you get to projects like <a href="https://github.com/zed-industries/zed">Zed</a>, which have over two thousands dependencies.</p>

<p>There are two issues with this. The first one is operating system limits on the length of command-line arguments. Passing thousand of CLI paths, where each path can have 100 bytes or more, quickly adds up. Of course, this was a problem long before the new build dir layout, especially on Windows. Cargo can already work around this using <code class="language-plaintext highlighter-rouge">argfiles</code>; if an invocation of <code class="language-plaintext highlighter-rouge">rustc</code> fails because of the command-line length limit, Cargo will instead write the arguments into an (arg)file, and then pass that file to <code class="language-plaintext highlighter-rouge">rustc</code> using the <code class="language-plaintext highlighter-rouge">@argfile</code> syntax. This sort of works, although it is not optimal for performance, because Cargo has to start <code class="language-plaintext highlighter-rouge">rustc</code> twice, and also write a file to disk for each process invocation. Another issue is that some tools do not handle argfiles when dealing with Rust compilation, which means that they might stop working with the new build dir layout. This was the case for <a href="https://github.com/mozilla/sccache"><code class="language-plaintext highlighter-rouge">sccache</code></a>, because it <a href="https://github.com/mozilla/sccache/issues/183">did not support argfiles</a>. Luckily, that is being <a href="https://github.com/mozilla/sccache/pull/2782">fixed</a> now.</p>

<p>So what is the problem, when we have the argfile workaround? Well, shortly after stabilization, I tried the new build dir layout on Zed, and it <a href="https://github.com/rust-lang/cargo/issues/17183">failed</a>. It turns out that not just the command-line length is a problem, but also environment variable length. Recently, Cargo started passing many more paths than before to the <code class="language-plaintext highlighter-rouge">LD_LIBRARY_PATH</code> variable<sup id="fnref:ld-library-path"><a href="#fn:ld-library-path" class="footnote" rel="footnote" role="doc-noteref">4</a></sup>. On Zed, this resulted in this environment variable having over 150 KiB (!) of data. Unsurprisingly, Linux didn’t like that.</p>

<p>The second issue is the performance of dependency lookups in the Rust compiler. Historically, the compiler was optimized precisely for the case of being passed a single <code class="language-plaintext highlighter-rouge">-L</code> argument, which was supposed to contain all the necessary dependencies. The compiler thus does a lot of preprocessing to optimize lookups of dependencies in a single large dependency. With the new build dir layout, this is completely opposite, because the compiler receives hundreds or even thousands of <code class="language-plaintext highlighter-rouge">-L</code> arguments, but each such directory typically contains only a single file.</p>

<p>This means that the previous optimizations can actually be harmful to performance, and also result in quadratic behavior. On our artificial <a href="https://github.com/rust-lang/rustc-perf/blob/main/collector/compile-benchmarks/large-workspace/Cargo.toml"><code class="language-plaintext highlighter-rouge">large-workspace</code></a> benchmark, which contains hundreds of crates that depend on one another, the old dir layout resulted in ~4 thousand dependency queries in the compiler, while the new dir layout resulted in ~2 <em>million</em> queries. In absolute terms, it was only a ~20ms difference, but it is still not ideal to do so much unneeded work.</p>

<p>This was interesting to me, because over the past year I did some work on optimizing search path lookups (<a href="https://github.com/rust-lang/rust/pull/145408">#145408</a>, <a href="https://github.com/rust-lang/rust/pull/153131">#153131</a>). So I rewrote the dependency lookup in a way that would be independent on the number of <code class="language-plaintext highlighter-rouge">-L</code> paths passed to the compiler, and thus be fast with both the old and the new build dir layout. This work was done in <a href="https://github.com/rust-lang/rust/pull/158823">#158823</a>. Enabling the new build dir layout made the <code class="language-plaintext highlighter-rouge">large-workspace</code> benchmark ~twice slower, and merging this PR made it ~2x faster, so it more or less balanced itself out.</p>

<p><code class="language-plaintext highlighter-rouge">@ranger-ross</code>, who implemented the new build dir layout and is pushing its stabilization (and who recently became a <a href="https://github.com/rust-lang/team/pull/2575">member</a> of the Cargo team :tada:) already implemented several optimizations to avoid passing unnecessary paths to <code class="language-plaintext highlighter-rouge">rustc</code> (<a href="https://github.com/rust-lang/cargo/pull/17168">#17168</a>, <a href="https://github.com/rust-lang/cargo/pull/17191">#17191</a>, <a href="https://github.com/rust-lang/cargo/pull/17236">#17236</a>). I also tried to help slightly with this in <a href="https://github.com/rust-lang/cargo/pull/17174">#17174</a>, which avoids passing unnecessary lint flags when compiling dependencies (for which most lints do not report warnings anyway).</p>

<p>While that helps, the fact remains that Cargo still passes a lot of paths to <code class="language-plaintext highlighter-rouge">rustc</code> with the new build dir layout. I suppose that we will have to see whether this will be an issue in practice or not. Having the dependencies exist in separate directories is core to the new build dir layout, but maybe we could do some workarounds to merge both approaches. For example, Cargo could create a single temporary directory with symlinks pointing to the individual dependency files. But it is hard to tell how fast that would be.</p>

<p>Anyway, after these optimizations landed, the new build dir layout was <a href="https://github.com/rust-lang/cargo/pull/17258">re-enabled</a>, and it is now used by default in nightly Cargo. If you have use-cases that are broken by the new build dir layout, please report them <a href="https://github.com/rust-lang/cargo/issues/17287">here</a>. I hope that the new build dir layout will work fine on nightly :crossed_fingers:.</p>

<h2 id="other-things-i-worked-on">Other things I worked on</h2>

<ul>
  <li>An <a href="https://rust-lang.zulipchat.com/#narrow/channel/326414-t-infra.2Fbootstrap/topic/.E2.9C.94.20weird.20code.20in.20fill_target_compiler">innocuous question</a> on Zulip led me down on a hunt during which I found that passing certain C/C++ build flags in the <code class="language-plaintext highlighter-rouge">bootstrap</code> build system has been broken for years. I fixed that in <a href="https://github.com/rust-lang/rust/pull/158169">#158169</a>, and re-enabled debuginfo compression for the little debuginfo that we ship in our distributed Rust artifacts.
    <ul>
      <li>Funnily enough, this caused backtraces in builds of the compiler with debuginfo to be <a href="https://github.com/rust-lang/rust/issues/158969">much slower</a> to print. It turns out that it can be very slow to decompress hundreds of MiBs with <code class="language-plaintext highlighter-rouge">zlib</code>, especially with the pure-Rust <code class="language-plaintext highlighter-rouge">miniz_oxide</code> implementation used by the standard library. I tried to replace the implementation with the much faster <code class="language-plaintext highlighter-rouge">zlib-rs</code> crate, but it turns out that it didn’t help <a href="https://github.com/rust-lang/rust/issues/158969#issuecomment-4928124652">at all</a> here :( I even tried switching to <a href="https://github.com/rust-lang/rust/issues/158969#issuecomment-4934887305">zstd</a>, but with the pure-Rust <code class="language-plaintext highlighter-rouge">ruzstd</code> crate, it was even slower than <code class="language-plaintext highlighter-rouge">zlib</code>. Some issues are just annoying like that.</li>
    </ul>
  </li>
  <li>I did some other random work on compiler performance in <a href="https://github.com/rust-lang/rust/pull/158842">#158842</a> and <a href="https://github.com/rust-lang/rust/pull/158931">#158931</a>.</li>
  <li>I started looking into debuginfo generation performance (<a href="https://github.com/rust-lang/rust/pull/158868">#158868</a>). There is a lot of wasted work here done by the Rust compiler, I hope we will be able to <a href="https://rust-lang.zulipchat.com/#narrow/channel/604410-t-compiler.2Frelink-dont-rebuild/topic/Span.20sensitivity/near/609306703">improve it</a>.</li>
  <li>I started working on integrating <a href="https://github.com/obi1kenobi/cargo-semver-checks"><code class="language-plaintext highlighter-rouge">cargo-semver-checks</code></a> into Rust’s CI, so that we can check whether the standard library hasn’t introduced a semver breaking change by accident (<a href="https://github.com/rust-lang/rust/pull/159188">#159188</a>, <a href="https://github.com/rust-lang/rust/pull/159671">#159671</a>, <a href="https://github.com/rust-lang/rust/pull/160253">#160253</a>).</li>
  <li>I tried to move forward with stabilizing <code class="language-plaintext highlighter-rouge">-Zembed-metadata=no</code> in Cargo in <a href="https://github.com/rust-lang/cargo/pull/17149">#17149</a>, <a href="https://github.com/rust-lang/cargo/pull/17266">#17266</a> and <a href="https://github.com/rust-lang/cargo/pull/17267">#17267</a>. You can read more about this compiler/Cargo flag in my <a href="/rust/rustc/2025/06/02/reduce-cargo-target-dir-size-with-z-no-embed-metadata.html">previous</a> blog post.
    <ul>
      <li>Since the new build dir layout is now enabled on nightly too, we decided to postpone this to the middle of August, to avoid enabling two disruptive directory layout changes at once.</li>
    </ul>
  </li>
  <li>I implemented tooling for helping uphold our <a href="https://github.com/rust-lang/team/pull/2583">alumni policy</a>, and started using it to move inactive Rust Project members to alumni status, to keep our member database up-to-date.</li>
  <li>I implemented various improvements to our bots, such as adding <a href="https://github.com/rust-lang/rustc-perf/pull/2501">historical charts</a> for toolchain size to rustc-perf or teaching bors how to <a href="https://github.com/rust-lang/bors/pull/784">communicate</a> with us on Zulip.</li>
  <li><code class="language-plaintext highlighter-rouge">@Mark-Simulacrum</code> did some experiments with using EC2 runners for some of our CI jobs, instead of using CodeBuild. It looks like it could speed up the jobs we most care about by 2x while making them cheaper, by using Zen 5 machines for them. It will also require some tooling implemented somewhere in our infra though. We decided to test this in bors; it is now ready for experimentation, after some bors implementation work (<a href="https://github.com/rust-lang/bors/pull/786">#786</a>, <a href="https://github.com/rust-lang/bors/pull/791">#791</a>, <a href="https://github.com/rust-lang/bors/pull/792">#792</a>, <a href="https://github.com/rust-lang/bors/pull/794">#794</a>, <a href="https://github.com/rust-lang/bors/pull/799">#799</a>, <a href="https://github.com/rust-lang/bors/pull/800">#800</a>, <a href="https://github.com/rust-lang/bors/pull/802">#802</a>).</li>
  <li>As usually, I did lots of tiny fixes and improvements to CI, infrastructure or tooling of various <code class="language-plaintext highlighter-rouge">rust-lang</code> repositories.</li>
  <li>As usually, I participated in the Compiler <a href="https://github.com/rust-lang/rustc-perf/tree/main/triage#rust-compiler-performance-triage">performance triage</a>. I helped <a href="https://github.com/rust-lang/team/pull/2537">onboard</a> <code class="language-plaintext highlighter-rouge">@JonathanBrouwer</code> to the triage rotation, to bolster our ranks.</li>
  <li>As a part of the Rust funding team, I communicated with a lot of Rust maintainers, and also potential funders, to find opportunities for funding Rust maintainers. We will have very exciting updates on this front soon (in August). I also started working on some <a href="https://github.com/rust-lang/www.rust-lang.org/pull/2323">improvements</a> to the Rust website, to highlight funded contributors.</li>
  <li>I did a bunch of reviews for the two Google Summer of Code projects that I mentor, and overall oversaw the progress of our Rust GSoC 2026 cohort. All contributors have passed their midterm evaluations, and they are doing a great job! You can follow their progress on <a href="https://rust-lang.zulipchat.com/#narrow/channel/421156-gsoc">our Zulip</a>.</li>
  <li>I ran a <a href="https://github.com/rust-lang/surveys/pull/409">survey</a> about the performance of the Rust Leadership Council.</li>
  <li>I wrote a couple of blog posts on the official Rust blog. Two maintainer spotlights (with <a href="https://blog.rust-lang.org/inside-rust/2026/06/03/maintainer-spotlight-tiffany-pek-yuan-tiif/">@tiif</a> and <a href="https://blog.rust-lang.org/inside-rust/2026/07/07/maintainer-spotlight-gen-li-rami3l/">@rami3l</a>), the <a href="https://blog.rust-lang.org/2026/06/02/launching-the-rust-foundation-maintainers-fund/">launch</a> of the Rust Foundation Maintainers Fund and the Funding team and an <a href="https://blog.rust-lang.org/inside-rust/2026/06/04/how-josh-helps-rust-manage-code-across-multiple-repositories/">appreciation post</a> for the <a href="https://github.com/josh-project/josh">Josh</a> tool, which helps us manage subtrees in the <code class="language-plaintext highlighter-rouge">rust-lang/rust</code> repository.</li>
  <li>I had a <a href="https://rustmeet.eu/how-we-ship-the-rust-toolchain/">keynote</a> about the Rust Project infrastructure and tooling at the RustMeet conference in Poland.<sup id="fnref:rustmeet-recording"><a href="#fn:rustmeet-recording" class="footnote" rel="footnote" role="doc-noteref">5</a></sup></li>
  <li>I participated in many discussions about AI policies. That is something that I didn’t enjoy very much.</li>
</ul>

<h2 id="contribution-statistics-and-pull-requests">Contribution statistics and pull requests</h2>

<p>Below you can find some statistics and a list of PRs that I opened during June and July 2026.</p>

<!-- stats -->

<ul>
  <li>Opened <strong>162</strong> pull requests in Rust-related repositories.</li>
  <li>Reviewed <strong>108</strong> pull requests in Rust-related repositories.</li>
  <li>Sent <strong>651</strong> comments in the <code class="language-plaintext highlighter-rouge">rust-lang</code> GitHub organization.</li>
  <li>Sent <strong>1725</strong> public and <strong>2789</strong> private messages on the <a href="https://rust-lang.zulipchat.com">Rust Zulip</a>.</li>
</ul>

<!-- pr-list -->

<details>
  <summary>List of opened PRs</summary>

  <h3 id="rust-langrust-39-prs">rust-lang/rust (39 PRs)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/rust/pull/157798">#157798</a>: Prevent approving PRs that wait for Crater or formal decisions (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/158169">#158169</a>: Fix debuginfo compression in bootstrap (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/158562">#158562</a>: Improve tracing of steps in bootstrap (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/158609">#158609</a>: Update sccache to 0.16.0 (<span style="color: green;">open</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/158823">#158823</a>: Flatten all -L search paths into a single list of files (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/158842">#158842</a>: Use SmallVec for return places in MIR (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/158868">#158868</a>: Cache some data structures for debuginfo generation (<span style="color: green;">open</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/158912">#158912</a>: Introduce new bootstrap config section for <abbr title="Profile-Guided Optimizations">PGO</abbr> configuration (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/158925">#158925</a>: [perf] Link LLVM statically on Linux x64 (<span style="color: red;">closed</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/158931">#158931</a>: Inline some <code class="language-plaintext highlighter-rouge">Symbol</code> functions (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/158932">#158932</a>: Do not build the compiler when invoking <code class="language-plaintext highlighter-rouge">x perf compare</code> (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159040">#159040</a>: Print step stack trace when bootstrap panics (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159056">#159056</a>: Print a loud error if the configured LLDB cannot be found (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159091">#159091</a>: Use <abbr title="Profile-Guided Optimizations">PGO</abbr> for rustdoc (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159126">#159126</a>: Fix PR number in bootstrap’s change tracker (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159149">#159149</a>: Use <abbr title="Profile-Guided Optimizations">PGO</abbr> for Cargo (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159177">#159177</a>: Explicitly materialize debuginfo tests for all debuggers (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159178">#159178</a>: Print duration of BOLT instrumentation and optimization steps (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159188">#159188</a>: Always generate private and hidden items in JSON docs of the stdlib (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159193">#159193</a>: Port compiletest’s CLI to clap (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159251">#159251</a>: Bump rustc-perf submodule (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159270">#159270</a>: Only force backtraces when a panic occurs in bootstrap (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159378">#159378</a>: [perf] Use a flat token stream representation when lexing (<span style="color: green;">open</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159451">#159451</a>: Remove config cloning in compiletest (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159455">#159455</a>: Implement opt-in debugger discovery for GDB and LLDB in bootstrap (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159500">#159500</a>: Move compiletest CLI parsing to <code class="language-plaintext highlighter-rouge">cli.rs</code> (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159524">#159524</a>: Gather <abbr title="Profile-Guided Optimizations">PGO</abbr> profiles in parallel (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159642">#159642</a>: Optimize Clippy with <abbr title="Profile-Guided Optimizations">PGO</abbr> (<span style="color: green;">open</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159671">#159671</a>: Add semver check test command for checking API compatibility of stdlib (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159710">#159710</a>: Add rustdoc/cargo <abbr title="Profile-Guided Optimizations">PGO</abbr> profiles to reproducible artifacts (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159765">#159765</a>: Avoid spurious rebuilds of JSON docs in bootstrap (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159832">#159832</a>: Use RUNNER_TEMP in <code class="language-plaintext highlighter-rouge">distcheck</code> when running on CI (<span style="color: green;">open</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159992">#159992</a>: Do not run tests in <code class="language-plaintext highlighter-rouge">dist</code> jobs (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159994">#159994</a>: Show jobs where a given test was executed in <code class="language-plaintext highlighter-rouge">test-dashboard</code> (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/159999">#159999</a>: Fix invalidation of stdlib in bootstrap when using non-LLVM codegen backends (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/160152">#160152</a>: Create on-demand CI job for testing EC2 instances (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/160200">#160200</a>: Run mir-opt panic=abort tests on CI (<span style="color: green;">open</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/160253">#160253</a>: Add CI job for checking stdlib semver compatibility (<span style="color: green;">open</span>)</li>
    <li><a href="https://github.com/rust-lang/rust/pull/160257">#160257</a>: EC2 runner test (<span style="color: green;">open</span>)</li>
  </ul>

  <h3 id="rust-langteam-31-prs">rust-lang/team (31 PRs)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/team/pull/2515">#2515</a>: Add website entry for the Program team (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2537">#2537</a>: Add Jonathan Brouwer to wg-compiler-performance (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2555">#2555</a>: Configure trusted publishing for <code class="language-plaintext highlighter-rouge">rustc-hash</code> (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2556">#2556</a>: Fix dry run (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2565">#2565</a>: Change default branch of <code class="language-plaintext highlighter-rouge">rustc-perf</code> to <code class="language-plaintext highlighter-rouge">main</code> (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2583">#2583</a>: Add command for finding inactive users and moving users to alumni (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2588">#2588</a>: Move lukas-code to alumni (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2589">#2589</a>: Allow setting labels with triagebot (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2590">#2590</a>: Enable merge queue for the blog repository (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2591">#2591</a>: Allow deploying the blog from the merge queue (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2592">#2592</a>: Create <code class="language-plaintext highlighter-rouge">maintainers-in-residence</code> marker team (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2594">#2594</a>: Configure trusted publishing for futures-rs (<span style="color: green;">open</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2595">#2595</a>: Configure trusted publishing for jobserver-rs (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2596">#2596</a>: Move edmilsonefs to alumni (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2597">#2597</a>: Move joelpalmer to alumni (<span style="color: green;">open</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2598">#2598</a>: Move Muirrum to alumni (<span style="color: green;">open</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2599">#2599</a>: Move cdmistman to alumni (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2600">#2600</a>: Move anden3 to alumni (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2601">#2601</a>: Move vertexclique to alumni (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2602">#2602</a>: Move JakobDegen to alumni (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2603">#2603</a>: Move tranquillity-codes to alumni (<span style="color: green;">open</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2604">#2604</a>: Move celaus to alumni (<span style="color: red;">closed</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2605">#2605</a>: Move alibektas to alumni (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2606">#2606</a>: Move KittyBorgX to alumni (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2607">#2607</a>: Move manyinsects to alumni (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2608">#2608</a>: Move shard77 to alumni (<span style="color: green;">open</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2609">#2609</a>: Move johncsimon to alumni (<span style="color: green;">open</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2610">#2610</a>: Move rbtcollins to alumni (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2615">#2615</a>: Archive community-events and community-localization teams (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2624">#2624</a>: Add Abi Broom to funding-advisors (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/team/pull/2636">#2636</a>: Configure merge queue for rustfmt (<span style="color: #8250DF;">merged</span>)</li>
  </ul>

  <h3 id="rust-langrustc-perf-20-prs">rust-lang/rustc-perf (20 PRs)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/rustc-perf/pull/2483">#2483</a>: Add 2026-06-01 triage (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rustc-perf/pull/2488">#2488</a>: Downgrade openssl-sys (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rustc-perf/pull/2490">#2490</a>: Update perf. triage rotation (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rustc-perf/pull/2495">#2495</a>: Change default branch to <code class="language-plaintext highlighter-rouge">main</code> (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rustc-perf/pull/2496">#2496</a>: Remove OpenSSL from Dockerfile (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rustc-perf/pull/2497">#2497</a>: Add <code class="language-plaintext highlighter-rouge">rustc-perf</code> PR note when marking PRs as rollup=never (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rustc-perf/pull/2500">#2500</a>: Fix rustdoc lookup on Windows (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rustc-perf/pull/2501">#2501</a>: Add artifact size history chart to toolchain page (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rustc-perf/pull/2502">#2502</a>: Properly set rustc-fake extension on Windows (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rustc-perf/pull/2503">#2503</a>: Run benchmark smoke test for all profiles on Windows on CI (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rustc-perf/pull/2504">#2504</a>: Fix selecting color for the bootstrap chart on the toolchain page (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rustc-perf/pull/2505">#2505</a>: Add 30 day history link to artifact size tab on the compare page (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rustc-perf/pull/2506">#2506</a>: Add 2026-07-21 triage (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rustc-perf/pull/2507">#2507</a>: Add early check for missing rustdoc/clippy in a toolchain (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rustc-perf/pull/2508">#2508</a>: Download Clippy when a Clippy profile is requested (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rustc-perf/pull/2512">#2512</a>: Rename <code class="language-plaintext highlighter-rouge">frontend_threads</code> to <code class="language-plaintext highlighter-rouge">Threads</code> (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rustc-perf/pull/2513">#2513</a>: Run DocJson benchmarks on CI (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rustc-perf/pull/2514">#2514</a>: Fix compilation of regex-automata-0.4.8 with <code class="language-plaintext highlighter-rouge">doc-json</code> (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rustc-perf/pull/2515">#2515</a>: Run DocJson benchmarks on production by default (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rustc-perf/pull/2516">#2516</a>: Add <code class="language-plaintext highlighter-rouge">doc-json</code> filter to the compare page (<span style="color: #8250DF;">merged</span>)</li>
  </ul>

  <h3 id="rust-langbors-19-prs">rust-lang/bors (19 PRs)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/bors/pull/762">#762</a>: Show try build parent on a separate line (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/bors/pull/766">#766</a>: Add “Co-authored-by” commit trailer for squashed commits (<span style="color: red;">closed</span>)</li>
    <li><a href="https://github.com/rust-lang/bors/pull/767">#767</a>: Add “Co-authored-by” commit trailer for squashed commits (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/bors/pull/770">#770</a>: Implement delegation to arbitrary GitHub users (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/bors/pull/771">#771</a>: Optimize test rebuild times by specifying a default migrator (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/bors/pull/774">#774</a>: Improve logging in mergeability check (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/bors/pull/776">#776</a>: Update some dependencies (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/bors/pull/777">#777</a>: Allow providing a reason for tree closure (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/bors/pull/778">#778</a>: Allow attaching notes to PRs (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/bors/pull/780">#780</a>: Allow setting squashed commit message to the PR body (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/bors/pull/781">#781</a>: Mark PRs that had a significant perf. benchmark on the queue page (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/bors/pull/782">#782</a>: Add Beaver favicon to bors (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/bors/pull/784">#784</a>: Teach bors to post to Zulip and notify about tree closed/open events (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/bors/pull/786">#786</a>: Add support for launching EC2 runners (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/bors/pull/791">#791</a>: Install AWS CLI in the Dockerfile (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/bors/pull/792">#792</a>: Assume AWS role when running aws commands (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/bors/pull/793">#793</a>: Revert “Show expected time left for pending PRs” (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/bors/pull/794">#794</a>: Use latest template EC2 version (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/bors/pull/795">#795</a>: Add table with pending builds to the queue page (<span style="color: #8250DF;">merged</span>)</li>
  </ul>

  <h3 id="rust-langcargo-13-prs">rust-lang/cargo (13 PRs)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/cargo/pull/17103">#17103</a>: Add funding links (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/cargo/pull/17149">#17149</a>: Rename <code class="language-plaintext highlighter-rouge">-Zno-embed-metadata</code> to <code class="language-plaintext highlighter-rouge">-Zembed-metadata=no</code> (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/cargo/pull/17167">#17167</a>: Avoid cloning parsed TOML manifest in <code class="language-plaintext highlighter-rouge">ManifestErrorContext</code> (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/cargo/pull/17169">#17169</a>: Change HashMaps and HashSets in Cargo to use Fxhasher (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/cargo/pull/17173">#17173</a>: Try to run tests in release mode (<span style="color: red;">closed</span>)</li>
    <li><a href="https://github.com/rust-lang/cargo/pull/17174">#17174</a>: Do not pass lint rustflags when <code class="language-plaintext highlighter-rouge">--cap-lints=allow</code> is set (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/cargo/pull/17175">#17175</a>: Make TOML manifest document non-optional (<span style="color: red;">closed</span>)</li>
    <li><a href="https://github.com/rust-lang/cargo/pull/17176">#17176</a>: Avoid extra clone of parsed TOML manifest (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/cargo/pull/17177">#17177</a>: Pre-allocate a few vectors (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/cargo/pull/17178">#17178</a>: Remove unneeded cloning when parsing package index (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/cargo/pull/17180">#17180</a>: Use a set when checking visited workspace members (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/cargo/pull/17266">#17266</a>: Allow setting <code class="language-plaintext highlighter-rouge">-Zembed-metadata</code> value from the config (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/cargo/pull/17267">#17267</a>: Enable <code class="language-plaintext highlighter-rouge">-Zembed-metadata=no</code> by default on nightly Cargo (<span style="color: green;">open</span>)</li>
  </ul>

  <h3 id="rust-langci-mirrors-6-prs">rust-lang/ci-mirrors (6 PRs)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/ci-mirrors/pull/37">#37</a>: Mirror sccache 0.16.0 (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/ci-mirrors/pull/38">#38</a>: Mirror sccache 0.16.0 for x64 Windows (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/ci-mirrors/pull/39">#39</a>: Mirror sccache 0.16.0 for x64 Apple (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/ci-mirrors/pull/40">#40</a>: Mirror sccache 0.16.0 (attempt 4/N) (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/ci-mirrors/pull/51">#51</a>: Mirror gettext-1.0.tar.gz (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/ci-mirrors/pull/52">#52</a>: Mirror NetBSD 10.1 files (<span style="color: #8250DF;">merged</span>)</li>
  </ul>

  <h3 id="rust-langblogrust-langorg-4-prs">rust-lang/blog.rust-lang.org (4 PRs)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1865">#1865</a>: Add Maintainer spotlight interview with tiif (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1866">#1866</a>: Josh appreciation blog post (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1878">#1878</a>: Add Maintainer spotlight interview with rami3l (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1893">#1893</a>: Use merge queue (<span style="color: #8250DF;">merged</span>)</li>
  </ul>

  <h3 id="rust-langjosh-sync-4-prs">rust-lang/josh-sync (4 PRs)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/josh-sync/pull/51">#51</a>: Allow passing custom josh-proxy path (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/josh-sync/pull/52">#52</a>: Read Zulip bot e-mail from inputs and not secrets in CI action (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/josh-sync/pull/53">#53</a>: Install <code class="language-plaintext highlighter-rouge">josh</code> binaries into a local directory (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/josh-sync/pull/54">#54</a>: Add filter version config field (<span style="color: #8250DF;">merged</span>)</li>
  </ul>

  <h3 id="rust-langrustccodegencranelift-3-prs">rust-lang/rustc_codegen_cranelift (3 PRs)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/rustc_codegen_cranelift/pull/1672">#1672</a>: Rustc pull update (<span style="color: red;">closed</span>)</li>
    <li><a href="https://github.com/rust-lang/rustc_codegen_cranelift/pull/1678">#1678</a>: Rustc pull update (<span style="color: red;">closed</span>)</li>
    <li><a href="https://github.com/rust-lang/rustc_codegen_cranelift/pull/1679">#1679</a>: Rustc pull update (<span style="color: green;">open</span>)</li>
  </ul>

  <h3 id="rust-langrustfmt-3-prs">rust-lang/rustfmt (3 PRs)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/rustfmt/pull/6943">#6943</a>: Add funding links (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rustfmt/pull/6987">#6987</a>: Merge Linux, Windows and Mac CI into one workflow (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rustfmt/pull/6989">#6989</a>: Use merge queue (<span style="color: #8250DF;">merged</span>)</li>
  </ul>

  <h3 id="rust-langcalendar-2-prs">rust-lang/calendar (2 PRs)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/calendar/pull/124">#124</a>: Add JonathanBrouwer to perf. triage rotation (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/calendar/pull/126">#126</a>: Add periodic events to the Leadership Council calendar (<span style="color: green;">open</span>)</li>
  </ul>

  <h3 id="rust-langrust-log-analyzer-2-prs">rust-lang/rust-log-analyzer (2 PRs)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/rust-log-analyzer/pull/98">#98</a>: Make failed job doc URL more visible (<span style="color: #8250DF;">merged</span>)</li>
    <li><a href="https://github.com/rust-lang/rust-log-analyzer/pull/99">#99</a>: Update Zizmor and fix lint (<span style="color: #8250DF;">merged</span>)</li>
  </ul>

  <h3 id="transact-rssqlx-2-prs">transact-rs/sqlx (2 PRs)</h3>
  <ul>
    <li><a href="https://github.com/transact-rs/sqlx/pull/4320">#4320</a>: Mention in docs that many <code class="language-plaintext highlighter-rouge">#[sqlx::test]</code>s can slow down rebuild time (<span style="color: green;">open</span>)</li>
    <li><a href="https://github.com/transact-rs/sqlx/pull/4326">#4326</a>: Add support for setting a default migrator (<span style="color: green;">open</span>)</li>
  </ul>

  <h3 id="rust-langfutures-rs-1-pr">rust-lang/futures-rs (1 PR)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/futures-rs/pull/3027">#3027</a>: Add release-plz to use trusted publishing (<span style="color: green;">open</span>)</li>
  </ul>

  <h3 id="rust-langjobserver-rs-1-pr">rust-lang/jobserver-rs (1 PR)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/jobserver-rs/pull/124">#124</a>: Configure trusted publishing (<span style="color: #8250DF;">merged</span>)</li>
  </ul>

  <h3 id="rust-langpromote-release-1-pr">rust-lang/promote-release (1 PR)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/promote-release/pull/114">#114</a>: Split rustc promotion into a separate binary (<span style="color: #8250DF;">merged</span>)</li>
  </ul>

  <h3 id="rust-langrfcbot-rs-1-pr">rust-lang/rfcbot-rs (1 PR)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/rfcbot-rs/pull/354">#354</a>: Remove <code class="language-plaintext highlighter-rouge">needs-fcp</code> label when an FCP is started (<span style="color: #8250DF;">merged</span>)</li>
  </ul>

  <h3 id="rust-langrust-analyzer-1-pr">rust-lang/rust-analyzer (1 PR)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/rust-analyzer/pull/22588">#22588</a>: Add funding links (<span style="color: #8250DF;">merged</span>)</li>
  </ul>

  <h3 id="rust-langrust-clippy-1-pr">rust-lang/rust-clippy (1 PR)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/rust-clippy/pull/17238">#17238</a>: Add funding links (<span style="color: #8250DF;">merged</span>)</li>
  </ul>

  <h3 id="rust-langrust-forge-1-pr">rust-lang/rust-forge (1 PR)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/rust-forge/pull/1072">#1072</a>: Fix Rust source archive reproduction instructions (<span style="color: #8250DF;">merged</span>)</li>
  </ul>

  <h3 id="rust-langrustc-dev-guide-1-pr">rust-lang/rustc-dev-guide (1 PR)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2906">#2906</a>: Update syntax of try build delegation in bors (<span style="color: #8250DF;">merged</span>)</li>
  </ul>

  <h3 id="rust-langrustc-hash-1-pr">rust-lang/rustc-hash (1 PR)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/rustc-hash/pull/79">#79</a>: Configure trusted publishing via release-plz (<span style="color: #8250DF;">merged</span>)</li>
  </ul>

  <h3 id="rust-langrustup-1-pr">rust-lang/rustup (1 PR)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/rustup/pull/4905">#4905</a>: Add funding links (<span style="color: #8250DF;">merged</span>)</li>
  </ul>

  <h3 id="rust-langsimpleinfra-1-pr">rust-lang/simpleinfra (1 PR)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/simpleinfra/pull/1131">#1131</a>: Configure Zulip bot for bors (<span style="color: #8250DF;">merged</span>)</li>
  </ul>

  <h3 id="rust-langsurveys-1-pr">rust-lang/surveys (1 PR)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/surveys/pull/409">#409</a>: Add 2026 Leadership Council review survey (<span style="color: #8250DF;">merged</span>)</li>
  </ul>

  <h3 id="rust-langthanks-1-pr">rust-lang/thanks (1 PR)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/thanks/pull/105">#105</a>: Add CSV output mode (<span style="color: green;">open</span>)</li>
  </ul>

  <h3 id="rust-langwwwrust-langorg-1-pr">rust-lang/www.rust-lang.org (1 PR)</h3>
  <ul>
    <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2323">#2323</a>: Add dedicated Maintainer in Residence page (<span style="color: green;">open</span>)</li>
  </ul>

</details>

<h2 id="conclusion">Conclusion</h2>

<p>Overall, these two months felt really good. Even though I was very busy in my personal life, and I wasn’t working much during the first two weeks of June due to preparing for RustMeet, I feel like I managed to do a lot of work on Rust, which always fills me with joy. As always, none of this would be possible without the help of <em>many</em> other people from the Rust Project, who collaborated with me, discussed various things with me, and reviewed my code. Thank you very much!</p>

<p>I would also like to thank the <a href="https://www.sovereign.tech/">Sovereign Tech Agency</a> for funding my open source maintenance work! I am really grateful for this opportunity.</p>

<p>Recently, I also enrolled into <a href="https://github.com/sponsors/kobzol">GitHub Sponsors</a>. I did not advertise it much, because I currently have funding for my open source work through the Sovereign Tech Fellowship. However, that is for one year (and it also isn’t full-time), and it is hard to say whether I will be able to find the next source of funding after that. So it is good to have at least some backup. If you’d like to support my open source Rust work, I would really appreciate it! You can find more ways of supporting Rust Project maintainers <a href="https://rust-lang.org/funding/">here</a>.</p>

<p>By the way, since some people keep asking me, unless otherwise noted, I usually do not use LLMs for my Rust work. In the past two months, there was one <a href="https://github.com/rust-lang/rust/pull/159193">pull request</a> where I performed a command-line argument refactoring that was very mechanical and boilerplate-y (moving from <code class="language-plaintext highlighter-rouge">getopts</code> to <code class="language-plaintext highlighter-rouge">clap</code>) with the assistance of an LLM.</p>

<p>If you have any questions regarding my upstream Rust work, feel free to ask on <a href="https://www.reddit.com/r/rust/comments/1vecx73/sovereign_tech_fellowship_for_rust_maintenance/">Reddit</a>.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:rustup-toolchain-install-master">
      <p>Any merged commit in <code class="language-plaintext highlighter-rouge">rust-lang/rust</code> can be directly installed using <a href="https://github.com/kennytm/rustup-toolchain-install-master"><code class="language-plaintext highlighter-rouge">rustup-toolchain-install-master</code></a>. It’s a super useful tool! <a href="#fnref:rustup-toolchain-install-master" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:bootstrap">
      <p>The build system of the Rust toolchain. <a href="#fnref:bootstrap" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:rustc-perf-cargo">
      <p>We cannot really see the effects of Cargo performance improvements in <code class="language-plaintext highlighter-rouge">rustc-perf</code>. <a href="#fnref:rustc-perf-cargo" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:ld-library-path">
      <p>The name of this environment variable, used for looking up paths for the dynamic linker, is different on other operating systems. <a href="#fnref:ld-library-path" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:rustmeet-recording">
      <p>Once the recording is available, I will share it <a href="/talks">here</a>. <a href="#fnref:rustmeet-recording" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="rust" /><summary type="html"><![CDATA[For the past few years, I was sponsored by Futurewei to work on upstream Rust. It was great to have the freedom to work on anything I wanted in Rust, be it new features, performance optimizations, bot/tooling/CI improvements or even non-technical work, such as mentoring, governance, surveys or preparing Rust talks. Alas, nothing lasts forever, and my open source funding ended this February.]]></summary></entry><entry><title type="html">Optimizing #[sqlx::test] rebuild time</title><link href="https://kobzol.github.io/rust/2026/06/21/optimizing-sqlx-test-rebuild-time.html" rel="alternate" type="text/html" title="Optimizing #[sqlx::test] rebuild time" /><published>2026-06-21T16:00:00+00:00</published><updated>2026-06-21T16:00:00+00:00</updated><id>https://kobzol.github.io/rust/2026/06/21/optimizing-sqlx-test-rebuild-time</id><content type="html" xml:base="https://kobzol.github.io/rust/2026/06/21/optimizing-sqlx-test-rebuild-time.html"><![CDATA[<blockquote>
  <p>You might find this post especially useful if you have a project with many <code class="language-plaintext highlighter-rouge">#[sqlx::test]</code> tests.</p>
</blockquote>

<p>One of the upstream Rust projects that I worked on during the past few years was the rewrite of <a href="https://github.com/rust-lang/bors"><code class="language-plaintext highlighter-rouge">bors</code></a>,
the merge queue bot we use to merge all <a href="https://github.com/rust-lang/rust"><code class="language-plaintext highlighter-rouge">rust-lang/rust</code></a> PRs. If you are interested in learning more about this bot, check out my <a href="https://www.youtube.com/watch?v=NhTwLwznok8">talk</a> from RustWeek 2026.</p>

<p>I’m quite proud of the integration test suite of <code class="language-plaintext highlighter-rouge">bors</code>, which I spent a lot of effort on, and thanks
to which the bot has been working pretty much flawlessly since we deployed it to production in January 2026
(despite GitHub lately often having… troubles).</p>

<p>One thing that I’m not very happy about though is the incremental rebuild time of <code class="language-plaintext highlighter-rouge">bors</code>, and in particular its
test suite. It takes a long time (~8-10s) to rebuild the tests after each change on my laptop,
which is quite bad for productivity.</p>

<p>Recently I finally found some time to profile<sup id="fnref:samply"><a href="#fn:samply" class="footnote" rel="footnote" role="doc-noteref">1</a></sup> its build time, and learned that it is caused by a
combination of several factors:</p>
<ul>
  <li>Generation of debug information takes a long time. This is a <a href="/rust/rustc/2025/05/20/disable-debuginfo-to-improve-rust-compile-times.html">known issue</a>, but in
this case I didn’t want to give up debug info, because I actually debug and step through <code class="language-plaintext highlighter-rouge">bors</code> tests
quite often.</li>
  <li><code class="language-plaintext highlighter-rouge">rustc</code> takes a long time to load and persist the incremental session. I plan to take a look into this.</li>
  <li>Probably because of all the debuginfo (the final binary has like 220 MiB), it takes <code class="language-plaintext highlighter-rouge">lld</code> a whole second (!)
to link the tests. With <a href="https://github.com/wild-linker/wild"><code class="language-plaintext highlighter-rouge">wild</code></a>, it’s just ~200ms.</li>
  <li>The <code class="language-plaintext highlighter-rouge">sqlx::test</code>s that I am using heavily in <code class="language-plaintext highlighter-rouge">bors</code> take a long time to compile. This is what I will focus on
in this post.</li>
</ul>

<h2 id="slow-compilation-of-sqlx-tests">Slow compilation of sqlx tests</h2>

<p>As a frame of reference, for my benchmark I was using <code class="language-plaintext highlighter-rouge">touch &lt;test-file&gt; &amp;&amp; time cargo test --no-run</code>.
Even after a no-op change, it took ~7.5 seconds to recompile the tests, which is super slow.</p>

<p>Of course, it is very well known that <a href="https://github.com/transact-rs/sqlx"><code class="language-plaintext highlighter-rouge">sqlx</code></a>’s proc macros can slow down compilation times,
because of all the <del>crimes</del> interesting things that they do<sup id="fnref:sqlx-db"><a href="#fn:sqlx-db" class="footnote" rel="footnote" role="doc-noteref">2</a></sup>. However, the case that I encountered
might not be so obvious. In my case, <code class="language-plaintext highlighter-rouge">sqlx</code> did not actually even connect to a database! Because I’m compiling with <code class="language-plaintext highlighter-rouge">SQLX_OFFLINE=1</code>, unless I work directly on SQL queries. And <em>yes</em>, I am setting
<code class="language-plaintext highlighter-rouge">opt-level = 3</code> for the <code class="language-plaintext highlighter-rouge">sqlx-macros</code> crate, as <a href="https://github.com/transact-rs/sqlx#compile-time-verification">recommended</a> by the <code class="language-plaintext highlighter-rouge">sqlx</code> documentation.</p>

<p>So what is happening here? To find out, it is important to understand what is happening when you
have a test like this:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[sqlx::test]</span>
<span class="k">async</span> <span class="k">fn</span> <span class="nf">test_foo</span><span class="p">(</span><span class="n">pool</span><span class="p">:</span> <span class="nn">sqlx</span><span class="p">::</span><span class="n">PgPool</span><span class="p">)</span> <span class="p">{}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">#[sqlx::test]</code> attribute is super useful, because it creates a new database before the execution
of the test, runs migrations on it, and then gives you a database connection pool, so that you can
run your tests against an actual database, and not against a mocked HashMap<sup id="fnref:testing"><a href="#fn:testing" class="footnote" rel="footnote" role="doc-noteref">3</a></sup>.</p>

<p>Wait, did I say migrations? Hmm, where does it find them? Well, from disk, of course! Each usage of
<code class="language-plaintext highlighter-rouge">#[sqlx::test]</code> will gather all migrations from a directory on disk, and then read, parse, validate
and hash each migration. Perhaps counter-intuitively, this part is not that slow! Turns
out that Rust is actually quite fast (who knew, right??), and if you do not have gigabytes of migrations, I/O is probably also not a problem<sup id="fnref:windows"><a href="#fn:windows" class="footnote" rel="footnote" role="doc-noteref">4</a></sup>.</p>

<p>What is worse is the generated output of those macros. For each such test, the macro will generate a
complete list of migrations, including their text content and a checksum in the form of a byte array,
in the Rust source code as a constant. So if you expand the macro, before each test you’ll find something
like this:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">args</span><span class="nf">.migrator</span><span class="p">(</span><span class="o">&amp;</span><span class="p">::</span><span class="nn">sqlx</span><span class="p">::</span><span class="nn">migrate</span><span class="p">::</span><span class="n">Migrator</span> <span class="p">{</span>
   <span class="n">migrations</span><span class="p">:</span> <span class="p">::</span><span class="nn">std</span><span class="p">::</span><span class="nn">borrow</span><span class="p">::</span><span class="nn">Cow</span><span class="p">::</span><span class="nf">Borrowed</span><span class="p">(</span><span class="o">&amp;</span><span class="p">[</span>
      <span class="p">::</span><span class="nn">sqlx</span><span class="p">::</span><span class="nn">migrate</span><span class="p">::</span><span class="n">Migration</span> <span class="p">{</span>
        <span class="n">version</span><span class="p">:</span> <span class="mi">20240517094752i64</span><span class="p">,</span>
        <span class="n">description</span><span class="p">:</span> <span class="p">::</span><span class="nn">std</span><span class="p">::</span><span class="nn">borrow</span><span class="p">::</span><span class="nn">Cow</span><span class="p">::</span><span class="nf">Borrowed</span><span class="p">(</span><span class="s">"create build"</span><span class="p">),</span>
        <span class="n">migration_type</span><span class="p">:</span> <span class="p">::</span><span class="nn">sqlx</span><span class="p">::</span><span class="nn">migrate</span><span class="p">::</span><span class="nn">MigrationType</span><span class="p">::</span><span class="n">ReversibleUp</span><span class="p">,</span>
        <span class="n">sql</span><span class="p">:</span> <span class="p">::</span><span class="nn">std</span><span class="p">::</span><span class="nn">borrow</span><span class="p">::</span><span class="nn">Cow</span><span class="p">::</span><span class="nf">Borrowed</span><span class="p">(</span><span class="s">"CREATE TABLE &lt;skipped&gt;)"</span><span class="p">),</span>
        <span class="n">no_tx</span><span class="p">:</span> <span class="k">false</span><span class="p">,</span>
        <span class="n">checksum</span><span class="p">:</span> <span class="p">::</span><span class="nn">std</span><span class="p">::</span><span class="nn">borrow</span><span class="p">::</span><span class="nn">Cow</span><span class="p">::</span><span class="nf">Borrowed</span><span class="p">(</span><span class="o">&amp;</span><span class="p">[</span><span class="mi">193u8</span><span class="p">,</span> <span class="mi">202u8</span><span class="p">,</span> <span class="o">&lt;</span><span class="n">skipped</span><span class="o">&gt;</span><span class="p">]),</span>
      <span class="p">},</span>
      <span class="p">::</span><span class="nn">sqlx</span><span class="p">::</span><span class="nn">migrate</span><span class="p">::</span><span class="n">Migration</span> <span class="p">{</span>
        <span class="o">&lt;</span><span class="n">skipped</span><span class="o">&gt;</span>
      <span class="p">},</span>
      <span class="o">&lt;</span><span class="n">skipped</span><span class="o">&gt;</span>
   <span class="p">]),</span>
   <span class="o">&lt;</span><span class="n">skipped</span><span class="o">&gt;</span>
<span class="p">});</span>
</code></pre></div></div>

<p>The example above is shortened, and it skips a lot of stuff. The actual generated code will be much longer, and of course it scales with the number (and content) of your migrations.</p>

<p>Now, if you have something like this in your source code once, that’s not so bad. However, in <code class="language-plaintext highlighter-rouge">bors</code>, there are ~350 <code class="language-plaintext highlighter-rouge">sqlx</code> tests and 30 migrations. And at that point, it starts to add up rather quickly.</p>

<p>To test my hypothesis that migrations might be causing some of the build slowness, I tested what would happen
if I had only one migration by deleting the rest of them. And sure enough, the rebuild time immediately went from ~7.5s to ~5s! What is perhaps even more telling is that the size of the output of <code class="language-plaintext highlighter-rouge">cargo expand --lib --tests</code> went from 32 MiB (!) with 30 migrations to “only” 6 MiB with a single migration. Compiling an additional 26 MiB of Rust code sure isn’t for free.</p>

<p>It wasn’t just about the compilation time of the generated code though. In the profiles, it looked like converting all the migration description data to tokens using the <code class="language-plaintext highlighter-rouge">quote</code> crate during the proc macro execution also takes a non-trivial amount of time.</p>

<p>This behavior can be pretty inconspicious, because at the start of the project, the rebuilds were fast
(or at least, <em>faster</em>). But then, with each added test, and each added migration, the rebuild time
slowly increases, so it creeps up on you.</p>

<p><del>I tried if the experimental <a href="https://github.com/rust-lang/rust/pull/145354">proc macro caching</a> feature that I landed in the compiler last year might help, but it didn’t. Probably the time needed to compile the generated code dwarfs the time to run the proc macro itself, so it does not help if the proc macro itself is cached.</del></p>

<p>Nevermind: <code class="language-plaintext highlighter-rouge">#[sqlx::test]</code> is an <em>attribute</em>, not a <em>derive</em> macro, so this flag doesn’t apply here.
Thanks to <code class="language-plaintext highlighter-rouge">@futile</code> for the correction.</p>

<h2 id="what-can-be-done-about-it">What can be done about it</h2>

<p>First, I started thinking about reducing the size of the generated code, e.g. by representing the checksum byte array in a more compact form. While this would likely help, I realized that as long as we generate code for all migrations next to each test, there will still be too much code.</p>

<p>Then I tried to patch <code class="language-plaintext highlighter-rouge">sqlx</code> to move the loading of the migrations to (test) runtime from compilation
time, to get rid of all the inlined migrations. This actually had the desired effect! The rebuild time went down to ~5s, and (at least in the case of <code class="language-plaintext highlighter-rouge">bors</code>), the <code class="language-plaintext highlighter-rouge">cargo expand</code> output was reduced to ~6 MiB, all while the execution time of the tests wasn’t affected in a measurable way. The change wasn’t even very complicated, all that’s needed is to generate code that will call the function to load migrations at runtime, rather than calling that function in the proc macro and then embedding the description of all migrations in the generated source code.</p>

<p>So essentially, you go from this (pseudo-code):</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">sqlx_proc_macro</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="n">TokenStream</span> <span class="p">{</span>
  <span class="k">let</span> <span class="n">migrations</span> <span class="o">=</span> <span class="nf">generate_migrations</span><span class="p">();</span>
  <span class="nd">quote!</span> <span class="p">{</span>
    <span class="n">Migrator</span> <span class="p">{</span>
        <span class="n">migrations</span><span class="p">:</span> #<span class="n">migrations</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>to this:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">sqlx_proc_macro</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="n">TokenStream</span> <span class="p">{</span>
  <span class="nd">quote!</span> <span class="p">{</span>
    <span class="n">Migrator</span> <span class="p">{</span>
        <span class="n">migrations</span><span class="p">:</span> <span class="p">::</span><span class="nn">sqlx</span><span class="p">::</span><span class="nf">generate_migrations</span><span class="p">()</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>However, loading the migrations at runtime might have some disadvantages, e.g. the tests would no
longer be self-contained.</p>

<p>I went to the <a href="https://github.com/transact-rs/sqlx/blob/main/CONTRIBUTING.md">sqlx Discord</a> server and asked if people have any suggestions. My proposal was that <code class="language-plaintext highlighter-rouge">sqlx</code> could either load the migrations at runtime (which I described above), or that there could be a shared variable with the migrations that all the tests would reference, to avoid the generated code bloat. Funnily enough, one person responded and told me that the second solution is already implemented (well, sort of). You can actually specify a path to a variable containing the migrations to apply in <code class="language-plaintext highlighter-rouge">#[sqlx::test]</code>:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// The macro generates the migrations, we store it in a single variable.</span>
<span class="k">const</span> <span class="n">MIGRATOR</span><span class="p">:</span> <span class="nn">sqlx</span><span class="p">::</span><span class="nn">migrate</span><span class="p">::</span><span class="n">Migrator</span> <span class="o">=</span> <span class="nn">sqlx</span><span class="p">::</span><span class="nd">migrate!</span><span class="p">();</span>

<span class="c1">// Each test just references the variable, instead of inlining all the</span>
<span class="c1">// migrations next to the test's source code.</span>
<span class="nd">#[sqlx::test(migrator</span> <span class="nd">=</span> <span class="s">"crate::MIGRATOR"</span><span class="nd">)]</span>
<span class="k">async</span> <span class="k">fn</span> <span class="nf">test_1</span><span class="p">(</span><span class="n">pool</span><span class="p">:</span> <span class="nn">sqlx</span><span class="p">::</span><span class="n">PgPool</span><span class="p">)</span> <span class="p">{}</span>

<span class="nd">#[sqlx::test(migrator</span> <span class="nd">=</span> <span class="s">"crate::MIGRATOR"</span><span class="nd">)]</span>
<span class="k">async</span> <span class="k">fn</span> <span class="nf">test_2</span><span class="p">(</span><span class="n">pool</span><span class="p">:</span> <span class="nn">sqlx</span><span class="p">::</span><span class="n">PgPool</span><span class="p">)</span> <span class="p">{}</span>
</code></pre></div></div>

<blockquote>
  <p>I wasn’t sure if <code class="language-plaintext highlighter-rouge">const</code> or <code class="language-plaintext highlighter-rouge">static</code> would be better here, and I didn’t measure any rebuild time differences. But <code class="language-plaintext highlighter-rouge">static</code> makes more sense to me, to ensure that the data exists just once in the final binary.</p>
</blockquote>

<p>This solution worked great for <code class="language-plaintext highlighter-rouge">bors</code>! After <a href="https://github.com/rust-lang/bors/pull/771">adding</a> the <code class="language-plaintext highlighter-rouge">migrator</code> argument to all <code class="language-plaintext highlighter-rouge">#[sqlx::test]</code> instances with a bit of <code class="language-plaintext highlighter-rouge">Find + Replace</code> magic, the rebuild time went down to ~5s.</p>

<p>While this works, what I don’t like about it is the annoying <code class="language-plaintext highlighter-rouge">migrator = "crate::MIGRATOR"</code> attribute
that I have to remember to add to all my tests to avoid the migration code bloat problem. I think that
it would be quite elegant to specify a default value for the <code class="language-plaintext highlighter-rouge">migrator</code> argument in the <code class="language-plaintext highlighter-rouge">sqlx.toml</code> configuration
file added in the <code class="language-plaintext highlighter-rouge">0.9</code> release of <code class="language-plaintext highlighter-rouge">sqlx</code>, so that the shared variable would be used by default, without having
to think about it.</p>

<p>I opened an <a href="https://github.com/transact-rs/sqlx/issues/4318">issue</a> that proposes this feature. Let’s see what do <code class="language-plaintext highlighter-rouge">sqlx</code> maintainers think,
although I would understand if they had some concerns about a feature like this.</p>

<p>In any case, even this manual solution was quite helpful, and it made my test rebuild times a bit faster.
There is still a lot of space for improvements, as 5s is still slow, though.</p>

<p>Maybe it would be useful to mention this “footgun” in the <code class="language-plaintext highlighter-rouge">#[sqlx::test]</code> <a href="https://docs.rs/sqlx/latest/sqlx/attr.test.html">documentation</a>. I suggested that <a href="https://github.com/transact-rs/sqlx/pull/4320">here</a>.</p>

<h2 id="conclusion">Conclusion</h2>

<p>I think that this post offers two main takeaways:</p>
<ul>
  <li>If you have a project with many <code class="language-plaintext highlighter-rouge">#[sqlx::test]</code> tests, and you suffer from long rebuild times,
try to use the <code class="language-plaintext highlighter-rouge">migrator = "..."</code> trick to see if that helps.</li>
  <li>If you have proc macros that generate a lot of code, measuring the <em>byte size</em> of your code using <code class="language-plaintext highlighter-rouge">cargo expand</code> can be a relatively good predictor of compilation time :)</li>
</ul>

<p>If you have further suggestions on how to optimize rebuild times when using <code class="language-plaintext highlighter-rouge">sqlx</code>, let me know on <a href="https://www.reddit.com/r/rust/comments/1ubuw7i/optimizing_sqlxtest_rebuild_time/">Reddit</a>.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:samply">
      <p>Using just <code class="language-plaintext highlighter-rouge">samply record</code>, nothing fancy this time. <a href="#fnref:samply" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:sqlx-db">
      <p>Such as connecting to a running database <em>during compilation</em>. I always get blank stares when I explain this to people new to Rust when I do Rust trainings. <a href="#fnref:sqlx-db" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:testing">
      <p>I have a talk and a WIP blog post about similar situations… testing is fun (for some definition of “fun”). <a href="#fnref:testing" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:windows">
      <p>At least if you’re not on Windows and have an <abbr title="Solid-state drive">SSD</abbr>. <a href="#fnref:windows" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="rust" /><summary type="html"><![CDATA[You might find this post especially useful if you have a project with many #[sqlx::test] tests.]]></summary></entry><entry><title type="html">How memory safety CVEs differ between Rust and C/C++</title><link href="https://kobzol.github.io/rust/2026/06/15/how-memory-safety-cves-differ-between-rust-and-c-cpp.html" rel="alternate" type="text/html" title="How memory safety CVEs differ between Rust and C/C++" /><published>2026-06-15T15:00:00+00:00</published><updated>2026-06-15T15:00:00+00:00</updated><id>https://kobzol.github.io/rust/2026/06/15/how-memory-safety-cves-differ-between-rust-and-c-cpp</id><content type="html" xml:base="https://kobzol.github.io/rust/2026/06/15/how-memory-safety-cves-differ-between-rust-and-c-cpp.html"><![CDATA[<p><abbr title="Common Vulnerabilities and Exposures">CVE</abbr> is a database used for categorizing and reporting security vulnerabilities in software. There
are various kinds of vulnerabilities that can be reported. Some of them are caused simply by bugs
in the program logic (like a recent <abbr title="Common Vulnerabilities and Exposures">CVE</abbr> reported in <a href="https://blog.rust-lang.org/2026/05/25/cve-2026-5222/">Cargo</a>), but some of the most nasty
ones are caused by memory unsafety, which can easily lead to exploits. In this post I want to focus
on the latter kind of CVEs, how they are reported, especially in libraries, and how it differs
between Rust and C or C++.</p>

<p>Because sometimes I see people online who compare the number of CVEs in Rust and C/C++ software, which
tends to be accompanied by claims about Rust not being <em>really</em> memory safe or not being worth
adopting when CVEs can still exist in it. And sometimes I also observe similar views when I teach Rust
to programmers who are used to programming in C or C++.</p>

<p>Now, anyone is, of course, free to do such comparisons, and make their own conclusions based on it. But I think that there is an important difference in how potential vulnerabilities related to memory safety are treated in Rust and C/C++, which might not be obvious at first, especially if you don’t know how Rust works. I’d like to explain that in this post.</p>

<p>But first, I should clarify that it is absolutely possible to cause memory unsafety bugs and undefined
behaviour in Rust. In the vast majority of cases<sup id="fnref:compiler-bug"><a href="#fn:compiler-bug" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>, the <code class="language-plaintext highlighter-rouge">unsafe</code> keyword is required for
this to happen, but anyone who claims that Rust programs cannot experience <abbr title="Undefined Behaviour">UB</abbr> at all is simply incorrect.
It is also perfectly possible to cause general <a href="https://rustsec.org/advisories/">vulnerabilities</a> (meaning those unrelated
to memory unsafety) in Rust. Forgetting to add a check that your admin dashboard is only accessible to admins can happen in any language, after all.</p>

<p>And yet, there is something very different between potential vulnerabilities in Rust and C or C++, which is
related to the core reason of why Rust <em>is</em> actually much more memory safe <a href="https://blog.google/security/rust-in-android-move-fast-fix-things">in practice</a> than C
or C++. I’ll try to demonstrate it on the <a href="https://curl.se/"><code class="language-plaintext highlighter-rouge">curl</code></a> networking library, which is written in C.</p>

<h2 id="potential-vulnerability-in-curl">Potential vulnerability in curl?</h2>

<p><code class="language-plaintext highlighter-rouge">(lib)curl</code> is one of the most used and well-maintained open source libraries in the world. Its primary
developer, <a href="https://daniel.haxx.se/">Daniel Stenberg</a>, is one of the most prolific open source maintainers of our time,
and together with many other people, he has been dilligently improving this library for the past 30 years.
Despite having to deal with a recent <a href="https://daniel.haxx.se/blog/2026/05/26/the-pressure/">avalanche</a> of CVEs found by LLMs,
he and his collaborators are doing a very good job of keeping <code class="language-plaintext highlighter-rouge">curl</code> safe from potential exploits and vulnerabilities,
and they take pride in <code class="language-plaintext highlighter-rouge">curl</code> being a very robust piece of software.</p>

<p>So, let’s take that to the test, shall we? I opened the <a href="https://curl.se/libcurl/c/allfuncs.html">documentation</a>
of <code class="language-plaintext highlighter-rouge">libcurl</code> and found the first function I saw that accepts an argument, <a href="https://curl.se/libcurl/c/curl_getenv.html"><code class="language-plaintext highlighter-rouge">curl_getenv</code></a>. This is supposed to be a simple function that provides a portable
abstraction for getting the value of an environment variable across different operating systems.
<code class="language-plaintext highlighter-rouge">curl</code> is supposed to be safe and robust, so surely this function doesn’t contain any <abbr title="Undefined Behaviour">UB</abbr> or memory unsafety, right?
So what about the following C program?</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;curl/curl.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">curl_getenv</span><span class="p">(</span><span class="nb">NULL</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This 5-line C program is as simple as it gets, it just calls the <code class="language-plaintext highlighter-rouge">curl_getenv</code> function with a
<code class="language-plaintext highlighter-rouge">NULL</code> pointer argument, and compiles without any warnings. And yet, when you execute it, you (might)
get a segfault, and thus a memory safety bug, and thus a potential vulnerability/exploit:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>gcc test.c <span class="nt">-otest</span> <span class="nt">-lcurl</span> <span class="nt">-Wall</span> <span class="nt">-Wextra</span>
<span class="nv">$ </span>./test
Segmentation fault <span class="o">(</span>core dumped<span class="o">)</span>
</code></pre></div></div>

<blockquote>
  <p>Of course, this program is artificially simple, but that’s kind of the point. In practice, situations
like this can (and do) easily happen in larger programs by accident all the time.</p>
</blockquote>

<p>Huh. So maybe <code class="language-plaintext highlighter-rouge">curl</code> isn’t so safe after all? Should I go and report this as a vulnerability in <code class="language-plaintext highlighter-rouge">curl</code>?!</p>

<p>No, of course not. That would be stupid. I know that, you know that. But how do we actually know it?
That’s the interesting part.</p>

<p>Consider a very similar program that would call the function like this: <code class="language-plaintext highlighter-rouge">curl_getenv("FOO")</code>. What if
that program would still segfault, and thus contain a potential vulnerability? I am sure that the <code class="language-plaintext highlighter-rouge">curl</code>
maintainers would like to know about that happening, and would consider it to be a pretty big issue if I reported it! At the same time, I’m sure that they would (rightfully) tell me off if I reported the first program as a vulnerability in <code class="language-plaintext highlighter-rouge">curl</code>. Yet those two programs differ only by so little.</p>

<p>So, what gives? Well, in practice, <abbr title="Undefined Behaviour">UB</abbr> like the one in my original example is said to be caused by “wrong usage”<sup id="fnref:skill-issue"><a href="#fn:skill-issue" class="footnote" rel="footnote" role="doc-noteref">2</a></sup>, and it is not considered to be an issue in the library or <abbr title="Application Programming Interface">API</abbr> that I am using, but in my (application)
code. This is done mostly for the following two reasons:</p>

<ul>
  <li>
    <p>In C, it is often not possible to specify the contract (invariants, preconditions,
postconditions, etc.) of APIs precisely<sup id="fnref:cpp"><a href="#fn:cpp" class="footnote" rel="footnote" role="doc-noteref">3</a></sup> due to its limited type system, and library authors often
don’t bother describing all possible kinds of wrong usage, as it would not be practical.</p>

    <p>Indeed, the <a href="https://curl.se/libcurl/c/curl_getenv.html">documentation</a> of <code class="language-plaintext highlighter-rouge">curl_getenv</code> does not say that calling it with <code class="language-plaintext highlighter-rouge">NULL</code> is forbidden and might lead to a segfault! The authors thus assume that you will use the library “correctly” (whatever that means), and if you don’t, then any caused vulnerabilities are your fault.</p>
  </li>
  <li>
    <p>The fact that it is so <a href="https://blog.habets.se/2026/05/Everything-in-C-is-undefined-behavior.html">simple</a> to trigger <abbr title="Undefined Behaviour">UB</abbr> by accident in C or C++ means that if we reported all
the <em>potential possibilities</em> of causing a vulnerability, such as the one in my example program, most C or C++
libraries would be flooded by millions of CVEs. It wouldn’t make sense to do that, because there would
be five different ways of potentially causing a vulnerability in every function call.</p>
  </li>
</ul>

<p>And thus, in C and C++, we usually do not consider similar situations to warrant a <abbr title="Common Vulnerabilities and Exposures">CVE</abbr> in the used library.
In other words, we create CVEs for specific <em>misuses</em> of a library, not for the existence of a library <abbr title="Application Programming Interface">API</abbr> that
<em>can be misused</em>.</p>

<h2 id="how-does-it-differ-in-rust">How does it differ in Rust?</h2>

<p>So, what is the crucial difference between how the situation above would be treated in C or C++ and
in Rust? <a href="https://github.com/hyperium/hyper"><code class="language-plaintext highlighter-rouge">hyper</code></a> is likely the most popular networking/HTTP library in Rust, spiritually
similar to <code class="language-plaintext highlighter-rouge">libcurl</code> in C. Imagine that <code class="language-plaintext highlighter-rouge">hyper</code> would have a similar simple function that would take
an argument, I would write a Rust program like this:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="nn">hyper</span><span class="p">::</span><span class="nf">foo</span><span class="p">(</span><span class="nb">None</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Then I’d hit <code class="language-plaintext highlighter-rouge">cargo run</code>, and the program would segfault. Would that be a <abbr title="Common Vulnerabilities and Exposures">CVE</abbr> in <code class="language-plaintext highlighter-rouge">hyper</code>? Yes, absolutely<sup id="fnref:compiler-bugs"><a href="#fn:compiler-bugs" class="footnote" rel="footnote" role="doc-noteref">4</a></sup>!</p>

<p>The program does not contain any <code class="language-plaintext highlighter-rouge">unsafe</code> blocks, so if a memory bug occurs, it had to be caused by the hyper library having a soundness bug.</p>

<p>The difference is that in Rust, when it is <em>in any conceivable way possible</em> to use a library such
that a memory bug occurs, without using <code class="language-plaintext highlighter-rouge">unsafe</code> in the user code, it is always a bug in the library,
not in the user code. That is why we call such APIs <em>unsound</em>, or say that they have a <em>soundness hole</em>, because there is
a way to use them wrong (w.r.t. memory safety) in safe Rust.</p>

<p>In other words, we create CVEs when it is <em>possible</em> to use a safe library <abbr title="Application Programming Interface">API</abbr> in a way that might cause a memory bug, even if we haven’t (yet) found any program in the wild that would actually do so. This means that some of the CVEs reported in Rust are much more “strict” than the ones in C or C++, which some people <a href="https://xcancel.com/pcwalton/status/1579643729836924929">don’t find “fair”</a>.</p>

<p>If we applied the same logic to C, then <code class="language-plaintext highlighter-rouge">curl_getenv</code> should be flagged as a <abbr title="Common Vulnerabilities and Exposures">CVE</abbr> in <code class="language-plaintext highlighter-rouge">curl</code>, because it is possible to use it in a way that causes a memory bug. But of course, this doesn’t really make sense in C, because there is no concept of safe and <code class="language-plaintext highlighter-rouge">unsafe</code> C (or rather, all C code is implicitly <code class="language-plaintext highlighter-rouge">unsafe</code>), which is why I said earlier that reporting this <abbr title="Common Vulnerabilities and Exposures">CVE</abbr> would be stupid.</p>

<p>The answer to the question “do I use this function correctly” (with respect to memory safety issues,
not logic bugs), which is often difficult to figure out in C or C++, is very simple in Rust:</p>
<ul>
  <li>If the called function is not marked with <code class="language-plaintext highlighter-rouge">unsafe</code>, then the answer is simply <code class="language-plaintext highlighter-rouge">YES</code>. It is impossible to use it incorrectly.</li>
  <li>If the called function is <code class="language-plaintext highlighter-rouge">unsafe</code>, I must mark the call with an <code class="language-plaintext highlighter-rouge">unsafe</code> block, which makes it immediately obvious during code review and in the codebase that this place is potentially dangerous. In this (usually very rare)
case, we revert back to the level of C or C++.</li>
</ul>

<p>The first part of the answer is what enables Rust’s memory safety to scale in practice. If you do not use <code class="language-plaintext highlighter-rouge">unsafe</code> in your code, which is not needed in the vast majority of situations (unless you are writing something like an operating system or a lock-free data structure), and don’t encounter a compiler bug, you <em>know</em> that any potential causes of memory unsafety are not your fault. If a library does not expose any <code class="language-plaintext highlighter-rouge">unsafe</code> interface, you simply <em>cannot</em> use it in a way that would cause memory bugs, unless the library uses <code class="language-plaintext highlighter-rouge">unsafe</code> internally and has a bug. But if that happens, the bug is fixed <em>within</em> that library, and all its users are then again automatically safe from memory bugs.</p>

<p>This is the difference between Rust and C or C++. Even though the developers of <code class="language-plaintext highlighter-rouge">curl</code> are doing awesome work to build a perfectly safe and robust C library, the millions of other C programs that use it can still very easily introduce memory unsafety just by “holding it wrong”, without the <code class="language-plaintext highlighter-rouge">curl</code> developers having any way of preventing that.</p>

<h2 id="conclusion">Conclusion</h2>

<p>I used <code class="language-plaintext highlighter-rouge">curl</code> as an example, but the same holds for pretty much any C or C++ library, including
the standard libraries of those two languages (and also other memory unsafe languages in general).
Originally I wanted to show more examples, but in the end I realized that they are all the same, so I stayed with the single <code class="language-plaintext highlighter-rouge">curl</code> function, because I think it demonstrates the difference well.</p>

<p>What I described in this blog post is not really ground-breaking in any way, and I think that it is kind of
universally understood by most people who know how Rust works. But I also don’t remember seeing any blog
post about this, and I was repeatedly explaining it to some people, so I wanted to write down
some thoughts about it, so that I can simply link to them the next time this debate happens again.</p>

<p>I hope that what I described above shows that comparing raw numbers of CVEs per line of code of Rust
and C or C++ is all kinds of misleading, and we should take that into account when comparing the
memory safety of Rust and other systems programming languages.</p>

<p>If you have a different idea on how CVEs (should) work in Rust or C/C++, let me know on <a href="https://www.reddit.com/r/rust/comments/1u6km19/how_memory_safety_cves_differ_between_rust_and_cc">Reddit</a>.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:compiler-bug">
      <p>Essentially only except for <a href="https://github.com/Speykious/cve-rs">compiler bugs</a>. <a href="#fnref:compiler-bug" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:skill-issue">
      <p>Someone might even say “skill issue” :laughing: <a href="#fnref:skill-issue" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:cpp">
      <p>In C++, the type system offers many more amenities for describing invariants, yet it still also offers many other ways to sneakily introduce <abbr title="Undefined Behaviour">UB</abbr> by accident, so the end result is similar. <a href="#fnref:cpp" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:compiler-bugs">
      <p>Again, barring any compiler bugs or hardware issues with my RAM module :) <a href="#fnref:compiler-bugs" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="rust" /><summary type="html"><![CDATA[CVE is a database used for categorizing and reporting security vulnerabilities in software. There are various kinds of vulnerabilities that can be reported. Some of them are caused simply by bugs in the program logic (like a recent CVE reported in Cargo), but some of the most nasty ones are caused by memory unsafety, which can easily lead to exploits. In this post I want to focus on the latter kind of CVEs, how they are reported, especially in libraries, and how it differs between Rust and C or C++.]]></summary></entry><entry><title type="html">1160 PRs to improve Rust in 2025</title><link href="https://kobzol.github.io/rust/rustc/2026/01/05/my-rust-contributions-in-2025.html" rel="alternate" type="text/html" title="1160 PRs to improve Rust in 2025" /><published>2026-01-05T14:00:00+00:00</published><updated>2026-01-05T14:00:00+00:00</updated><id>https://kobzol.github.io/rust/rustc/2026/01/05/my-rust-contributions-in-2025</id><content type="html" xml:base="https://kobzol.github.io/rust/rustc/2026/01/05/my-rust-contributions-in-2025.html"><![CDATA[<p>The end of the year is a time for reflection, so I thought that I should take a look back at the
previous year. And since my main job is currently contributing to open-source, what better way to do
that than enumerating all the Rust pull requests that I opened in 2025. In this post, I’ll share
stats about my Rust contributions from the past year and also some thoughts about open-source (Rust)
maintenance, as this topic is <a href="https://rustfoundation.org/media/announcing-the-rust-foundation-maintainers-fund/">highly relevant</a> right now.</p>

<p>First, some statistics. The GitHub GraphQL API claims that:</p>
<ul>
  <li>I opened <code class="language-plaintext highlighter-rouge">1497</code> PRs in 2025 (+98% from 755 opened PRs in 2024).
    <ul>
      <li><code class="language-plaintext highlighter-rouge">1160</code> (77.49%) out of those were relevant to the <code class="language-plaintext highlighter-rouge">rust-lang</code> organization or other upstream Rust work<sup id="fnref:ecosystem"><a href="#fn:ecosystem" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>. I only consider these in this post.</li>
      <li>I contributed to <code class="language-plaintext highlighter-rouge">50</code> different upstream-Rust-related repositories in the past year.</li>
    </ul>
  </li>
  <li>I reviewed <code class="language-plaintext highlighter-rouge">976</code> PRs in 2025 (+131% from 421 reviewed PRs in 2024).
    <ul>
      <li><code class="language-plaintext highlighter-rouge">753</code> (77.15%) out of those were relevant to upstream Rust<sup id="fnref:ratio"><a href="#fn:ratio" class="footnote" rel="footnote" role="doc-noteref">2</a></sup>.</li>
    </ul>
  </li>
</ul>

<blockquote>
  <p>I used <a href="https://gist.github.com/Kobzol/076b5c7096bfc675e73d52227d341d66">this script</a> to calculate these statistics.</p>
</blockquote>

<p>When I saw these numbers, I was quite surprised. It felt like too much, and I had to go double-check the script I used to compute it. But after examining the list of my opened PRs, and thinking a bit more about my Rust contributions, it started making sense. The high number of PRs that I open(ed) stems from the fact that the kind of work I usually do in Rust is quite maintenance-heavy<sup id="fnref:maintenance-blog"><a href="#fn:maintenance-blog" class="footnote" rel="footnote" role="doc-noteref">3</a></sup>. As a member of the Rust <a href="https://rust-lang.org/governance/teams/infra/">Infrastructure team</a>, I do many small and drive-by contributions to various repositories to fix CI, update configs or docs, set up some tooling, etc.
I definitely couldn’t open a thousand PRs in a year if they were all implementations of new
compiler/languages features or something like that<sup id="fnref:compiler-errors"><a href="#fn:compiler-errors" class="footnote" rel="footnote" role="doc-noteref">4</a></sup>.</p>

<p>A lot of that work can be quite unglamorous, like doing house chores. There are probably less than <code class="language-plaintext highlighter-rouge">50</code> pull requests from the past year that I could show to someone and say “here, I did something cool”. The rest is mostly work that “had to be done”. And there’s a lot of that work! Even though <code class="language-plaintext highlighter-rouge">1160</code> PRs might sound like a lot, it is just a drop in the ocean in the
upstream Rust world. In 2025, just the <a href="https://github.com/rust-lang/rust"><code class="language-plaintext highlighter-rouge">rust-lang/rust</code></a> repository
received <code class="language-plaintext highlighter-rouge">10483</code> (!) pull requests<sup id="fnref:first-pr-2025"><a href="#fn:first-pr-2025" class="footnote" rel="footnote" role="doc-noteref">5</a></sup>. I personally opened only <code class="language-plaintext highlighter-rouge">307</code> out of those PRs. That shows just
how much work goes into maintaining Rust (and note that this is just a single repository, although the most important and extensive one that we have). I try to help this effort by often (but not always) prioritizing unblocking other Rust Project contributors, rather than pushing my own work forward, as that tends to have a larger effect down the line.</p>

<p>It should also be noted that opening pull requests is just a <a href="https://youtu.be/6n2eDcRjSsk?t=1046">fraction</a> of the work that a typical open-source contributor/maintainer does. There are certainly many other activities that <em>I</em> do, such as code reviews, writing and reading design docs, discussing various ideas with other people on Zulip and GitHub, reading and writing blog posts, triaging issues, governing and making decisions, listening to podcasts to find inspiration, attending
  and talking at conferences, watching recordings of conference talks that I didn’t see live or
  thinking about compiler optimizations while walking my dog :) Open-source is about communication as
  much as it is about writing code (and even that is a form of communication). So quantifying work based
  on just the number of opened pull requests does not paint the whole picture.</p>

<p>It can be quite hard to demonstrate the value of work like this, especially to companies. That is something that we are trying to improve with the <a href="https://rustfoundation.org/media/announcing-the-rust-foundation-maintainers-fund/">Rust Foundation Maintainer Fund</a> (and other similar efforts). Maintaining Rust so that it keeps working as well today as it did yesterday is a lot of work. And moving it forward by adding new features is even harder<sup id="fnref:100-engineers"><a href="#fn:100-engineers" class="footnote" rel="footnote" role="doc-noteref">6</a></sup>. That is why I think that it is crucial to support contributors that maintain and develop Rust. Without them, Rust could not thrive and evolve.</p>

<p>While I am currently one of the lucky people who are funded for their upstream Rust work<sup id="fnref:fulltime"><a href="#fn:fulltime" class="footnote" rel="footnote" role="doc-noteref">7</a></sup>, many
Rust contributors do not have access to stable funding. While the Rust Foundation (together with the Rust Project)
is currently trying to figure out how we can build a sustainable mechanism for funding full-time Rust maintainers,
there are also more targeted ways to help fund individual contributors “in the small”. If you are feeling generous,
consider <a href="https://rust-lang.org/funding/">sponsoring individuals</a> who make Rust better on a daily basis.</p>

<h2 id="summary">Summary</h2>

<p>Here is a short summary of a few things from 2025 that stand out (in no particular order). In 2025, I:</p>
<ul>
  <li>Was elected to the <a href="https://rust-lang.org/governance/teams/leadership-council/">Leadership Council</a> and invited to the <a href="https://blog.rust-lang.org/inside-rust/2025/05/30/compiler-team-new-members/">compiler team</a>.</li>
  <li>Oversaw the Rust GSoC program again, this year with 19 (!) projects. I think that we did <a href="https://blog.rust-lang.org/2025/11/18/gsoc-2025-results/">pretty well</a>!</li>
  <li>Attended <a href="https://2025.rustweek.org/">RustWeek</a> and the Rust <a href="https://blog.rust-lang.org/inside-rust/2025/09/30/all-hands-2026/">All Hands</a>, quite possibly the best conference/event I have ever been to.
    <ul>
      <li>Inspired by RustWeek, I wrote a <a href="/rust/rustc/2025/05/16/evolution-of-rustc-errors.html">blog post</a> that shows the evolution Rust compiler error messages. I’m quite proud of that one, if I do say so myself :)</li>
    </ul>
  </li>
  <li>Helped move the initiative to use the <a href="https://blog.rust-lang.org/2025/09/01/rust-lld-on-1.90.0-stable/">LLD linker by default on Linux</a> over the finish line, finally getting the great work of <a href="https://github.com/lqd">@lqd</a> and others to a large fraction of Rust users.</li>
  <li>Made a lot of progress on improving the <a href="https://github.com/rust-lang/rustc-perf">Rust Compiler Benchmark Suite</a> together with <a href="https://github.com/Jamesbarford">James Barford</a> from ARM, as a part of one of Rust’s <a href="https://rust-lang.github.io/rust-project-goals/2025h2/rustc-perf-improvements.html">Project Goals</a>. This reduced the time needed to wait for compiler benchmarks on our CI and opened the doors to benchmarking on more hardware architectures (e.g. ARM).</li>
  <li>Spent an unhealthy amount of time peeking into the abyss of <a href="https://rustc-dev-guide.rust-lang.org/building/bootstrapping/what-bootstrapping-does.html">bootstrap</a> (the Rust toolchain build system),
refactoring it and making it at least somewhat consistent again after the <a href="https://blog.rust-lang.org/inside-rust/2025/05/29/redesigning-the-initial-bootstrap-sequence/">Great stage0 redesign</a> has landed. Much more work remains to be done there, though.</li>
  <li>Helped prepare several surveys (<a href="https://blog.rust-lang.org/2025/09/10/rust-compiler-performance-survey-2025-results/">Rust Compiler Performance survey</a>, <a href="https://github.com/rust-lang/surveys/pull/350">Rust Contributor survey</a>, <a href="https://github.com/rust-lang/surveys/pull/338">Leadership Council survey</a>, <a href="https://blog.rust-lang.org/inside-rust/2025/09/22/variadic-generics-micro-survey/">Variadic Generics survey</a>, <a href="https://github.com/rust-lang/surveys/pull/384">Safety-critical survey</a> and of course the <a href="https://blog.rust-lang.org/2025/11/17/launching-the-2025-state-of-rust-survey/">State of Rust survey</a>).</li>
  <li>Made a lot of little improvements and reduced tech debt somewhat in our infrastructure and CI. By creating a <a href="https://github.com/rust-lang/josh-sync">tool</a> to make git subtree synces easier and more robust, helping move our CI off the <code class="language-plaintext highlighter-rouge">rust-lang-ci/rust</code> repository (finally!), syncing crates.io <a href="https://github.com/rust-lang/team/pull/2151">crate owners</a> in the <a href="https://github.com/rust-lang/team"><code class="language-plaintext highlighter-rouge">team</code></a> repo, moving the Rust web to a <a href="https://github.com/rust-lang/www.rust-lang.org/pull/2174">static website</a> and adding some <a href="https://rust-lang.org/governance/people/">cool</a> <a href="https://rust-lang.org/governance/people/Kobzol/">things</a> to it, and implementing many improvements to our bots (mostly <a href="https://github.com/rust-lang/triagebot">triagebot</a> and <a href="https://github.com/rust-lang/bors">bors</a>).</li>
  <li>Was able to make small progress on compiler performance. In addition to the already mentioned LLD linker stabilization, Compiler Performance survey and rustc-perf improvements, I made <a href="https://github.com/rust-lang/cargo/pull/15923">some</a> <a href="https://github.com/rust-lang/cargo/pull/15780">improvements</a> to visualizing what takes time in Rust builds, bootstrapped an official <a href="https://github.com/rust-lang/cargo/pull/15924">Cargo build performance guide</a>, made a few <a href="https://github.com/rust-lang/rust/pull/145408">drive-by</a> <a href="https://github.com/rust-lang/rust/pull/145358">performance</a> improvements, and restarted important work on <a href="https://github.com/rust-lang/rust/pull/145354">proc-macro caching</a><sup id="fnref:proc-macro-stuck"><a href="#fn:proc-macro-stuck" class="footnote" rel="footnote" role="doc-noteref">8</a></sup>. I also had some big ideas for building tooling for better diagnostics of compiler performance and build times, but sadly did not have time to work on these since ~June. Lots of other things got in the way!</li>
  <li>Made Rustup <em>start</em> ~3x faster by <a href="https://github.com/rust-lang/rustup/pull/4350">adding 6 lines</a> to it. Kinda proud of that one :laughing:.</li>
  <li>Made Rust Analyzer ~20% faster by implementing <a href="https://github.com/rust-lang/rust-analyzer/pull/19582">PGO</a> for it and Clippy ~5% faster by switching it to use <a href="https://github.com/rust-lang/rust/pull/142286">Jemalloc</a>.</li>
  <li>Implemented <a href="https://github.com/rust-lang/cargo/pull/15923">visualization of linking times</a> in <code class="language-plaintext highlighter-rouge">cargo build --timings</code> output (not stabilized yet).</li>
  <li>Wrote my first <a href="https://github.com/rust-lang/rfcs/pull/3809">RFC</a> (for the <code class="language-plaintext highlighter-rouge">#[derive(From)]</code> feature), got it accepted :tada: and <a href="https://github.com/rust-lang/rust/pull/144922">implemented</a> it. Although it will need to be landed over an edition due to some backwards incompatible name resolution issues, so it might take some time before it gets stabilized.</li>
  <li>Participated in various funding discussions, helped prepare a <a href="https://github.com/rust-lang/funding/pull/1">design document</a> for the <a href="https://rustfoundation.org/media/announcing-the-rust-foundation-maintainers-fund/">Rust Foundation Maintainer Fund</a> and created a dedicated <a href="https://rust-lang.org/funding/">Funding</a> page on the Rust website, to make it easier to sponsor Rust Project contributors.</li>
  <li>Had a talk about how Rust does open-source governance/maintenance at a local tech conference, gave some Rust talks at a few Rust <a href="https://www.meetup.com/rust-prague/events/311846118/">meetups</a>, led two Rust trainings and taught Rust at my university <a href="https://github.com/Kobzol/rust-course-fei">again</a>.</li>
</ul>

<p>It goes without saying that everything mentioned above was team work that couldn’t be done without the help of the awesome <a href="https://rust-lang.org/governance/people/">people</a> who <em>are</em> Rust :heart:.</p>

<h2 id="my-2025-rust-prs">My 2025 Rust PRs</h2>

<p>And here is the promised list of the Rust PRs that I opened in 2025. They are categorized by repository (repos are ordered by PR count in descending order) and within each repo they are ordered by creation time in ascending order. Unless otherwise noted, the pull request was merged, although I also kept the opened and closed ones, just to show the open/close/merge ratio.</p>

<p><a href="#a-few-more-thoughts-on-my-2025-contributions">Below the list</a> you can find a few more thoughts on my 2025 Rust contributions and the conclusion.</p>

<!-- pr-list -->

<h3 id="rust-langrust-307-prs">rust-lang/rust (307 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/rust/pull/135001">#135001</a>: Allow using self-contained LLD in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/135127">#135127</a>: rustc-dev-guide subtree update</li>
  <li><a href="https://github.com/rust-lang/rust/pull/135164">#135164</a>: Add test for checking used glibc symbols</li>
  <li><a href="https://github.com/rust-lang/rust/pull/135303">#135303</a>: CI: fix name of jobs</li>
  <li><a href="https://github.com/rust-lang/rust/pull/135478">#135478</a>: Run clippy for rustc_codegen_gcc on CI</li>
  <li><a href="https://github.com/rust-lang/rust/pull/135638">#135638</a>: Make it possible to build GCC on CI</li>
  <li><a href="https://github.com/rust-lang/rust/pull/135658">#135658</a>: Do not include GCC source code in source tarballs</li>
  <li><a href="https://github.com/rust-lang/rust/pull/135810">#135810</a>: Add Kobzol on vacation</li>
  <li><a href="https://github.com/rust-lang/rust/pull/135829">#135829</a>: Rustc dev guide subtree update</li>
  <li><a href="https://github.com/rust-lang/rust/pull/135832">#135832</a>: Apply LTO config to rustdoc</li>
  <li><a href="https://github.com/rust-lang/rust/pull/135950">#135950</a>: Tidy Python improvements</li>
  <li><a href="https://github.com/rust-lang/rust/pull/136530">#136530</a>: Implement <code class="language-plaintext highlighter-rouge">x perf</code> directly in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/136586">#136586</a>: Only apply LTO to rustdoc at stage 2</li>
  <li><a href="https://github.com/rust-lang/rust/pull/136864">#136864</a>: Rewrite the <code class="language-plaintext highlighter-rouge">ci.py</code> script in Rust</li>
  <li><a href="https://github.com/rust-lang/rust/pull/136911">#136911</a>: Add documentation URL to selected jobs</li>
  <li><a href="https://github.com/rust-lang/rust/pull/136913">#136913</a>: Put kobzol back on review rotation</li>
  <li><a href="https://github.com/rust-lang/rust/pull/136921">#136921</a>: Build GCC on CI</li>
  <li><a href="https://github.com/rust-lang/rust/pull/136924">#136924</a>: Add profiling of bootstrap commands using Chrome events</li>
  <li><a href="https://github.com/rust-lang/rust/pull/136941">#136941</a>: Move <code class="language-plaintext highlighter-rouge">llvm.ccache</code> to <code class="language-plaintext highlighter-rouge">build.ccache</code></li>
  <li><a href="https://github.com/rust-lang/rust/pull/136942">#136942</a>: Use ccache for stage0 tool builds</li>
  <li><a href="https://github.com/rust-lang/rust/pull/136977">#136977</a>: Upload Datadog metrics with citool</li>
  <li><a href="https://github.com/rust-lang/rust/pull/137023">#137023</a>: Bump sccache in CI to 0.9.1</li>
  <li><a href="https://github.com/rust-lang/rust/pull/137044">#137044</a>: [CRATER] Detect presence of .ctors/.dtors in linked objects (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/137070">#137070</a>: Do not generate invalid links in job summaries</li>
  <li><a href="https://github.com/rust-lang/rust/pull/137077">#137077</a>: Postprocess bootstrap metrics into GitHub job summary</li>
  <li><a href="https://github.com/rust-lang/rust/pull/137189">#137189</a>: Update host LLVM to 20.1 on CI</li>
  <li><a href="https://github.com/rust-lang/rust/pull/137340">#137340</a>: Add a notice about missing GCC sources into source tarballs</li>
  <li><a href="https://github.com/rust-lang/rust/pull/137362">#137362</a>: Add build step log for <code class="language-plaintext highlighter-rouge">run-make-support</code></li>
  <li><a href="https://github.com/rust-lang/rust/pull/137373">#137373</a>: Compile run-make-support and run-make tests with the bootstrap compiler</li>
  <li><a href="https://github.com/rust-lang/rust/pull/137535">#137535</a>: Introduce <code class="language-plaintext highlighter-rouge">-Zembed-metadata</code> to allow omitting full metadata from rlibs and dylibs</li>
  <li><a href="https://github.com/rust-lang/rust/pull/137610">#137610</a>: Revert “Auto merge of #135726 - jdonszelmann:attr-parsing, r=oli-obk” (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/137612">#137612</a>: Update bootstrap to edition 2024</li>
  <li><a href="https://github.com/rust-lang/rust/pull/137660">#137660</a>: Update gcc submodule</li>
  <li><a href="https://github.com/rust-lang/rust/pull/137665">#137665</a>: Update sccache to 0.10.0</li>
  <li><a href="https://github.com/rust-lang/rust/pull/137667">#137667</a>: Add <code class="language-plaintext highlighter-rouge">dist::Gcc</code> build step</li>
  <li><a href="https://github.com/rust-lang/rust/pull/137683">#137683</a>: Add a tidy check for GCC submodule version</li>
  <li><a href="https://github.com/rust-lang/rust/pull/137718">#137718</a>: Use original command for showing sccache stats</li>
  <li><a href="https://github.com/rust-lang/rust/pull/137732">#137732</a>: Use Windows 2019 for 32-bit MSVC CI jobs (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/137746">#137746</a>: [do not merge] Another attempt to fix 32-bit MSVC CI (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/137749">#137749</a>: Fix 32-bit MSVC CI</li>
  <li><a href="https://github.com/rust-lang/rust/pull/137926">#137926</a>: Add a test for <code class="language-plaintext highlighter-rouge">-znostart-stop-gc</code> usage with LLD</li>
  <li><a href="https://github.com/rust-lang/rust/pull/137945">#137945</a>: Skip Rust for Linux in CI temporarily</li>
  <li><a href="https://github.com/rust-lang/rust/pull/137947">#137947</a>: Do not install rustup on Rust for Linux job</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138013">#138013</a>: Add post-merge analysis CI workflow</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138051">#138051</a>: Add support for downloading GCC from CI</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138053">#138053</a>: Increase the max. custom try jobs requested to <code class="language-plaintext highlighter-rouge">20</code></li>
  <li><a href="https://github.com/rust-lang/rust/pull/138066">#138066</a>: [WIP] Enable automatic cross-compilation in run-make tests (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138223">#138223</a>: Fix post-merge workflow</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138232">#138232</a>: Reduce verbosity of GCC build log</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138268">#138268</a>: Handle empty test suites in GitHub job summary report</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138307">#138307</a>: Allow specifying glob patterns for try jobs</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138348">#138348</a>: Rollup of 11 pull requests (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138350">#138350</a>: Rollup of 10 pull requests</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138395">#138395</a>: Download GCC from CI on test builders</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138396">#138396</a>: Enable metrics and verbose tests in PR CI</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138451">#138451</a>: Build GCC on CI with GCC, not Clang</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138452">#138452</a>: Remove <code class="language-plaintext highlighter-rouge">RUN_CHECK_WITH_PARALLEL_QUERIES</code></li>
  <li><a href="https://github.com/rust-lang/rust/pull/138454">#138454</a>: Improve post-merge workflow</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138487">#138487</a>: Pass <code class="language-plaintext highlighter-rouge">CI_JOB_DOC_URL</code> to Docker</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138507">#138507</a>: Mirror NetBSD sources</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138531">#138531</a>: Store test diffs in job summaries and improve analysis formatting</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138533">#138533</a>: Only use <code class="language-plaintext highlighter-rouge">DIST_TRY_BUILD</code> for try jobs that were not selected explicitly</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138591">#138591</a>: Refactor git change detection in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138597">#138597</a>: [do not merge] beta test for git change detection (#138591) (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138645">#138645</a>: [do not merge] Preparation for LLD stabilization (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138655">#138655</a>: rustc-dev-guide sync</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138656">#138656</a>: Remove double nesting in post-merge workflow</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138704">#138704</a>: Simplify CI LLVM checks in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138706">#138706</a>: Improve bootstrap git modified path handling</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138709">#138709</a>: Update GCC submodule</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138792">#138792</a>: Rollup of 10 pull requests (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138834">#138834</a>: Group test diffs by stage in post-merge analysis</li>
  <li><a href="https://github.com/rust-lang/rust/pull/138930">#138930</a>: Add bootstrap step diff to CI job analysis</li>
  <li><a href="https://github.com/rust-lang/rust/pull/139015">#139015</a>: Remove unneeded LLVM CI test assertions</li>
  <li><a href="https://github.com/rust-lang/rust/pull/139016">#139016</a>: Add job duration changes to post-merge analysis report</li>
  <li><a href="https://github.com/rust-lang/rust/pull/139130">#139130</a>: Revert “Auto merge of #129827 - bvanjoi:less-decoding, r=petrochenkov”</li>
  <li><a href="https://github.com/rust-lang/rust/pull/139322">#139322</a>: Add helper function for checking LLD usage to <code class="language-plaintext highlighter-rouge">run-make-support</code></li>
  <li><a href="https://github.com/rust-lang/rust/pull/139378">#139378</a>: Use target-agnostic LLD flags in bootstrap for <code class="language-plaintext highlighter-rouge">use-lld</code></li>
  <li><a href="https://github.com/rust-lang/rust/pull/139473">#139473</a>: Rollup of 5 pull requests</li>
  <li><a href="https://github.com/rust-lang/rust/pull/139481">#139481</a>: Add job summary links to post-merge report</li>
  <li><a href="https://github.com/rust-lang/rust/pull/139588">#139588</a>: Use LTO to optimize Rust tools (cargo, miri, rustfmt, clippy, Rust Analyzer)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/139597">#139597</a>: Do not run per-module late lints if they can be all skipped</li>
  <li><a href="https://github.com/rust-lang/rust/pull/139648">#139648</a>: Optimize Rust Analyzer with BOLT (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/139678">#139678</a>: Gate advanced features of <code class="language-plaintext highlighter-rouge">citool</code> to reduce compilation time (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/139691">#139691</a>: Document that <code class="language-plaintext highlighter-rouge">opt-dist</code> requires metrics to be enabled</li>
  <li><a href="https://github.com/rust-lang/rust/pull/139707">#139707</a>: Fix comment in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/139807">#139807</a>: Improve wording of post-merge report</li>
  <li><a href="https://github.com/rust-lang/rust/pull/139819">#139819</a>: Use <code class="language-plaintext highlighter-rouge">rust-cache</code> to speed-up <code class="language-plaintext highlighter-rouge">citool</code> compilation</li>
  <li><a href="https://github.com/rust-lang/rust/pull/139853">#139853</a>: Disable combining LLD with external llvm-config</li>
  <li><a href="https://github.com/rust-lang/rust/pull/139894">#139894</a>: Fix <code class="language-plaintext highlighter-rouge">opt-dist</code> CLI flag and make it work without LLD</li>
  <li><a href="https://github.com/rust-lang/rust/pull/139978">#139978</a>: Add citool command for generating a test dashboard</li>
  <li><a href="https://github.com/rust-lang/rust/pull/140063">#140063</a>: Remove stray newline from post-merge report</li>
  <li><a href="https://github.com/rust-lang/rust/pull/140191">#140191</a>: Remove git repository from git config</li>
  <li><a href="https://github.com/rust-lang/rust/pull/140394">#140394</a>: Make bootstrap git tests more self-contained</li>
  <li><a href="https://github.com/rust-lang/rust/pull/140703">#140703</a>: Handle PR not found in post-merge workflow</li>
  <li><a href="https://github.com/rust-lang/rust/pull/140786">#140786</a>: Do not deny warnings in “fast” try builds</li>
  <li><a href="https://github.com/rust-lang/rust/pull/140889">#140889</a>: WIP: test PR for triagebot (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/140901">#140901</a>: Fix download of GCC from CI on non-nightly channels</li>
  <li><a href="https://github.com/rust-lang/rust/pull/141280">#141280</a>: Use Docker cache from the current repository</li>
  <li><a href="https://github.com/rust-lang/rust/pull/141323">#141323</a>: Add bors environment to CI</li>
  <li><a href="https://github.com/rust-lang/rust/pull/141335">#141335</a>: [do not merge] Triagebot test (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/141384">#141384</a>: Enable review queue tracking</li>
  <li><a href="https://github.com/rust-lang/rust/pull/141388">#141388</a>: Move <code class="language-plaintext highlighter-rouge">dist-x86_64-linux</code> CI job to GitHub temporarily</li>
  <li><a href="https://github.com/rust-lang/rust/pull/141423">#141423</a>: [do not merge] Test PR for moving off rust-lang-ci (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/141458">#141458</a>: [do not merge] Try build test for beta (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/141459">#141459</a>: [do not merge] Try build for stable (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/141634">#141634</a>: Fix CI for unrolled builds on the <code class="language-plaintext highlighter-rouge">try-perf</code> branch</li>
  <li><a href="https://github.com/rust-lang/rust/pull/141678">#141678</a>: Revert “increase perf of charsearcher for single ascii characters”</li>
  <li><a href="https://github.com/rust-lang/rust/pull/141723">#141723</a>: Provide secrets to try builds with new bors</li>
  <li><a href="https://github.com/rust-lang/rust/pull/141771">#141771</a>: Increase timeout for new bors try builds</li>
  <li><a href="https://github.com/rust-lang/rust/pull/141777">#141777</a>: Do not run PGO/BOLT in x64 Linux alt builds</li>
  <li><a href="https://github.com/rust-lang/rust/pull/141897">#141897</a>: Fix citool tests when executed locally</li>
  <li><a href="https://github.com/rust-lang/rust/pull/141899">#141899</a>: Turn <code class="language-plaintext highlighter-rouge">stdarch</code> into a Josh subtree</li>
  <li><a href="https://github.com/rust-lang/rust/pull/141910">#141910</a>: Fix <code class="language-plaintext highlighter-rouge">create-docs-artifacts.sh</code> with new bors</li>
  <li><a href="https://github.com/rust-lang/rust/pull/141912">#141912</a>: Rollup of 5 pull requests</li>
  <li><a href="https://github.com/rust-lang/rust/pull/141948">#141948</a>: Allow PR builds to read sccache entries from S3 (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142076">#142076</a>: Check documentation of bootstrap in PR CI</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142123">#142123</a>: Implement initial support for timing sections (<code class="language-plaintext highlighter-rouge">--json=timings</code>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142199">#142199</a>: Do not free disk space in the <code class="language-plaintext highlighter-rouge">mingw-check-tidy</code> job</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142210">#142210</a>: Run <code class="language-plaintext highlighter-rouge">mingw-check-tidy</code> on auto builds</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142211">#142211</a>: Do not checkout GCC submodule for the tidy job</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142231">#142231</a>: Run <code class="language-plaintext highlighter-rouge">calculate_matrix</code> job on <code class="language-plaintext highlighter-rouge">master</code> to cache citool builds</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142235">#142235</a>: Build rustc with assertions in <code class="language-plaintext highlighter-rouge">dist-alt</code> jobs</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142241">#142241</a>: Disable download-rustc on CI</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142282">#142282</a>: Only run <code class="language-plaintext highlighter-rouge">citool</code> tests on the <code class="language-plaintext highlighter-rouge">auto</code> branch</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142286">#142286</a>: Use jemalloc for Clippy</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142303">#142303</a>: Assorted bootstrap cleanups (step 1)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142344">#142344</a>: Revert “add <code class="language-plaintext highlighter-rouge">Cargo.lock</code> to CI-rustc allowed list for non-CI env”</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142357">#142357</a>: Simplify LLVM bitcode linker in bootstrap and add tests for it</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142364">#142364</a>: Do not warn on <code class="language-plaintext highlighter-rouge">rust.incremental</code> when using download CI rustc</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142374">#142374</a>: Fix missing newline trim in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142395">#142395</a>: Cache all crates for citool (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142407">#142407</a>: Remove bootstrap adhoc group</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142416">#142416</a>: Assorted bootstrap cleanups (step 2)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142431">#142431</a>: Add initial version of snapshot tests to bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142434">#142434</a>:  Pre-install JS dependencies in tidy Dockerfile</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142566">#142566</a>: Fix <code class="language-plaintext highlighter-rouge">-nopt</code> CI jobs</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142574">#142574</a>: Rollup of 12 pull requests</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142581">#142581</a>: Enforce in bootstrap that build must have stage at least 1</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142589">#142589</a>: Rollup of 8 pull requests</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142624">#142624</a>: Actually take <code class="language-plaintext highlighter-rouge">--build</code> into account in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142627">#142627</a>: Add <code class="language-plaintext highlighter-rouge">StepMetadata</code> to describe steps</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142629">#142629</a>: Add config builder for bootstrap tests</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142665">#142665</a>: Rollup of 12 pull requests (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142672">#142672</a>: Clarify bootstrap tools description</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142679">#142679</a>: Rollup of 12 pull requests (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142685">#142685</a>: Rollup of 11 pull requests</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142692">#142692</a>: Assorted bootstrap cleanups (step 3)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142703">#142703</a>: Rollup of 3 pull requests (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142719">#142719</a>: Rollup of 6 pull requests (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142781">#142781</a>: Rollup of 11 pull requests (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142784">#142784</a>: Add codegen timing section</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142795">#142795</a>: Rollup of 10 pull requests</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142912">#142912</a>: [perf] Try to skip some early lints with <code class="language-plaintext highlighter-rouge">--cap-lints</code> (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142963">#142963</a>: Skip unnecessary components in x64 try builds</li>
  <li><a href="https://github.com/rust-lang/rust/pull/142978">#142978</a>: Add new self-profiling event to cheaply aggregate query cache hit counts</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143041">#143041</a>: Remove cache for citool</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143048">#143048</a>: Enforce in bootstrap that check must have stage at least 1</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143175">#143175</a>: Make combining LLD with external LLVM config a hard error</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143255">#143255</a>: Do not enable LLD by default in the dist profile</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143285">#143285</a>: Add <code class="language-plaintext highlighter-rouge">stdarch</code> bootstrap smoke test (<span style="color: green;">open</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143316">#143316</a>: Add bootstrap check snapshot tests</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143325">#143325</a>: Use non-global interner in <code class="language-plaintext highlighter-rouge">test_string_interning</code> in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143412">#143412</a>: Move <code class="language-plaintext highlighter-rouge">std_detect</code> into stdlib</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143420">#143420</a>: rustc-dev-guide subtree update</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143421">#143421</a>: [do not merge] rustc-dev-guide subtree update (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143452">#143452</a>: Fix CLI completion check in <code class="language-plaintext highlighter-rouge">tidy</code></li>
  <li><a href="https://github.com/rust-lang/rust/pull/143581">#143581</a>: Implement <code class="language-plaintext highlighter-rouge">ToolTarget</code> and port <code class="language-plaintext highlighter-rouge">RemoteTestServer</code> and <code class="language-plaintext highlighter-rouge">WasmComponentLd</code> to it (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143586">#143586</a>: Fix wrong cache event query key</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143615">#143615</a>: Fix handling of no_std targets in <code class="language-plaintext highlighter-rouge">doc::Std</code> step</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143639">#143639</a>: [do not merge] stdarch subtree update (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143641">#143641</a>: Add <code class="language-plaintext highlighter-rouge">ToolTarget</code> to bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143642">#143642</a>: stdarch subtree update</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143644">#143644</a>: Add triagebot stdarch mention ping</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143676">#143676</a>: Rollup of 6 pull requests (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143707">#143707</a>: Fix <code class="language-plaintext highlighter-rouge">--skip-std-check-if-no-download-rustc</code></li>
  <li><a href="https://github.com/rust-lang/rust/pull/143816">#143816</a>: Implement <code class="language-plaintext highlighter-rouge">check</code> for compiletest and RA using tool macro</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143817">#143817</a>: Access <code class="language-plaintext highlighter-rouge">wasi_sdk_path</code> instead of reading environment variable in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143887">#143887</a>: Run bootstrap tests sooner in the <code class="language-plaintext highlighter-rouge">x test</code> pipeline</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143919">#143919</a>: Rollup of 10 pull requests</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143946">#143946</a>: Rollup of 14 pull requests (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/143947">#143947</a>: Rollup of 7 pull requests (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/144053">#144053</a>: Remove install Rust script from CI</li>
  <li><a href="https://github.com/rust-lang/rust/pull/144056">#144056</a>: Copy GCC sources into the build directory even outside CI</li>
  <li><a href="https://github.com/rust-lang/rust/pull/144085">#144085</a>: Rollup of 10 pull requests (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/144176">#144176</a>: Add approval blocking labels for new bors</li>
  <li><a href="https://github.com/rust-lang/rust/pull/144177">#144177</a>: Rollup of 8 pull requests (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/144193">#144193</a>: Suggest adding <code class="language-plaintext highlighter-rouge">Fn</code> bound when calling a generic parameter (<span style="color: green;">open</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/144222">#144222</a>: stdarch subtree update</li>
  <li><a href="https://github.com/rust-lang/rust/pull/144252">#144252</a>: Do not copy .rmeta files into the sysroot of the build compiler during check of rustc/std</li>
  <li><a href="https://github.com/rust-lang/rust/pull/144303">#144303</a>: Consolidate staging for <code class="language-plaintext highlighter-rouge">rustc_private</code> tools</li>
  <li><a href="https://github.com/rust-lang/rust/pull/144437">#144437</a>: Rollup of 10 pull requests (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/144462">#144462</a>: Allow pretty printing paths with <code class="language-plaintext highlighter-rouge">-Zself-profile-events=args</code></li>
  <li><a href="https://github.com/rust-lang/rust/pull/144464">#144464</a>: Only run bootstrap tests in <code class="language-plaintext highlighter-rouge">x test</code> on CI</li>
  <li><a href="https://github.com/rust-lang/rust/pull/144639">#144639</a>: Update rustc-perf submodule</li>
  <li><a href="https://github.com/rust-lang/rust/pull/144730">#144730</a>: Create a typed wrapper for codegen backends in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/144779">#144779</a>: Implement debugging output of the bootstrap Step graph into a DOT file</li>
  <li><a href="https://github.com/rust-lang/rust/pull/144787">#144787</a>: Refactor codegen backends in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/144899">#144899</a>: Print CGU reuse statistics in <code class="language-plaintext highlighter-rouge">-Zprint-mono-items</code></li>
  <li><a href="https://github.com/rust-lang/rust/pull/144906">#144906</a>: Require approval from t-infra instead of t-release on tier bumps</li>
  <li><a href="https://github.com/rust-lang/rust/pull/144922">#144922</a>: Implement <code class="language-plaintext highlighter-rouge">#[derive(From)]</code></li>
  <li><a href="https://github.com/rust-lang/rust/pull/144943">#144943</a>: Rollup of 15 pull requests (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/144950">#144950</a>: Rollup of 11 pull requests (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145000">#145000</a>: Remove unneeded <code class="language-plaintext highlighter-rouge">stage</code> parameter when setting up stdlib Cargo</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145003">#145003</a>: Rollup of 12 pull requests</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145007">#145007</a>: Fix build/doc/test of error index generator</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145011">#145011</a>: Enforce in bootstrap that doc must have stage at least 1</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145083">#145083</a>: Fix cross-compilation of Cargo</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145089">#145089</a>: Improve error output when a command fails in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145116">#145116</a>: Revert #143906</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145131">#145131</a>: Enforce in bootstrap that clippy must have stage at least 1</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145156">#145156</a>: Override custom Cargo <code class="language-plaintext highlighter-rouge">build-dir</code> in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145207">#145207</a>: Ship correct Cranelift library in its dist component</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145215">#145215</a>: Enable RUST_BACKTRACE=1 on CI</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145221">#145221</a>: Fix Cargo cross-compilation (take two)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145253">#145253</a>: Document compiler and stdlib in stage1 in <code class="language-plaintext highlighter-rouge">pr-check-2</code> CI job</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145261">#145261</a>: Improve tracing in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145295">#145295</a>: Consolidate stage directories and group logs in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145310">#145310</a>: Reduce usage of <code class="language-plaintext highlighter-rouge">compiler_for</code> in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145315">#145315</a>: Rollup of 6 pull requests (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145320">#145320</a>: Allow cross-compiling the Cranelift dist component</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145324">#145324</a>: Rename and document <code class="language-plaintext highlighter-rouge">ONLY_HOSTS</code> in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145334">#145334</a>: Rollup of 11 pull requests</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145340">#145340</a>: Split codegen backend check step into two and don’t run it with <code class="language-plaintext highlighter-rouge">x check compiler</code></li>
  <li><a href="https://github.com/rust-lang/rust/pull/145341">#145341</a>: Install libgccjit into the compiler’s sysroot when cg_gcc is enabled</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145343">#145343</a>: Dogfood <code class="language-plaintext highlighter-rouge">-Zno-embed-metadata</code> in the standard library (<span style="color: green;">open</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145354">#145354</a>: Cache derive proc macro expansion with incremental query (<span style="color: green;">open</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145358">#145358</a>: Sort mono items by symbol name</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145406">#145406</a>: Rollup of 12 pull requests (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145407">#145407</a>: Rollup of 11 pull requests</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145408">#145408</a>: Deduplicate -L search paths</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145450">#145450</a>: Rollup of 11 pull requests</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145452">#145452</a>: Do not strip binaries in bootstrap everytime if they are unchanged</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145453">#145453</a>: Remove duplicated tracing span in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145454">#145454</a>: Fix tracing debug representation of steps without arguments in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145455">#145455</a>: Do not copy files in <code class="language-plaintext highlighter-rouge">copy_src_dirs</code> in dry run</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145460">#145460</a>: Speedup <code class="language-plaintext highlighter-rouge">copy_src_dirs</code> in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145472">#145472</a>: Enforce in bootstrap that dist and install must have stage at least 1</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145490">#145490</a>: Trace some basic I/O operations in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145557">#145557</a>: Fix uplifting in <code class="language-plaintext highlighter-rouge">Assemble</code> step</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145560">#145560</a>: Remove unused <code class="language-plaintext highlighter-rouge">PartialOrd</code>/<code class="language-plaintext highlighter-rouge">Ord</code> from bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145563">#145563</a>: Remove the <code class="language-plaintext highlighter-rouge">From</code> derive macro from prelude</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145565">#145565</a>: Improve context of bootstrap errors in CI</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145645">#145645</a>: Fix rustc uplifting (take two)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145654">#145654</a>: Download CI GCC into the correct directory</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145663">#145663</a>: Enforce in bootstrap that test must have stage at least 1 (except for compiletest)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145763">#145763</a>: Ship LLVM tools for the correct target when cross-compiling</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145780">#145780</a>: Do not warn about missing change ID in tarball builds (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145781">#145781</a>: Remove profile section from Clippy</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145841">#145841</a>: Always build miri for the host in <code class="language-plaintext highlighter-rouge">x run miri</code></li>
  <li><a href="https://github.com/rust-lang/rust/pull/145845">#145845</a>: Make <code class="language-plaintext highlighter-rouge">x test distcheck</code> self-contained</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145848">#145848</a>: Slightly optimize reading of source files</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145874">#145874</a>: Remove unnecessary stage2 host builds from cross-compiled dist builders</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145875">#145875</a>: Make bootstrap command caching opt-in</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145876">#145876</a>: Enable building/disting standard library in stage 0</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145902">#145902</a>: Avoid more rustc rebuilds in cross-compilation scenarios</li>
  <li><a href="https://github.com/rust-lang/rust/pull/145904">#145904</a>: Move <code class="language-plaintext highlighter-rouge">riscv64-gc-unknown-linux-musl</code> from Tier 2 with Host tools to Tier 2</li>
  <li><a href="https://github.com/rust-lang/rust/pull/146076">#146076</a>: Consolidate staging for compiletest steps in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/146090">#146090</a>: Derive <code class="language-plaintext highlighter-rouge">PartialEq</code> for <code class="language-plaintext highlighter-rouge">InvisibleOrigin</code></li>
  <li><a href="https://github.com/rust-lang/rust/pull/146124">#146124</a>: Test <code class="language-plaintext highlighter-rouge">rustc-dev</code> in <code class="language-plaintext highlighter-rouge">distcheck</code></li>
  <li><a href="https://github.com/rust-lang/rust/pull/146127">#146127</a>: Rename <code class="language-plaintext highlighter-rouge">ToolRustc</code> to <code class="language-plaintext highlighter-rouge">ToolRustcPrivate</code></li>
  <li><a href="https://github.com/rust-lang/rust/pull/146199">#146199</a>: Document Cargo with in-tree rustdoc</li>
  <li><a href="https://github.com/rust-lang/rust/pull/146203">#146203</a>: Do not copy rustc rlibs into the sysroot of the build compiler (<span style="color: green;">open</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/146253">#146253</a>: Optimize Cargo with LTO</li>
  <li><a href="https://github.com/rust-lang/rust/pull/146435">#146435</a>: Change the default value of <code class="language-plaintext highlighter-rouge">gcc.download-ci-gcc</code> to <code class="language-plaintext highlighter-rouge">true</code></li>
  <li><a href="https://github.com/rust-lang/rust/pull/146449">#146449</a>: Fix <code class="language-plaintext highlighter-rouge">libgccjit</code> symlink when we build GCC locally</li>
  <li><a href="https://github.com/rust-lang/rust/pull/146582">#146582</a>: Only run Cranelift dist test on nightly</li>
  <li><a href="https://github.com/rust-lang/rust/pull/146592">#146592</a>: Implement a simple diagnostic system for tidy</li>
  <li><a href="https://github.com/rust-lang/rust/pull/146771">#146771</a>: Simplify default value of <code class="language-plaintext highlighter-rouge">download-ci-llvm</code></li>
  <li><a href="https://github.com/rust-lang/rust/pull/146774">#146774</a>: Allow running <code class="language-plaintext highlighter-rouge">x &lt;cmd&gt; &lt;path&gt;</code> from a different directory</li>
  <li><a href="https://github.com/rust-lang/rust/pull/146884">#146884</a>: Fix modification check of <code class="language-plaintext highlighter-rouge">rustdoc-json-types</code></li>
  <li><a href="https://github.com/rust-lang/rust/pull/146920">#146920</a>: Rollup of 8 pull requests (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/146927">#146927</a>: Make it possible to <code class="language-plaintext highlighter-rouge">x install</code> Cranelift and LLVM bitcode linker</li>
  <li><a href="https://github.com/rust-lang/rust/pull/147038">#147038</a>: Rename verbosity functions in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/147039">#147039</a>: [DO NOT MERGE] Test PR for new rustc-perf (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/147046">#147046</a>: Rename <code class="language-plaintext highlighter-rouge">rust.use-lld</code> to <code class="language-plaintext highlighter-rouge">rust.bootstrap-override-lld</code></li>
  <li><a href="https://github.com/rust-lang/rust/pull/147157">#147157</a>: Generalize configuring LLD as the default linker in bootstrap (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/147188">#147188</a>: Remove usage of <code class="language-plaintext highlighter-rouge">compiletest-use-stage0-libtest</code> from CI</li>
  <li><a href="https://github.com/rust-lang/rust/pull/147515">#147515</a>: Update rustc-perf submodule</li>
  <li><a href="https://github.com/rust-lang/rust/pull/147625">#147625</a>: Add a warning when running tests with the GCC backend and debug assertions are enabled</li>
  <li><a href="https://github.com/rust-lang/rust/pull/147626">#147626</a>: Generalize configuring LLD as the default linker in bootstrap</li>
  <li><a href="https://github.com/rust-lang/rust/pull/147698">#147698</a>: Do not enable LLD if we don’t build host code for targets that opt into it</li>
  <li><a href="https://github.com/rust-lang/rust/pull/147816">#147816</a>: Do not error out for <code class="language-plaintext highlighter-rouge">download-rustc</code> if LTO is configured</li>
  <li><a href="https://github.com/rust-lang/rust/pull/148395">#148395</a>: Generalize branch references</li>
  <li><a href="https://github.com/rust-lang/rust/pull/148500">#148500</a>: Update git index before running diff-index</li>
  <li><a href="https://github.com/rust-lang/rust/pull/148564">#148564</a>: Change default branch references</li>
  <li><a href="https://github.com/rust-lang/rust/pull/148675">#148675</a>: Remove eslint-js from npm dependencies</li>
  <li><a href="https://github.com/rust-lang/rust/pull/148896">#148896</a>: Revert “Rollup merge of #146627 - madsmtm:jemalloc-simplify, r=jdonszelmann”</li>
  <li><a href="https://github.com/rust-lang/rust/pull/149605">#149605</a>: Use branch name instead of HEAD when unshallowing</li>
  <li><a href="https://github.com/rust-lang/rust/pull/149612">#149612</a>: Apply the <code class="language-plaintext highlighter-rouge">bors</code> environment also to the <code class="language-plaintext highlighter-rouge">outcome</code> job</li>
  <li><a href="https://github.com/rust-lang/rust/pull/149657">#149657</a>: Revert “Rollup merge of #149147 - chenyukang:yukang-fix-unused_assignments-macro-gen-147648, r=JonathanBrouwer”</li>
  <li><a href="https://github.com/rust-lang/rust/pull/149724">#149724</a>: Fix off-by-one staging output when testing the library</li>
  <li><a href="https://github.com/rust-lang/rust/pull/149734">#149734</a>: Mirror GCC 9.5.0</li>
  <li><a href="https://github.com/rust-lang/rust/pull/149806">#149806</a>: Mirror <code class="language-plaintext highlighter-rouge">ubuntu:24.04</code> on ghcr</li>
  <li><a href="https://github.com/rust-lang/rust/pull/149807">#149807</a>: Use ubuntu:24.04 for the <code class="language-plaintext highlighter-rouge">x86_64-gnu-miri</code> job</li>
  <li><a href="https://github.com/rust-lang/rust/pull/149808">#149808</a>: WIP: Try to reuse PGO profiles in <code class="language-plaintext highlighter-rouge">opt-dist</code> (<span style="color: green;">open</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/149921">#149921</a>: Add new source component that includes GPL-licensed source</li>
  <li><a href="https://github.com/rust-lang/rust/pull/150070">#150070</a>: Partially revert #147888 and print warning if LLVM CMake dir is missing when building Enzyme</li>
  <li><a href="https://github.com/rust-lang/rust/pull/150071">#150071</a>: Add dist step for Enzyme (<span style="color: green;">open</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/150308">#150308</a>: Update bors configuration</li>
  <li><a href="https://github.com/rust-lang/rust/pull/150478">#150478</a>: Fix new bors config</li>
  <li><a href="https://github.com/rust-lang/rust/pull/150489">#150489</a>: Disable debuginfo when building GCC</li>
  <li><a href="https://github.com/rust-lang/rust/pull/150490">#150490</a>: Specify bug URL when building GCC</li>
  <li><a href="https://github.com/rust-lang/rust/pull/150534">#150534</a>: Run rustdoc tests in opt-dist tests (<span style="color: green;">open</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/150535">#150535</a>: Rename the gcc component to ci-gcc (<span style="color: green;">open</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/150538">#150538</a>: Add a dist component for cg_gcc (<span style="color: green;">open</span>)</li>
  <li><a href="https://github.com/rust-lang/rust/pull/150541">#150541</a>: Add a dist component for libgccjit (<span style="color: green;">open</span>)</li>
</ul>

<h3 id="rust-langrustc-perf-167-prs">rust-lang/rustc-perf (167 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2029">#2029</a>: Triage 2025 01 07</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2030">#2030</a>: Change what we check in CI profiling test</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2035">#2035</a>: Triage 2025 01 27</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2040">#2040</a>: Update bitmaps to 3.2.1</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2042">#2042</a>: Bump MSRV to 1.81</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2044">#2044</a>: Triage 2025 02 18</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2045">#2045</a>: Make compare page slightly less misleading when there are no relevant results</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2046">#2046</a>: Pin the rustc version used to compile collector</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2049">#2049</a>: Add manual deploy workflow</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2050">#2050</a>: Fix gather data test</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2051">#2051</a>: Fix bootstrap rustc benchmark</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2053">#2053</a>: Fix stable benchmarks</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2055">#2055</a>: Increase the number of finished runs shown in the status page</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2056">#2056</a>: Revert “Increase the number of finished runs shown in the status page”</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2058">#2058</a>: Triage 2025 03 11</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2069">#2069</a>: Update html5ever to 0.31.0</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2071">#2071</a>: Update cargo to 0.87.1</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2073">#2073</a>: Add clap_derive 4.5.32 benchmark</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2075">#2075</a>: Remove bitmaps-3.1.0 benchmark</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2076">#2076</a>: Remove html5ever-0.26.0 benchmark</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2077">#2077</a>: Update cranelift codegen to 0.119.0</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2078">#2078</a>: Add support for selecting workspace package</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2079">#2079</a>: Remove cargo 0.60.0 benchmark</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2082">#2082</a>: Update diesel to 2.2.10</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2084">#2084</a>: Add <code class="language-plaintext highlighter-rouge">eza 0.21.2</code> benchmark</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2089">#2089</a>: Remove <code class="language-plaintext highlighter-rouge">cranelift-codegen-0.82.1</code> benchmark</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2090">#2090</a>: Remove <code class="language-plaintext highlighter-rouge">diesel-1.4.8</code> benchmark</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2091">#2091</a>: Update <code class="language-plaintext highlighter-rouge">hyper</code> to 1.6.0</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2092">#2092</a>: Move typenum-1.18.0 patch file to the correct directory</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2093">#2093</a>: Fix category value case</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2095">#2095</a>: Update image to 0.25.6</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2102">#2102</a>: Remove <code class="language-plaintext highlighter-rouge">clap-3.1.6</code> benchmark</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2103">#2103</a>: Remove <code class="language-plaintext highlighter-rouge">exa-0.10.1</code> benchmark</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2104">#2104</a>: Benchmark update 2025 libc 0.2.172</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2109">#2109</a>: Add <code class="language-plaintext highlighter-rouge">regex-automata-0.4.8</code> benchmark</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2111">#2111</a>: Remove <code class="language-plaintext highlighter-rouge">hyper-0.14.18</code></li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2112">#2112</a>: Remove <code class="language-plaintext highlighter-rouge">image-0.24.1</code></li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2120">#2120</a>: Remove <code class="language-plaintext highlighter-rouge">libc-0.2.124</code></li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2121">#2121</a>: Remove <code class="language-plaintext highlighter-rouge">regex-1.5.5</code></li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2122">#2122</a>: Remove <code class="language-plaintext highlighter-rouge">ripgrep-13.0.0</code></li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2123">#2123</a>: Remove <code class="language-plaintext highlighter-rouge">ripgrep-13.0.0-tiny</code></li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2124">#2124</a>: Add <code class="language-plaintext highlighter-rouge">diesel-2.2.10 -new-solver</code> benchmark (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2125">#2125</a>: Add <code class="language-plaintext highlighter-rouge">nalgebra-0.33.0-new-solver</code> benchmark</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2126">#2126</a>: Add <code class="language-plaintext highlighter-rouge">syn-2.0.101-new-solver</code></li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2127">#2127</a>: Add <code class="language-plaintext highlighter-rouge">serde-1.0.219-new-solver</code></li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2128">#2128</a>: Add <code class="language-plaintext highlighter-rouge">bitmaps-3.2.1-new-solver</code></li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2129">#2129</a>: Add <code class="language-plaintext highlighter-rouge">html5ever-0.31.0-new-solver</code></li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2135">#2135</a>: Triage 2025 05 20</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2138">#2138</a>: Update performance triage roster</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2139">#2139</a>: Add <code class="language-plaintext highlighter-rouge">--exact-match</code> CLI argument to allow exact matching of benchmarks</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2141">#2141</a>: Add <code class="language-plaintext highlighter-rouge">serde-1.0.219-threads4</code> benchmark for the parallel frontend</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2142">#2142</a>: Improve error message when <code class="language-plaintext highlighter-rouge">perf-config.json</code> is not found</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2143">#2143</a>: Add <code class="language-plaintext highlighter-rouge">large-workspace</code> stress test benchmark</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2144">#2144</a>: Add a test for existence of <code class="language-plaintext highlighter-rouge">[workspace]</code> in compile benchmarks</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2145">#2145</a>: Add <code class="language-plaintext highlighter-rouge">triagebot</code> benchmark (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2146">#2146</a>: Add <code class="language-plaintext highlighter-rouge">crates.io</code> benchmark (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2147">#2147</a>: Use <code class="language-plaintext highlighter-rouge">--exact-match</code> instead of <code class="language-plaintext highlighter-rouge">--include</code> on the website</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2150">#2150</a>: Use async closures in collector</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2152">#2152</a>: Run DB tests against SQLite too</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2154">#2154</a>: Fix Clippy benchmarks</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2155">#2155</a>: Only invoke the Clippy wrapper for the leaf crate</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2156">#2156</a>: Add <code class="language-plaintext highlighter-rouge">DocJson</code> profile for benchmarking JSON rustdoc output</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2158">#2158</a>: Do not compile extended tools in bootstrap benchmark</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2160">#2160</a>: Use stable compiler for compiling the <code class="language-plaintext highlighter-rouge">collector</code></li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2161">#2161</a>: Check test cases with measurements</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2162">#2162</a>: Triage 2025 06 17</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2165">#2165</a>: Port the detailed query page to Vue</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2168">#2168</a>: Add query cache hits to detailed query table</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2172">#2172</a>: Temporarily disable release artifact benchmarking</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2173">#2173</a>: Add new <code class="language-plaintext highlighter-rouge">cargo</code> stable benchmark</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2174">#2174</a>: Remove the <code class="language-plaintext highlighter-rouge">style-servo</code> stable benchmark</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2175">#2175</a>: Add <code class="language-plaintext highlighter-rouge">include-blob</code> secondary benchmark</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2176">#2176</a>: Fix incr patch of <code class="language-plaintext highlighter-rouge">include-blob</code></li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2179">#2179</a>: Make <code class="language-plaintext highlighter-rouge">cargo</code> compilable with modern rustc and re-enable stable benchmarks</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2180">#2180</a>: Do not show patch release data in the dashboard</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2182">#2182</a>: Update benchmarking server specs</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2183">#2183</a>: Fix compilation of the stable cargo benchmark on latest beta</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2185">#2185</a>: Stream bootstrap log when running the rustc benchmark</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2186">#2186</a>: Remove unused dev dependency</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2187">#2187</a>: Revert #2161</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2188">#2188</a>: Do not panic on S3 file upload failures</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2189">#2189</a>: Log duration of self-profile parsing</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2194">#2194</a>: Revert “Do not panic on S3 file upload failures”</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2201">#2201</a>: Refactor benchmark requests</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2202">#2202</a>: Add 2025-07-15 triage</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2204">#2204</a>: Implement benchmark comparison TUI using Ratatui</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2206">#2206</a>: Add basic benchmark set implementation</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2209">#2209</a>: Add changes filter to compare page</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2210">#2210</a>: Make missing test case check more granular</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2216">#2216</a>: Preparation for collector job queue integration</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2218">#2218</a>: Do not exit website if there is no data</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2219">#2219</a>: Add 2025-08-12 triage</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2226">#2226</a>: Add job benchmark loop</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2229">#2229</a>: Track stdlib artifact size</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2230">#2230</a>: Check latest commit SHA in the collector</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2231">#2231</a>: Purge toolchain cache directory if it gets too large</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2232">#2232</a>: Fix duration formatting on the status page</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2234">#2234</a>: Refactor new status page</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2237">#2237</a>: Add 2025-09-02 triage</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2239">#2239</a>: Check if collector is active when starting it</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2240">#2240</a>: Stop hardcoding the master branch of rust-lang/rust</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2250">#2250</a>: Estimate when requests will end in the new status page</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2251">#2251</a>: Change <code class="language-plaintext highlighter-rouge">./target/release</code> example commands to <code class="language-plaintext highlighter-rouge">cargo run --release</code></li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2252">#2252</a>: Do not use the benchmark index when computing benchmark request queue</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2253">#2253</a>: Reload benchmark request and DB index after a request is completed</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2256">#2256</a>: Add 2025-09-23 triage</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2259">#2259</a>: Partially enable job queue in production</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2261">#2261</a>: Do not update collector progress when the job queue is used</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2262">#2262</a>: Fix rustc benchmark when the <code class="language-plaintext highlighter-rouge">rust</code> directory does not exist</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2265">#2265</a>: Remove benchmark request and jobs when an artifact is purged</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2266">#2266</a>: Fix job queue migration</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2270">#2270</a>: Use merge queue on CI</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2271">#2271</a>: Allow running the website with SQLite</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2272">#2272</a>: Render target in compile-time compare page</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2273">#2273</a>: Bump MSRV to 1.88.0</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2274">#2274</a>: Show stderr when an <code class="language-plaintext highlighter-rouge">eprintln</code> profiler invocation fails</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2275">#2275</a>: Fix Windows support</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2276">#2276</a>: Get host tuple from rustc used for benchmarking/profiling, if possible</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2277">#2277</a>: Add kind to job queue unique constraint</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2278">#2278</a>: Make <code class="language-plaintext highlighter-rouge">html5ever</code> compilable on beta</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2279">#2279</a>: Test stable benchmarks with the beta toolchain</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2280">#2280</a>: Add 2025-10-13 triage</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2284">#2284</a>: Make job queue handler more resilient against panics</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2285">#2285</a>: Add target to runtime benchmarks</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2289">#2289</a>: Add total accumulated change to the Aggregations tab</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2290">#2290</a>: Update Parcel version and re-enable Hot Module Reload</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2292">#2292</a>: Update design of the new status page</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2293">#2293</a>: Update job queue loop to avoid early exit</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2294">#2294</a>: Remove cutoff dates for inserting benchmark requests</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2295">#2295</a>: Fix stable benchmarks CI</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2296">#2296</a>: Ensure that deploys are not concurrent</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2299">#2299</a>: Sort jobs of in-progress benchmark requests by their creation date</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2300">#2300</a>: Ensure that compilation sections are always present</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2301">#2301</a>: Make rustc-perf example Cargo commands more bulletproof</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2303">#2303</a>: Create master benchmark requests in the new system if they are not in the old system</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2304">#2304</a>: Re-enable backfilling</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2305">#2305</a>: Switch new and old status pages</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2306">#2306</a>: Show collector jobs by default</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2307">#2307</a>: Improve error UI in status page</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2308">#2308</a>: Rename CI jobs to make them more accurate</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2309">#2309</a>: Use the new job queue for request duration estimation</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2310">#2310</a>: Add 2025-11-03 triage</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2311">#2311</a>: Add more error logs</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2312">#2312</a>: Do not send “commit queued” comments after every try build completes</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2313">#2313</a>: Change error handling for creating parent jobs</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2314">#2314</a>: Improve error reporting in status page and logs</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2315">#2315</a>: Treat in-progress requests as completed when computing queue order</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2316">#2316</a>: Always load the benchmark request index</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2320">#2320</a>: Correctly pass backend when computing benchmark detail graph</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2321">#2321</a>: Fix benchmarking of stable/beta releases</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2325">#2325</a>: Add stable benchmarks to the compile-time benchmark set</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2327">#2327</a>: Fix endless comment loop</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2328">#2328</a>: Add comment send error logging and handle empty commands</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2329">#2329</a>: Validate codegen backends in bot commands and mention them in help</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2330">#2330</a>: Parse profiles in GitHub commands</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2334">#2334</a>: Handle non-default benchmark parameters in the graphs endpoint</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2335">#2335</a>: Add 2025-11-25 triage</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2337">#2337</a>: Do not enqueue rustc job for release benchmark requests</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2338">#2338</a>: Improve queue ordering when bootstrapping the system</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2339">#2339</a>: Modify UI of the status page</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2340">#2340</a>: Make benchmark set and job operations a bit more robust</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2341">#2341</a>: Split the x64 benchmark set into two</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2343">#2343</a>: Do not consider completed requests with errors for request duration estimation</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2350">#2350</a>: Fix release benchmark with multiple collectors</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2351">#2351</a>: Update job queue documentation</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2355">#2355</a>: Add 2025-12-16 triage</li>
  <li><a href="https://github.com/rust-lang/rustc-perf/pull/2357">#2357</a>: Fix cyclic dependency in frontend</li>
</ul>

<h3 id="rust-langteam-164-prs">rust-lang/team (164 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/team/pull/1635">#1635</a>: Add branch protection for the <code class="language-plaintext highlighter-rouge">master-old</code> branch in <code class="language-plaintext highlighter-rouge">rustc-dev-guide</code></li>
  <li><a href="https://github.com/rust-lang/team/pull/1644">#1644</a>: Remove mentions of the bors-rs organization</li>
  <li><a href="https://github.com/rust-lang/team/pull/1657">#1657</a>: Extend the triagebot team</li>
  <li><a href="https://github.com/rust-lang/team/pull/1663">#1663</a>: Add dry run workflow for testing the applied changes</li>
  <li><a href="https://github.com/rust-lang/team/pull/1670">#1670</a>: Add <code class="language-plaintext highlighter-rouge">mentorship-programs</code> team</li>
  <li><a href="https://github.com/rust-lang/team/pull/1677">#1677</a>: PR 2 (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/team/pull/1679">#1679</a>: Fix cargo build in dry run workflow</li>
  <li><a href="https://github.com/rust-lang/team/pull/1680">#1680</a>: [do not merge] Test PR for dry run (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/team/pull/1681">#1681</a>: Fix dry run workflow for PR from forks</li>
  <li><a href="https://github.com/rust-lang/team/pull/1682">#1682</a>: Add explicit <code class="language-plaintext highlighter-rouge">--repo</code> argument in dry-run workflow</li>
  <li><a href="https://github.com/rust-lang/team/pull/1683">#1683</a>: Fix concurrency group in dry-run</li>
  <li><a href="https://github.com/rust-lang/team/pull/1684">#1684</a>: Redirect stderr to file in dry-run workflow</li>
  <li><a href="https://github.com/rust-lang/team/pull/1686">#1686</a>: Also print sync-team output to CI logs</li>
  <li><a href="https://github.com/rust-lang/team/pull/1688">#1688</a>: Use pipefail in dry-run workflow</li>
  <li><a href="https://github.com/rust-lang/team/pull/1689">#1689</a>: Add infra-admins explicitly to sync-team</li>
  <li><a href="https://github.com/rust-lang/team/pull/1690">#1690</a>: Add validation for <code class="language-plaintext highlighter-rouge">allowed-merge-teams</code></li>
  <li><a href="https://github.com/rust-lang/team/pull/1696">#1696</a>: Add CODEOWNERS</li>
  <li><a href="https://github.com/rust-lang/team/pull/1697">#1697</a>: Import sync team into team</li>
  <li><a href="https://github.com/rust-lang/team/pull/1701">#1701</a>: Disable sending dry-run PR comments temporarily</li>
  <li><a href="https://github.com/rust-lang/team/pull/1702">#1702</a>: Add basic threat model</li>
  <li><a href="https://github.com/rust-lang/team/pull/1705">#1705</a>: Update CODEOWNERS</li>
  <li><a href="https://github.com/rust-lang/team/pull/1710">#1710</a>: Re-enable dry run comments</li>
  <li><a href="https://github.com/rust-lang/team/pull/1712">#1712</a>: Use local <code class="language-plaintext highlighter-rouge">sync-team</code> code for performing dry runs</li>
  <li><a href="https://github.com/rust-lang/team/pull/1713">#1713</a>: Migrate <code class="language-plaintext highlighter-rouge">team</code> to <code class="language-plaintext highlighter-rouge">clap</code></li>
  <li><a href="https://github.com/rust-lang/team/pull/1715">#1715</a>: Switch to merge queues</li>
  <li><a href="https://github.com/rust-lang/team/pull/1717">#1717</a>: Upload GitHub Pages using the standard action</li>
  <li><a href="https://github.com/rust-lang/team/pull/1718">#1718</a>: No-op change to retrigger pages (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/team/pull/1719">#1719</a>: Disable dry-run in merge queue</li>
  <li><a href="https://github.com/rust-lang/team/pull/1721">#1721</a>: Run sync-team in merge queue</li>
  <li><a href="https://github.com/rust-lang/team/pull/1722">#1722</a>: Backport sync team changes</li>
  <li><a href="https://github.com/rust-lang/team/pull/1723">#1723</a>: Run sync every four hours and allow manual execution</li>
  <li><a href="https://github.com/rust-lang/team/pull/1725">#1725</a>: Pin Rust version used on CI</li>
  <li><a href="https://github.com/rust-lang/team/pull/1726">#1726</a>: Use <code class="language-plaintext highlighter-rouge">sync-team</code> from <code class="language-plaintext highlighter-rouge">team</code> directly</li>
  <li><a href="https://github.com/rust-lang/team/pull/1727">#1727</a>: Remove individual bors permissions</li>
  <li><a href="https://github.com/rust-lang/team/pull/1728">#1728</a>: Forbid admin permissions</li>
  <li><a href="https://github.com/rust-lang/team/pull/1737">#1737</a>: Add aws-runners-test repository under automation</li>
  <li><a href="https://github.com/rust-lang/team/pull/1738">#1738</a>: Fix name of binary in dry-run CI workflow</li>
  <li><a href="https://github.com/rust-lang/team/pull/1739">#1739</a>: Add Urgau to t-triagebot</li>
  <li><a href="https://github.com/rust-lang/team/pull/1741">#1741</a>: Fix sync-team link in README</li>
  <li><a href="https://github.com/rust-lang/team/pull/1742">#1742</a>: Sync GitHub App branch protection push allowances</li>
  <li><a href="https://github.com/rust-lang/team/pull/1743">#1743</a>: Run dry-run with code from PR if it is not from a fork</li>
  <li><a href="https://github.com/rust-lang/team/pull/1745">#1745</a>: Work around missing permissions for rust-lang/rust branch protections</li>
  <li><a href="https://github.com/rust-lang/team/pull/1746">#1746</a>: Add bots needed for rust-lang/rust</li>
  <li><a href="https://github.com/rust-lang/team/pull/1747">#1747</a>: Remove unused bors repos from permission allowlist</li>
  <li><a href="https://github.com/rust-lang/team/pull/1748">#1748</a>: Change repository sync order to handle archiving</li>
  <li><a href="https://github.com/rust-lang/team/pull/1749">#1749</a>: Make it possible to run sync-team against local data</li>
  <li><a href="https://github.com/rust-lang/team/pull/1750">#1750</a>: Add required CI check for triagebot merge queue</li>
  <li><a href="https://github.com/rust-lang/team/pull/1751">#1751</a>: Do not require PRs for branches managed by homu</li>
  <li><a href="https://github.com/rust-lang/team/pull/1752">#1752</a>: Add debug logging of team and repo diffs during actual sync</li>
  <li><a href="https://github.com/rust-lang/team/pull/1755">#1755</a>: Revert “Merge pull request #1752 from Kobzol/debug-log”</li>
  <li><a href="https://github.com/rust-lang/team/pull/1756">#1756</a>: Use destructuring in diffs to make it harder to forget to include diff fields</li>
  <li><a href="https://github.com/rust-lang/team/pull/1761">#1761</a>: Add kobzol to relnotes-interest-group</li>
  <li><a href="https://github.com/rust-lang/team/pull/1762">#1762</a>: [WIP] Add all unmanaged repos (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/team/pull/1772">#1772</a>: Backport archived Rust Analyzer repos</li>
  <li><a href="https://github.com/rust-lang/team/pull/1773">#1773</a>: Backport archived rust-dev-tools repos</li>
  <li><a href="https://github.com/rust-lang/team/pull/1774">#1774</a>: Backport archived rust-lang-deprecated repos (batch 1)</li>
  <li><a href="https://github.com/rust-lang/team/pull/1775">#1775</a>: Backport archived rust-lang-deprecated repos (batch 2)</li>
  <li><a href="https://github.com/rust-lang/team/pull/1776">#1776</a>: Backport archived rust-lang-nursery repos (batch 1)</li>
  <li><a href="https://github.com/rust-lang/team/pull/1777">#1777</a>: Backport archived rust-lang-nursery repos (batch 2)</li>
  <li><a href="https://github.com/rust-lang/team/pull/1788">#1788</a>: Backport rust-dev-tools repos (batch 1)</li>
  <li><a href="https://github.com/rust-lang/team/pull/1789">#1789</a>: Backport rust-dev-tools repos (batch 2)</li>
  <li><a href="https://github.com/rust-lang/team/pull/1796">#1796</a>: Backport rustc-perf-collector repo</li>
  <li><a href="https://github.com/rust-lang/team/pull/1797">#1797</a>: Backport rust-lang-nursery repos (batch 1)</li>
  <li><a href="https://github.com/rust-lang/team/pull/1798">#1798</a>: Backport rust-lang-nursery repos (batch 2)</li>
  <li><a href="https://github.com/rust-lang/team/pull/1799">#1799</a>: Backport rust-lang-nursery repos (batch 3)</li>
  <li><a href="https://github.com/rust-lang/team/pull/1800">#1800</a>: Backport rust-cookbook repo</li>
  <li><a href="https://github.com/rust-lang/team/pull/1801">#1801</a>: Backport rust-lang-ci/rust repo (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/team/pull/1802">#1802</a>: Backport archived rls-vfs repo</li>
  <li><a href="https://github.com/rust-lang/team/pull/1803">#1803</a>: Backport archived rust-embedded repos (batch 1)</li>
  <li><a href="https://github.com/rust-lang/team/pull/1804">#1804</a>: Backport archived rust-embedded repos (batch 2)</li>
  <li><a href="https://github.com/rust-lang/team/pull/1805">#1805</a>: Backport rust-analyzer repos (batch 1)</li>
  <li><a href="https://github.com/rust-lang/team/pull/1806">#1806</a>: Backport rust-analyzer repos for archival</li>
  <li><a href="https://github.com/rust-lang/team/pull/1807">#1807</a>: Backport rust-analyzer repos (batch 2)</li>
  <li><a href="https://github.com/rust-lang/team/pull/1813">#1813</a>: Allow automatically fetching Zulip ID for newly added users</li>
  <li><a href="https://github.com/rust-lang/team/pull/1814">#1814</a>: Add Google Summer of Code 2025 contributors to the <code class="language-plaintext highlighter-rouge">gsoc-contributors</code> team</li>
  <li><a href="https://github.com/rust-lang/team/pull/1816">#1816</a>: Add branch protection to rust-log-analyzer</li>
  <li><a href="https://github.com/rust-lang/team/pull/1819">#1819</a>: Add Karan Janthe to <code class="language-plaintext highlighter-rouge">gsoc-contributors</code></li>
  <li><a href="https://github.com/rust-lang/team/pull/1820">#1820</a>: Add website entry for GSoC contributors</li>
  <li><a href="https://github.com/rust-lang/team/pull/1821">#1821</a>: Add GSoC 2025 mentors to the <code class="language-plaintext highlighter-rouge">mentors</code> team</li>
  <li><a href="https://github.com/rust-lang/team/pull/1823">#1823</a>: Add <code class="language-plaintext highlighter-rouge">gsoc-contributors</code> as a subteam of <code class="language-plaintext highlighter-rouge">launching-pad</code></li>
  <li><a href="https://github.com/rust-lang/team/pull/1828">#1828</a>: Add support for <code class="language-plaintext highlighter-rouge">rust-timer</code> merge bot and <code class="language-plaintext highlighter-rouge">try-perf</code>/<code class="language-plaintext highlighter-rouge">perf-tmp</code> branch protections</li>
  <li><a href="https://github.com/rust-lang/team/pull/1835">#1835</a>: Update rustlings homepage URL</li>
  <li><a href="https://github.com/rust-lang/team/pull/1836">#1836</a>: Remove mentions of rust-lang-ci</li>
  <li><a href="https://github.com/rust-lang/team/pull/1843">#1843</a>: Make homu permissions more strict</li>
  <li><a href="https://github.com/rust-lang/team/pull/1847">#1847</a>: Simplify comment implementation in dry-run</li>
  <li><a href="https://github.com/rust-lang/team/pull/1851">#1851</a>: Remove homu from <code class="language-plaintext highlighter-rouge">a-mir-formality</code></li>
  <li><a href="https://github.com/rust-lang/team/pull/1852">#1852</a>: Add back two removed homepages</li>
  <li><a href="https://github.com/rust-lang/team/pull/1853">#1853</a>: Add back branch protection for <code class="language-plaintext highlighter-rouge">expect-test</code></li>
  <li><a href="https://github.com/rust-lang/team/pull/1854">#1854</a>: Add <code class="language-plaintext highlighter-rouge">expect-test</code> team</li>
  <li><a href="https://github.com/rust-lang/team/pull/1859">#1859</a>: Add <code class="language-plaintext highlighter-rouge">vscode-themes</code> under automation and archive it</li>
  <li><a href="https://github.com/rust-lang/team/pull/1866">#1866</a>: Sync organization members (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/team/pull/1869">#1869</a>: Fixup links in README</li>
  <li><a href="https://github.com/rust-lang/team/pull/1870">#1870</a>: Add required CI check to Forge</li>
  <li><a href="https://github.com/rust-lang/team/pull/1873">#1873</a>: Add panstromek and James Barford into the compiler performance working area</li>
  <li><a href="https://github.com/rust-lang/team/pull/1879">#1879</a>: Unify dependency versions between team and sync-team</li>
  <li><a href="https://github.com/rust-lang/team/pull/1891">#1891</a>: Add more blocking CI checks to rustc-perf</li>
  <li><a href="https://github.com/rust-lang/team/pull/1893">#1893</a>: Add <code class="language-plaintext highlighter-rouge">josh-sync</code> repository</li>
  <li><a href="https://github.com/rust-lang/team/pull/1896">#1896</a>: Require tests to pass on <code class="language-plaintext highlighter-rouge">josh-sync</code></li>
  <li><a href="https://github.com/rust-lang/team/pull/1900">#1900</a>: Add dev desktop permissions to wg-gcc-backend</li>
  <li><a href="https://github.com/rust-lang/team/pull/1906">#1906</a>: Add <code class="language-plaintext highlighter-rouge">infra-admins</code> to the <code class="language-plaintext highlighter-rouge">bors</code> repo</li>
  <li><a href="https://github.com/rust-lang/team/pull/1907">#1907</a>: Fix rustc-perf CI checks</li>
  <li><a href="https://github.com/rust-lang/team/pull/1912">#1912</a>: Change cron to only run once per day</li>
  <li><a href="https://github.com/rust-lang/team/pull/1972">#1972</a>: Add reference expansion repo</li>
  <li><a href="https://github.com/rust-lang/team/pull/1978">#1978</a>: Lock down permissions on <code class="language-plaintext highlighter-rouge">promote-release</code></li>
  <li><a href="https://github.com/rust-lang/team/pull/1986">#1986</a>: Add archived teams to v1 API</li>
  <li><a href="https://github.com/rust-lang/team/pull/1988">#1988</a>: Make the output of the static API deterministic</li>
  <li><a href="https://github.com/rust-lang/team/pull/1992">#1992</a>: Ensure that Zulip user IDs are unique</li>
  <li><a href="https://github.com/rust-lang/team/pull/1997">#1997</a>: Rename community survey team to survey team</li>
  <li><a href="https://github.com/rust-lang/team/pull/1998">#1998</a>: Change branch protection of rustc-dev-guide to <code class="language-plaintext highlighter-rouge">main</code></li>
  <li><a href="https://github.com/rust-lang/team/pull/1999">#1999</a>: Add a few website descriptions to archived teams</li>
  <li><a href="https://github.com/rust-lang/team/pull/2002">#2002</a>: Give permission to do try builds to wg-triage (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/team/pull/2004">#2004</a>: Change default branch of www.rust-lang.org to <code class="language-plaintext highlighter-rouge">main</code></li>
  <li><a href="https://github.com/rust-lang/team/pull/2005">#2005</a>: Update configuration of the website repo</li>
  <li><a href="https://github.com/rust-lang/team/pull/2010">#2010</a>: Rename default branch of rustup to <code class="language-plaintext highlighter-rouge">main</code></li>
  <li><a href="https://github.com/rust-lang/team/pull/2011">#2011</a>: Add Sakibul to the bors team</li>
  <li><a href="https://github.com/rust-lang/team/pull/2012">#2012</a>: Add Zalathar to the bootstrap team</li>
  <li><a href="https://github.com/rust-lang/team/pull/2013">#2013</a>: Use merge queue on rustc-perf</li>
  <li><a href="https://github.com/rust-lang/team/pull/2015">#2015</a>: Update Project directors based on 2025 election results</li>
  <li><a href="https://github.com/rust-lang/team/pull/2035">#2035</a>: Remove website entry for the alumni team</li>
  <li><a href="https://github.com/rust-lang/team/pull/2039">#2039</a>: Backfill missing Zulip IDs</li>
  <li><a href="https://github.com/rust-lang/team/pull/2040">#2040</a>: Make Zulip IDs required</li>
  <li><a href="https://github.com/rust-lang/team/pull/2043">#2043</a>: Remove Discord roles and website Discord info</li>
  <li><a href="https://github.com/rust-lang/team/pull/2049">#2049</a>: Fix GitHub username of iownrena</li>
  <li><a href="https://github.com/rust-lang/team/pull/2050">#2050</a>: Backfill missing Zulip IDs based on GitHub account association and user comments</li>
  <li><a href="https://github.com/rust-lang/team/pull/2051">#2051</a>: Archive the <code class="language-plaintext highlighter-rouge">rust-analyzer/countme</code> repository</li>
  <li><a href="https://github.com/rust-lang/team/pull/2052">#2052</a>: Backfill Zulip IDs based on Zulip name and GitHub username match</li>
  <li><a href="https://github.com/rust-lang/team/pull/2053">#2053</a>: Backfill Zulip IDs based on Zulip name and team name match</li>
  <li><a href="https://github.com/rust-lang/team/pull/2054">#2054</a>: Backfill Zulip IDs based on Zulip name and GitHub name match</li>
  <li><a href="https://github.com/rust-lang/team/pull/2055">#2055</a>: Change branch protection for <code class="language-plaintext highlighter-rouge">rust-lang/rust</code></li>
  <li><a href="https://github.com/rust-lang/team/pull/2056">#2056</a>: Change branch protection of <code class="language-plaintext highlighter-rouge">rustfmt</code></li>
  <li><a href="https://github.com/rust-lang/team/pull/2058">#2058</a>: Add <code class="language-plaintext highlighter-rouge">t-website</code> Zulip stream to t-website website entry</li>
  <li><a href="https://github.com/rust-lang/team/pull/2061">#2061</a>: Update how often is the website updated</li>
  <li><a href="https://github.com/rust-lang/team/pull/2071">#2071</a>: Make it possible to document usage of GitHub Sponsors</li>
  <li><a href="https://github.com/rust-lang/team/pull/2072">#2072</a>: Configure a <code class="language-plaintext highlighter-rouge">T-website</code> zulip group for the website team</li>
  <li><a href="https://github.com/rust-lang/team/pull/2078">#2078</a>: Sync crates.io Trusted Publishing configs</li>
  <li><a href="https://github.com/rust-lang/team/pull/2082">#2082</a>: Add triagebot to glob</li>
  <li><a href="https://github.com/rust-lang/team/pull/2084">#2084</a>: Create a private channel that contains all team members</li>
  <li><a href="https://github.com/rust-lang/team/pull/2090">#2090</a>: Add trusted publishing for the <code class="language-plaintext highlighter-rouge">annotate-snippets</code> crate</li>
  <li><a href="https://github.com/rust-lang/team/pull/2091">#2091</a>: Add trusted publishing for the <code class="language-plaintext highlighter-rouge">measureme</code> crates</li>
  <li><a href="https://github.com/rust-lang/team/pull/2096">#2096</a>: Add trusted publishing to <code class="language-plaintext highlighter-rouge">thorin</code></li>
  <li><a href="https://github.com/rust-lang/team/pull/2102">#2102</a>: Make validation error messages more verbose</li>
  <li><a href="https://github.com/rust-lang/team/pull/2114">#2114</a>: Add trusted publishing to mdbook</li>
  <li><a href="https://github.com/rust-lang/team/pull/2115">#2115</a>: Fix script for finding publishable package</li>
  <li><a href="https://github.com/rust-lang/team/pull/2118">#2118</a>: Add trusted publishing to <code class="language-plaintext highlighter-rouge">cc-rs</code></li>
  <li><a href="https://github.com/rust-lang/team/pull/2133">#2133</a>: Add Zulip ID for U007D</li>
  <li><a href="https://github.com/rust-lang/team/pull/2134">#2134</a>: Allow configuring “trusted publishing only” config for crates</li>
  <li><a href="https://github.com/rust-lang/team/pull/2139">#2139</a>: Add environment to <code class="language-plaintext highlighter-rouge">annotate-snippets</code></li>
  <li><a href="https://github.com/rust-lang/team/pull/2151">#2151</a>: Sync crate owners on crates.io</li>
  <li><a href="https://github.com/rust-lang/team/pull/2154">#2154</a>: Fix outputs of <code class="language-plaintext highlighter-rouge">generate-tokens</code> reusable action</li>
  <li><a href="https://github.com/rust-lang/team/pull/2156">#2156</a>: Run crates.io sync in dry run</li>
  <li><a href="https://github.com/rust-lang/team/pull/2157">#2157</a>: Test crates.io dry-run (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/team/pull/2158">#2158</a>: Actually run crates-io sync in dry run workflow</li>
  <li><a href="https://github.com/rust-lang/team/pull/2160">#2160</a>: Batch load trusted publishing configs and remove unused ones</li>
  <li><a href="https://github.com/rust-lang/team/pull/2161">#2161</a>: Add a <code class="language-plaintext highlighter-rouge">funding</code> repository</li>
  <li><a href="https://github.com/rust-lang/team/pull/2162">#2162</a>: Backfill all crates.io crates (<span style="color: green;">open</span>)</li>
  <li><a href="https://github.com/rust-lang/team/pull/2172">#2172</a>: Set <code class="language-plaintext highlighter-rouge">CRATES_IO_USERNAME</code> environment variable in dry-run</li>
  <li><a href="https://github.com/rust-lang/team/pull/2173">#2173</a>: Add crates.io trusted publishing back to <code class="language-plaintext highlighter-rouge">ar_archive_writer</code></li>
  <li><a href="https://github.com/rust-lang/team/pull/2174">#2174</a>: Add crates.io trusted publishing back to <code class="language-plaintext highlighter-rouge">crates_io_og_image</code></li>
  <li><a href="https://github.com/rust-lang/team/pull/2175">#2175</a>: Print crate name in deleting TP diff</li>
  <li><a href="https://github.com/rust-lang/team/pull/2178">#2178</a>: Create a GitHub team and a Zulip group for the RFMF design committee</li>
  <li><a href="https://github.com/rust-lang/team/pull/2179">#2179</a>: Do not panic when a person is not found</li>
  <li><a href="https://github.com/rust-lang/team/pull/2180">#2180</a>: Track <code class="language-plaintext highlighter-rouge">gll</code> repository</li>
  <li><a href="https://github.com/rust-lang/team/pull/2202">#2202</a>: Add bors merge bot</li>
  <li><a href="https://github.com/rust-lang/team/pull/2203">#2203</a>: Configure bors for rust-lang/rust</li>
</ul>

<h3 id="rust-langbors-142-prs">rust-lang/bors (142 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/bors/pull/204">#204</a>: Respect lockfile of <code class="language-plaintext highlighter-rouge">sqlx-cli</code> in CI</li>
  <li><a href="https://github.com/rust-lang/bors/pull/216">#216</a>: Add universal synchronization in tests</li>
  <li><a href="https://github.com/rust-lang/bors/pull/225">#225</a>: Refactoring</li>
  <li><a href="https://github.com/rust-lang/bors/pull/228">#228</a>: Refactor storage of <code class="language-plaintext highlighter-rouge">TreeState</code> in the DB</li>
  <li><a href="https://github.com/rust-lang/bors/pull/229">#229</a>: Improve compilation performance</li>
  <li><a href="https://github.com/rust-lang/bors/pull/232">#232</a>: Use <code class="language-plaintext highlighter-rouge">sqlx::Encode</code> implementation for <code class="language-plaintext highlighter-rouge">GithubRepoName</code></li>
  <li><a href="https://github.com/rust-lang/bors/pull/237">#237</a>: Refactor tests</li>
  <li><a href="https://github.com/rust-lang/bors/pull/249">#249</a>: Update PR status in upsert query</li>
  <li><a href="https://github.com/rust-lang/bors/pull/251">#251</a>: Fix column migrations without <code class="language-plaintext highlighter-rouge">DEFAULT</code> clause and add a test for it</li>
  <li><a href="https://github.com/rust-lang/bors/pull/252">#252</a>: Update deps</li>
  <li><a href="https://github.com/rust-lang/bors/pull/253">#253</a>: Deduplicate some dependencies</li>
  <li><a href="https://github.com/rust-lang/bors/pull/261">#261</a>: Add helper methods for waiting for a PR condition</li>
  <li><a href="https://github.com/rust-lang/bors/pull/271">#271</a>: Add <code class="language-plaintext highlighter-rouge">IF NOT EXISTS</code> to repository creation migration</li>
  <li><a href="https://github.com/rust-lang/bors/pull/272">#272</a>: Remove redundant colon from “Not approved” PR info comment</li>
  <li><a href="https://github.com/rust-lang/bors/pull/273">#273</a>: Make check for cancelled workflows deterministic</li>
  <li><a href="https://github.com/rust-lang/bors/pull/278">#278</a>: Split refresh handlers</li>
  <li><a href="https://github.com/rust-lang/bors/pull/282">#282</a>: Update README</li>
  <li><a href="https://github.com/rust-lang/bors/pull/293">#293</a>: Make try build comments more compact</li>
  <li><a href="https://github.com/rust-lang/bors/pull/296">#296</a>: Update docs</li>
  <li><a href="https://github.com/rust-lang/bors/pull/297">#297</a>: Make <code class="language-plaintext highlighter-rouge">help</code> and <code class="language-plaintext highlighter-rouge">info</code> command output nicer</li>
  <li><a href="https://github.com/rust-lang/bors/pull/299">#299</a>: Use correct bot prefix in try build in progress comment</li>
  <li><a href="https://github.com/rust-lang/bors/pull/300">#300</a>: Parse PR comments as Markdown and ignore code and links</li>
  <li><a href="https://github.com/rust-lang/bors/pull/302">#302</a>: Upsert PRs into the database in the command handler, rather than in each command separately</li>
  <li><a href="https://github.com/rust-lang/bors/pull/307">#307</a>: Cancel previously running try build on <code class="language-plaintext highlighter-rouge">@bors try</code></li>
  <li><a href="https://github.com/rust-lang/bors/pull/308">#308</a>: Reload PR state from DB after each executed command</li>
  <li><a href="https://github.com/rust-lang/bors/pull/310">#310</a>: Drain previously seen notifications in <code class="language-plaintext highlighter-rouge">TestSyncMarker</code></li>
  <li><a href="https://github.com/rust-lang/bors/pull/311">#311</a>: Show parent commit SHA in try build completed message</li>
  <li><a href="https://github.com/rust-lang/bors/pull/313">#313</a>: Add http to link printed by bors</li>
  <li><a href="https://github.com/rust-lang/bors/pull/320">#320</a>: Add test for parsing try jobs with a comma and glob</li>
  <li><a href="https://github.com/rust-lang/bors/pull/335">#335</a>: Support Markdown emphasis (<code class="language-plaintext highlighter-rouge">*</code>) in the command parser</li>
  <li><a href="https://github.com/rust-lang/bors/pull/336">#336</a>: Disallow approving non open PRs</li>
  <li><a href="https://github.com/rust-lang/bors/pull/341">#341</a>: Add yet another test for MarkDown asterisk parsing</li>
  <li><a href="https://github.com/rust-lang/bors/pull/342">#342</a>: Add bot web URL to README</li>
  <li><a href="https://github.com/rust-lang/bors/pull/344">#344</a>: Handle Markdown paragraphs</li>
  <li><a href="https://github.com/rust-lang/bors/pull/345">#345</a>: Add suggestion to run help command when command parsing fails</li>
  <li><a href="https://github.com/rust-lang/bors/pull/346">#346</a>: Temporarily partially revert 338</li>
  <li><a href="https://github.com/rust-lang/bors/pull/347">#347</a>: Re-enable PR checks</li>
  <li><a href="https://github.com/rust-lang/bors/pull/348">#348</a>: Remove multi-line logs</li>
  <li><a href="https://github.com/rust-lang/bors/pull/352">#352</a>: Parse <code class="language-plaintext highlighter-rouge">@bors try</code> commands to replace homu for try builds</li>
  <li><a href="https://github.com/rust-lang/bors/pull/354">#354</a>: Add tests for empty reviewer in approval command</li>
  <li><a href="https://github.com/rust-lang/bors/pull/359">#359</a>: Add more logging around check suites</li>
  <li><a href="https://github.com/rust-lang/bors/pull/360">#360</a>: Improve delegate comments</li>
  <li><a href="https://github.com/rust-lang/bors/pull/362">#362</a>: Refactor test suite to handle timeouts in a more robust way</li>
  <li><a href="https://github.com/rust-lang/bors/pull/363">#363</a>: Detect completed builds based only on workflow completion events</li>
  <li><a href="https://github.com/rust-lang/bors/pull/364">#364</a>: Strip PR description from merge message in try builds</li>
  <li><a href="https://github.com/rust-lang/bors/pull/365">#365</a>: Show a sample of failed jobs when a build fails</li>
  <li><a href="https://github.com/rust-lang/bors/pull/366">#366</a>: Do not allow approving WIP PRs</li>
  <li><a href="https://github.com/rust-lang/bors/pull/367">#367</a>: Forbid approving PRs with labels blocking approval</li>
  <li><a href="https://github.com/rust-lang/bors/pull/368">#368</a>: Remove repository name from merge commit message</li>
  <li><a href="https://github.com/rust-lang/bors/pull/372">#372</a>: Show information about auto build in <code class="language-plaintext highlighter-rouge">@bors info</code></li>
  <li><a href="https://github.com/rust-lang/bors/pull/373">#373</a>: Include timeout duration in <code class="language-plaintext highlighter-rouge">Test timed out</code> comment</li>
  <li><a href="https://github.com/rust-lang/bors/pull/374">#374</a>: Configure web URL for bors and show it in approved commit message</li>
  <li><a href="https://github.com/rust-lang/bors/pull/375">#375</a>: Forbid unapproving closed PRs</li>
  <li><a href="https://github.com/rust-lang/bors/pull/376">#376</a>: Improve web redirect</li>
  <li><a href="https://github.com/rust-lang/bors/pull/377">#377</a>: Reduce log clutter when there are no PRs for mergeability check</li>
  <li><a href="https://github.com/rust-lang/bors/pull/378">#378</a>: Run refresh handlers and merge queue after starting the bot</li>
  <li><a href="https://github.com/rust-lang/bors/pull/379">#379</a>: Make error message nicer when bors service panics in tests</li>
  <li><a href="https://github.com/rust-lang/bors/pull/380">#380</a>: Generalize bors prefix override</li>
  <li><a href="https://github.com/rust-lang/bors/pull/390">#390</a>: Improve logging around build cancellation</li>
  <li><a href="https://github.com/rust-lang/bors/pull/391">#391</a>: Clarify deployment instructions</li>
  <li><a href="https://github.com/rust-lang/bors/pull/392">#392</a>: Handle build cancellation errors properly</li>
  <li><a href="https://github.com/rust-lang/bors/pull/393">#393</a>: Unify logic for unapproving a DB</li>
  <li><a href="https://github.com/rust-lang/bors/pull/394">#394</a>: Run Clippy on tests in CI and fix test Clippy lints</li>
  <li><a href="https://github.com/rust-lang/bors/pull/398">#398</a>: Ignore <code class="language-plaintext highlighter-rouge">bors build finished</code> failed jobs</li>
  <li><a href="https://github.com/rust-lang/bors/pull/399">#399</a>: Remove unused .sqlx file</li>
  <li><a href="https://github.com/rust-lang/bors/pull/400">#400</a>: Include commit SHA in build failed comment</li>
  <li><a href="https://github.com/rust-lang/bors/pull/401">#401</a>: Fix flakiness of <code class="language-plaintext highlighter-rouge">enqueue_prs_on_pr_opened</code></li>
  <li><a href="https://github.com/rust-lang/bors/pull/402">#402</a>: Refactor test suite</li>
  <li><a href="https://github.com/rust-lang/bors/pull/407">#407</a>: Implement general retry mechanism</li>
  <li><a href="https://github.com/rust-lang/bors/pull/412">#412</a>: Ignore cancelled jobs in build failed comment</li>
  <li><a href="https://github.com/rust-lang/bors/pull/417">#417</a>: Make bors info build status more accurate</li>
  <li><a href="https://github.com/rust-lang/bors/pull/418">#418</a>: Hide previous try build started comment(s) on try build restart</li>
  <li><a href="https://github.com/rust-lang/bors/pull/419">#419</a>: Do not warn about pushes to approved PRs that have a failed auto build</li>
  <li><a href="https://github.com/rust-lang/bors/pull/420">#420</a>: Synchronize labels with homu</li>
  <li><a href="https://github.com/rust-lang/bors/pull/421">#421</a>: Add tests for <code class="language-plaintext highlighter-rouge">min_ci_time</code></li>
  <li><a href="https://github.com/rust-lang/bors/pull/422">#422</a>: Refeactor merge queue tests and change how the merge queue is executed</li>
  <li><a href="https://github.com/rust-lang/bors/pull/423">#423</a>: Document and refactor mergeability queue</li>
  <li><a href="https://github.com/rust-lang/bors/pull/424">#424</a>: Tiny merge queue improvements</li>
  <li><a href="https://github.com/rust-lang/bors/pull/427">#427</a>: Remove hardcoded master branch reference</li>
  <li><a href="https://github.com/rust-lang/bors/pull/433">#433</a>: Correctly finish successful auto builds when the tree is closed</li>
  <li><a href="https://github.com/rust-lang/bors/pull/437">#437</a>: Fix merge queue PR query</li>
  <li><a href="https://github.com/rust-lang/bors/pull/438">#438</a>: Reduce space taken by workflow URLs in try build started comment</li>
  <li><a href="https://github.com/rust-lang/bors/pull/443">#443</a>: Add a contributing section to README</li>
  <li><a href="https://github.com/rust-lang/bors/pull/444">#444</a>: Ensure that Cargo.lock is up-to-date on CI</li>
  <li><a href="https://github.com/rust-lang/bors/pull/447">#447</a>: Rename “extended logs” to “enhanced plaintext logs”</li>
  <li><a href="https://github.com/rust-lang/bors/pull/460">#460</a>: Check the correct mergeable state in merge queue sanity checks</li>
  <li><a href="https://github.com/rust-lang/bors/pull/461">#461</a>: Remove unneeded wrap in reapproval comment message</li>
  <li><a href="https://github.com/rust-lang/bors/pull/462">#462</a>: Sort PRs on the queue page</li>
  <li><a href="https://github.com/rust-lang/bors/pull/463">#463</a>: Make the queue PR table striped</li>
  <li><a href="https://github.com/rust-lang/bors/pull/470">#470</a>: Add test for making sure that example config is valid</li>
  <li><a href="https://github.com/rust-lang/bors/pull/471">#471</a>: Remove mention of LLD for faster compilation</li>
  <li><a href="https://github.com/rust-lang/bors/pull/475">#475</a>: Update development docs</li>
  <li><a href="https://github.com/rust-lang/bors/pull/476">#476</a>: Update Octocrab to 0.48</li>
  <li><a href="https://github.com/rust-lang/bors/pull/478">#478</a>: Allow leading hyphen in private key argument</li>
  <li><a href="https://github.com/rust-lang/bors/pull/481">#481</a>: Make refresh checks more robust</li>
  <li><a href="https://github.com/rust-lang/bors/pull/482">#482</a>: Add simple API JSON endpoint for merge queue pull requests</li>
  <li><a href="https://github.com/rust-lang/bors/pull/483">#483</a>: Unify help between GitHub comments and the webpage</li>
  <li><a href="https://github.com/rust-lang/bors/pull/488">#488</a>: Tag auto build started comments</li>
  <li><a href="https://github.com/rust-lang/bors/pull/489">#489</a>: Rename <code class="language-plaintext highlighter-rouge">stalled</code> to <code class="language-plaintext highlighter-rouge">failed</code></li>
  <li><a href="https://github.com/rust-lang/bors/pull/490">#490</a>: Add priority to mergeability queue</li>
  <li><a href="https://github.com/rust-lang/bors/pull/491">#491</a>: Add conflict reporting on PRs</li>
  <li><a href="https://github.com/rust-lang/bors/pull/492">#492</a>: Update Ubuntu to 24.04 in Docker and build with Rust 1.92</li>
  <li><a href="https://github.com/rust-lang/bors/pull/496">#496</a>: Restructure tests to split GitHub state and mocks better</li>
  <li><a href="https://github.com/rust-lang/bors/pull/497">#497</a>: Add initial infrastructure for rollup tests</li>
  <li><a href="https://github.com/rust-lang/bors/pull/498">#498</a>: Finish test infrastructure for rollups</li>
  <li><a href="https://github.com/rust-lang/bors/pull/499">#499</a>: Recover from more PR sanity checks in merge queue</li>
  <li><a href="https://github.com/rust-lang/bors/pull/500">#500</a>: Apply unapprove labels when unapproving a PR in the mergeability queue</li>
  <li><a href="https://github.com/rust-lang/bors/pull/501">#501</a>: Move some code around</li>
  <li><a href="https://github.com/rust-lang/bors/pull/502">#502</a>: Add a build completion queue</li>
  <li><a href="https://github.com/rust-lang/bors/pull/503">#503</a>: Reconcile DB and GitHub workflow state when completing try/auto builds</li>
  <li><a href="https://github.com/rust-lang/bors/pull/504">#504</a>: Test refactorings</li>
  <li><a href="https://github.com/rust-lang/bors/pull/505">#505</a>: Do not re-schedule mergeability check if a PR is not open anymore</li>
  <li><a href="https://github.com/rust-lang/bors/pull/507">#507</a>: Make less items public in tests and clean up some test APIs</li>
  <li><a href="https://github.com/rust-lang/bors/pull/508">#508</a>: Sort rollup PRs by priority</li>
  <li><a href="https://github.com/rust-lang/bors/pull/509">#509</a>: Post a notice that the tree is closed when approving PRs</li>
  <li><a href="https://github.com/rust-lang/bors/pull/511">#511</a>: Add more rollup tests</li>
  <li><a href="https://github.com/rust-lang/bors/pull/513">#513</a>: Synchronize waiting for open PRs</li>
  <li><a href="https://github.com/rust-lang/bors/pull/514">#514</a>: Add test for rolling up PRs with different base branches</li>
  <li><a href="https://github.com/rust-lang/bors/pull/515">#515</a>: Add test for rolling up too many rollups</li>
  <li><a href="https://github.com/rust-lang/bors/pull/516">#516</a>: Fetch rolled up PRs concurrently</li>
  <li><a href="https://github.com/rust-lang/bors/pull/517">#517</a>: Fix workflow result URL shown in try/auto build completed comments</li>
  <li><a href="https://github.com/rust-lang/bors/pull/518">#518</a>: Do not add or remove labels on PRs unnecessarily</li>
  <li><a href="https://github.com/rust-lang/bors/pull/520">#520</a>: Set rollup label manually, rather than depending on rustbot</li>
  <li><a href="https://github.com/rust-lang/bors/pull/521">#521</a>: Filter out duplicate PRs when making a rollup</li>
  <li><a href="https://github.com/rust-lang/bors/pull/522">#522</a>: Add “Create similar rollup” functionality</li>
  <li><a href="https://github.com/rust-lang/bors/pull/524">#524</a>: Add test for label optimization</li>
  <li><a href="https://github.com/rust-lang/bors/pull/525">#525</a>: Implement homu-compatible ignore blocks</li>
  <li><a href="https://github.com/rust-lang/bors/pull/526">#526</a>: Thank contributors in README</li>
  <li><a href="https://github.com/rust-lang/bors/pull/529">#529</a>: Implement pause/resume functionality</li>
  <li><a href="https://github.com/rust-lang/bors/pull/530">#530</a>: Require review permissions for pausing and resuming</li>
  <li><a href="https://github.com/rust-lang/bors/pull/531">#531</a>: Require review permissions for creating rollups</li>
  <li><a href="https://github.com/rust-lang/bors/pull/532">#532</a>: Fix some tracing spans</li>
  <li><a href="https://github.com/rust-lang/bors/pull/533">#533</a>: Increase global event interval periods</li>
  <li><a href="https://github.com/rust-lang/bors/pull/534">#534</a>: Improve design of queue page</li>
  <li><a href="https://github.com/rust-lang/bors/pull/535">#535</a>: Optimize queue page</li>
  <li><a href="https://github.com/rust-lang/bors/pull/536">#536</a>: Add a function to prepare Octocrab client</li>
  <li><a href="https://github.com/rust-lang/bors/pull/537">#537</a>: Do not show try build status in queue page</li>
  <li><a href="https://github.com/rust-lang/bors/pull/538">#538</a>: Return error instead of panicking when GitHub returns inconsistent state</li>
  <li><a href="https://github.com/rust-lang/bors/pull/539">#539</a>: Increase timeout for fetching non-closed PRs</li>
  <li><a href="https://github.com/rust-lang/bors/pull/540">#540</a>: Update required webhook events in docs</li>
  <li><a href="https://github.com/rust-lang/bors/pull/542">#542</a>: Update reqwest to 0.13</li>
  <li><a href="https://github.com/rust-lang/bors/pull/543">#543</a>: Update test snapshots to newer insta format</li>
</ul>

<h3 id="rust-langtriagebot-60-prs">rust-lang/triagebot (60 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1874">#1874</a>: Remove rustc-dev-guide as a submodule</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1891">#1891</a>: Improve query for getting users for review (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1892">#1892</a>: Add simple test infrastructure for testing DB queries</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1905">#1905</a>: Handle open/close PR events in PR tracking</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1906">#1906</a>: Store assigned PRs in memory</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1908">#1908</a>: Extend user loading from the DB</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1918">#1918</a>: Make pull request assignment loading more robust</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1919">#1919</a>: Implement setting review assignment limit</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1921">#1921</a>: Switch CI to use a merge queue</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1923">#1923</a>: Require <code class="language-plaintext highlighter-rouge">test</code> job to succeed before starting <code class="language-plaintext highlighter-rouge">deploy</code> job</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1925">#1925</a>: Use GitHub Actions cache to make deploys faster</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1930">#1930</a>: Revert “Use GitHub Actions cache to make deploys faster”</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1935">#1935</a>: Refactor assignment logic slightly</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1937">#1937</a>: Allow using r? even without the owners table</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1938">#1938</a>: Unify <code class="language-plaintext highlighter-rouge">@rustbot assign</code> and <code class="language-plaintext highlighter-rouge">r?</code> behavior on PRs (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1939">#1939</a>: Refactor and unify assignment logic</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1943">#1943</a>: Allow review requesting ghost</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1946">#1946</a>: Run migrations after loading the workqueue</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1947">#1947</a>: Take review preferences into account when determining reviewers</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1952">#1952</a>: Only consider PRs with certain labels to be in the reviewer workqueue</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1961">#1961</a>: Fix assigned PR count in Zulip message</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1963">#1963</a>: Do not perform PR assignment for draft PRs without r?</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1968">#1968</a>: Do not apply labels in autolabel when opening a draft PR</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1969">#1969</a>: Handle drafts better in PR tracking and do not consider self-assigned PRs for the workqueue</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1970">#1970</a>: Store vacation status in the database</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1972">#1972</a>: Determine whether reviewers were requested directly or not</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1975">#1975</a>: Do not write <code class="language-plaintext highlighter-rouge">IssueData</code> to the DB when it is unchanged</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1976">#1976</a>: Add test for parsing <code class="language-plaintext highlighter-rouge">assign.review_prefs</code></li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1977">#1977</a>: Take rotation mode into account when performing assignments</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/1984">#1984</a>: Refactor PR detection in autolabel</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2014">#2014</a>: Always allow direct review requests</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2015">#2015</a>: Add <code class="language-plaintext highlighter-rouge">whoami</code> Zulip command</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2023">#2023</a>: Only post suppressed assignment warning when assignment succeeds</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2025">#2025</a>: Add <code class="language-plaintext highlighter-rouge">lookup [github|zulip] &lt;username&gt;</code> command for determining the GitHub/Zulip username mapping</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2028">#2028</a>: Simplify implementation of <code class="language-plaintext highlighter-rouge">lookup zulip</code></li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2029">#2029</a>: Fix Zulip username formatting in <code class="language-plaintext highlighter-rouge">lookup github</code></li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2039">#2039</a>: Add possibility to skip workqueue loading</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2040">#2040</a>: Do not post welcome message when no reviewer is found without fallback</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2044">#2044</a>: Add a Zulip client API</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2046">#2046</a>: Replace structopt with clap</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2047">#2047</a>: Update dependencies</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2049">#2049</a>: Parse Zulip commands with <code class="language-plaintext highlighter-rouge">clap</code></li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2050">#2050</a>: Do not notify users when a non-sensitive command is executed on their behalf</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2052">#2052</a>: Remove unused code</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2053">#2053</a>: Store PR title in the workqueue and output it in <code class="language-plaintext highlighter-rouge">work show</code></li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2054">#2054</a>: Put review capacity into backticks</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2055">#2055</a>: Add team API client and cache team API calls</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2057">#2057</a>: Improve formatting of <code class="language-plaintext highlighter-rouge">work show</code></li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2059">#2059</a>: Fix command impersonation on Zulip</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2060">#2060</a>: Fix self-assign fast path</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2061">#2061</a>: Add <code class="language-plaintext highlighter-rouge">team-stats</code> Zulip command</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2063">#2063</a>: Make it possible to run <code class="language-plaintext highlighter-rouge">docs-update</code> and <code class="language-plaintext highlighter-rouge">ping-goals</code> also through DMs</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2081">#2081</a>: Return JSON content type in Zulip webhook responses</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2132">#2132</a>: Allow opting out of workqueue tracking with a label</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2142">#2142</a>: Log axum anyhow errors using <code class="language-plaintext highlighter-rouge">Debug</code> impl</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2174">#2174</a>: Do not hardcode the “master” branch</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2219">#2219</a>: Add <code class="language-plaintext highlighter-rouge">@rustbot reroll</code> command</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2231">#2231</a>: Store tokens as secret strings</li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2238">#2238</a>: Update <code class="language-plaintext highlighter-rouge">team</code></li>
  <li><a href="https://github.com/rust-lang/triagebot/pull/2240">#2240</a>: Modify no reviewer available error</li>
</ul>

<h3 id="rust-langrustc-dev-guide-35-prs">rust-lang/rustc-dev-guide (35 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2193">#2193</a>: Update (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2195">#2195</a>: Perform the first josh pull</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2197">#2197</a>: Add rustc-dev-guide to the list of repositories managed by josh</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2200">#2200</a>: Rustc pull</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2201">#2201</a>: Rustc pull (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2202">#2202</a>: Add a CI workflow for performing rustc-pull automatically</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2214">#2214</a>: Add portable SIMD to list of subtrees</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2215">#2215</a>: rustc pull</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2216">#2216</a>: Send a message to Zulip when a sync finishes</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2217">#2217</a>: Add <code class="language-plaintext highlighter-rouge">@bors rollup=never</code> to rustc-push PR body</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2220">#2220</a>: Revert “Add <code class="language-plaintext highlighter-rouge">@bors rollup=never</code> to rustc-push PR body”</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2226">#2226</a>: Fix rustc-pull CI’s bash commands</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2234">#2234</a>: Make rustc pulls on CI more frequent</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2235">#2235</a>: Rewrite section on executing Docker tests</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2237">#2237</a>: Pass <code class="language-plaintext highlighter-rouge">GITHUB_TOKEN</code> to Zulip CI step</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2239">#2239</a>: Checkout repository sources in rustc-pull CI action</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2243">#2243</a>: Make the rustc-pull workflow run less often</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2262">#2262</a>: Fix posting message to Zulip</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2286">#2286</a>: Fix MCP links</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2294">#2294</a>: Add Fuchsia ping group page and mention Fuchsia and RfL ping groups in integration test pages</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2405">#2405</a>: Remove mentions of rust-lang-ci/rust</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2489">#2489</a>: Rustc pull update</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2490">#2490</a>: Rustc pull update (old tooling) (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2491">#2491</a>: Finish transition to <code class="language-plaintext highlighter-rouge">rustc-josh-sync</code></li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2495">#2495</a>: Mention that stdarch is managed by josh-sync</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2501">#2501</a>: Mention that compiler-builtins is now using <code class="language-plaintext highlighter-rouge">rustc-josh-sync</code></li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2502">#2502</a>: Migrate rustc-pull to CI workflow from <code class="language-plaintext highlighter-rouge">josh-sync</code></li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2517">#2517</a>: Update josh sync documentation</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2521">#2521</a>: Authenticate using GitHub app for the sync workflow</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2523">#2523</a>: Use main branch of josh-sync for CI workflow</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2525">#2525</a>: Remove outdated ci.py reference</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2540">#2540</a>: Only run the pull workflow once per week</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2589">#2589</a>: Clarify that backtick escaping doesn’t work for <code class="language-plaintext highlighter-rouge">@bors try jobs</code></li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2609">#2609</a>: Add debug assertions flag to <code class="language-plaintext highlighter-rouge">cg_gcc</code> invocation</li>
  <li><a href="https://github.com/rust-lang/rustc-dev-guide/pull/2611">#2611</a>: Overhaul GCC codegen backend section</li>
</ul>

<h3 id="rust-langthis-week-in-rust-33-prs">rust-lang/this-week-in-rust (33 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/6260">#6260</a>: Add 2025-01-07 perf triage</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/6289">#6289</a>: Add link to <code class="language-plaintext highlighter-rouge">Async Rust is about concurrency, not (just) performance</code> blog post</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/6327">#6327</a>: Add 2025-01-27 perf triage</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/6392">#6392</a>: Add 2025-02-18 perf triage</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/6407">#6407</a>: Fold list of merged pull requests (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/6408">#6408</a>: Add blog post about nasty bug (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/6456">#6456</a>: Add 2025-03-11 perf triage</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/6498">#6498</a>: Add a “Just write a test for it” blog post</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/6576">#6576</a>: Add <code class="language-plaintext highlighter-rouge">Two ways of interpreting visibility in Rust</code> blog post (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/6648">#6648</a>: Add <code class="language-plaintext highlighter-rouge">Evolution of Rust compiler errors</code> blog post</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/6657">#6657</a>: Add 2025-05-20 perf triage</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/6658">#6658</a>: Add <code class="language-plaintext highlighter-rouge">Disable debuginfo to improve Rust compile times</code> blog post</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/6697">#6697</a>: Add Reducing Cargo target directory size with -Zno-embed-metadata blog post link</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/6722">#6722</a>: Add <code class="language-plaintext highlighter-rouge">Why doesn't Rust care more about compiler performance?</code> blog post (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/6740">#6740</a>: Add Rust Compiler Performance Survey 2025</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/6748">#6748</a>: Add 2025-06-17 perf triage</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/6818">#6818</a>: Add 2025-07-15 perf triage</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/6890">#6890</a>: Add 2025-08-12 perf triage</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/6966">#6966</a>: Add <code class="language-plaintext highlighter-rouge">Combining struct literal syntax with read-only field access</code> blog post</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/6972">#6972</a>: Add 2025-09-02 perf triage</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/6973">#6973</a>: Add <code class="language-plaintext highlighter-rouge">Adding #[derive(From)] to Rust</code> blog post</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/6999">#6999</a>: Add <code class="language-plaintext highlighter-rouge">Rust compiler performance survey 2025 results</code> blog post</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/7048">#7048</a>: Add <code class="language-plaintext highlighter-rouge">Reducing binary size of (Rust) programs with debuginfo</code> blog post</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/7049">#7049</a>: Add <code class="language-plaintext highlighter-rouge">Variadic Generics Micro Survey</code> official Inside Rust blog post</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/7055">#7055</a>: Add 2025-09-23 perf triage</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/7130">#7130</a>: Add 2025-10-13 perf triage</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/7222">#7222</a>: Add 2025-11-03 perf triage</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/7278">#7278</a>: Add <code class="language-plaintext highlighter-rouge">Launching the 2025 State of Rust Survey</code> Rust blog post</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/7280">#7280</a>: Add <code class="language-plaintext highlighter-rouge">Google Summer of Code 2025 results</code> blog post</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/7313">#7313</a>: Add 2025-11-25 perf triage</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/7364">#7364</a>: Add <code class="language-plaintext highlighter-rouge">Making it easier to sponsor Rust contributors</code> blog post</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/7395">#7395</a>: Add 2025-12-16 perf triage</li>
  <li><a href="https://github.com/rust-lang/this-week-in-rust/pull/7435">#7435</a>: Add <code class="language-plaintext highlighter-rouge">Investigating and fixing a nasty clone bug</code> blog post</li>
</ul>

<h3 id="rust-langwwwrust-langorg-25-prs">rust-lang/www.rust-lang.org (25 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2152">#2152</a>: Include marker and unknown teams with a website section</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2174">#2174</a>: Add rendering of the web into a directory</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2189">#2189</a>: Fix CI trigger condition</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2190">#2190</a>: Fix endless redirect in index page</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2192">#2192</a>: Fix community redirect loop</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2193">#2193</a>: Remove Heroku/Rocket config and update docs</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2194">#2194</a>: Allow deploying the website manually and add cron job for deploy</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2198">#2198</a>: Disable Jekyll on Github Pages</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2201">#2201</a>: Install Clippy on CI</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2202">#2202</a>: Add archived teams to the website</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2208">#2208</a>: Make logos available under <code class="language-plaintext highlighter-rouge">/logos</code></li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2211">#2211</a>: Add a page with everyone in the Rust Project, including alumni</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2213">#2213</a>: Add a separate page for each Rust team member</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2226">#2226</a>: Unify logic for showing all Project Members and generating individual pages for each person</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2227">#2227</a>: Sort team members on the website</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2229">#2229</a>: Change what does an “active team” mean</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2230">#2230</a>: Use nicer URL for all project members page</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2231">#2231</a>: Extend the final section of a page to avoid white space</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2233">#2233</a>: Fix passing Zulip domain to team pages</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2236">#2236</a>: Add GitHub Sponsors link to person profile page</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2237">#2237</a>: Add a funding page that lists Project members with GitHub Sponsors</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2244">#2244</a>: Shuffle fundable people on the server</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2245">#2245</a>: Add Funding page navbar link (<span style="color: green;">open</span>)</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2249">#2249</a>: Generalize funding page</li>
  <li><a href="https://github.com/rust-lang/www.rust-lang.org/pull/2251">#2251</a>: Remove extra characters</li>
</ul>

<h3 id="rust-langblogrust-langorg-24-prs">rust-lang/blog.rust-lang.org (24 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1455">#1455</a>: Add 2024 Annual Rust survey announcement blog post</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1478">#1478</a>: Add 2024 State of Rust results link to the announcement post</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1493">#1493</a>: Add blog post about our participation in GSoC 2025</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1599">#1599</a>: Add GSoC 2025 selected projects blog post</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1600">#1600</a>: Fix auto completion of username from git</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1601">#1601</a>: Add autocompletion for team names and URLs</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1606">#1606</a>: Let people choose team label in blog generation</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1622">#1622</a>: Add Rust compiler performance 2025 survey blog post</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1671">#1671</a>: Make links in 1.89.0 blog post clickable</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1672">#1672</a>: Fix submodule path (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1690">#1690</a>: Make build of the blog much faster by only fetching section metadata</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1692">#1692</a>: Add Rust Compiler Performance 2025 Survey results</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1693">#1693</a>: Add link to the results to the original Compiler Performance Survey post</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1697">#1697</a>: Update compiler performance charts to render better without JS</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1699">#1699</a>: Add variadic generics survey announcement blog post</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1717">#1717</a>: Add blog post announcing the 2025 State of Rust survey</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1727">#1727</a>: Use correct image link in Clippy feature freeze end post</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1734">#1734</a>: Recommend users to rename their fork’s default branch</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1736">#1736</a>: Update instructions for renaming rust-lang/rust default branch</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1739">#1739</a>: Add a note that the rust-lang/rust default branch has happened</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1742">#1742</a>: Add GSoC 2025 results blog post</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1750">#1750</a>: Update team dependency</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1751">#1751</a>: Add blog post about the new Funding page</li>
  <li><a href="https://github.com/rust-lang/blog.rust-lang.org/pull/1773">#1773</a>: Add a post about what is maintenance (<span style="color: green;">open</span>)</li>
</ul>

<h3 id="rust-langjosh-sync-22-prs">rust-lang/josh-sync (22 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/josh-sync/pull/1">#1</a>: Implement pull and push synchronization scripts</li>
  <li><a href="https://github.com/rust-lang/josh-sync/pull/2">#2</a>: Rename tool to <code class="language-plaintext highlighter-rouge">rustc-josh-sync</code> and add a few fixes</li>
  <li><a href="https://github.com/rust-lang/josh-sync/pull/3">#3</a>: Add CI workflow and fix compilation</li>
  <li><a href="https://github.com/rust-lang/josh-sync/pull/4">#4</a>: Handle newline in <code class="language-plaintext highlighter-rouge">rust-version</code> and mention this repo in merge commit message</li>
  <li><a href="https://github.com/rust-lang/josh-sync/pull/6">#6</a>: Stream output of certain commands to the terminal</li>
  <li><a href="https://github.com/rust-lang/josh-sync/pull/7">#7</a>: Improve commit message of pushes</li>
  <li><a href="https://github.com/rust-lang/josh-sync/pull/8">#8</a>: Make <code class="language-plaintext highlighter-rouge">gh</code> PR creation prompt more accurate</li>
  <li><a href="https://github.com/rust-lang/josh-sync/pull/9">#9</a>: Add reusable workflow for performing rustc-pull on CI</li>
  <li><a href="https://github.com/rust-lang/josh-sync/pull/10">#10</a>: Handle empty diff more precisely</li>
  <li><a href="https://github.com/rust-lang/josh-sync/pull/11">#11</a>: Fix README CI example</li>
  <li><a href="https://github.com/rust-lang/josh-sync/pull/12">#12</a>: Allow configuring upstream repository for <code class="language-plaintext highlighter-rouge">pull</code></li>
  <li><a href="https://github.com/rust-lang/josh-sync/pull/14">#14</a>: Fix push command in README</li>
  <li><a href="https://github.com/rust-lang/josh-sync/pull/15">#15</a>: Allow overriding upstream commit to pull from</li>
  <li><a href="https://github.com/rust-lang/josh-sync/pull/16">#16</a>: Add a flag to exit with status code 0 if there is nothing to pull</li>
  <li><a href="https://github.com/rust-lang/josh-sync/pull/19">#19</a>: Use GitHub app for PR authentication</li>
  <li><a href="https://github.com/rust-lang/josh-sync/pull/20">#20</a>: Implement verbose flag</li>
  <li><a href="https://github.com/rust-lang/josh-sync/pull/21">#21</a>: Do not rollback branch state in case of a merge failure</li>
  <li><a href="https://github.com/rust-lang/josh-sync/pull/23">#23</a>: Add uptream diff link to pull merge commit message</li>
  <li><a href="https://github.com/rust-lang/josh-sync/pull/28">#28</a>: Implement post-pull commands</li>
  <li><a href="https://github.com/rust-lang/josh-sync/pull/29">#29</a>: Keep intermediate git state if <code class="language-plaintext highlighter-rouge">--allow-noop</code> was passed</li>
  <li><a href="https://github.com/rust-lang/josh-sync/pull/30">#30</a>: Keep all intermediate state with <code class="language-plaintext highlighter-rouge">--allow-noop</code></li>
  <li><a href="https://github.com/rust-lang/josh-sync/pull/32">#32</a>: Use more qualified git ref when pushing branch to josh (<span style="color: green;">open</span>)</li>
</ul>

<h3 id="rust-langsurveys-19-prs">rust-lang/surveys (19 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/surveys/pull/327">#327</a>: State of Rust 2024 report</li>
  <li><a href="https://github.com/rust-lang/surveys/pull/328">#328</a>: Add a guide for what to do after the State of Rust survey finishes</li>
  <li><a href="https://github.com/rust-lang/surveys/pull/331">#331</a>: Flip angle of bar chart text in mobile layout</li>
  <li><a href="https://github.com/rust-lang/surveys/pull/332">#332</a>: Add State of Rust 2024 results to FAQ</li>
  <li><a href="https://github.com/rust-lang/surveys/pull/337">#337</a>: Add compiler performance survey</li>
  <li><a href="https://github.com/rust-lang/surveys/pull/339">#339</a>: Allow using survey verifier for more survey kinds</li>
  <li><a href="https://github.com/rust-lang/surveys/pull/340">#340</a>: Add support for rating scale question</li>
  <li><a href="https://github.com/rust-lang/surveys/pull/343">#343</a>: Bootstrap questions for the 2025 annual survey</li>
  <li><a href="https://github.com/rust-lang/surveys/pull/344">#344</a>: Compiler performance survey analysis</li>
  <li><a href="https://github.com/rust-lang/surveys/pull/347">#347</a>: Add declarative macros attributes and derives to features to be stabilized</li>
  <li><a href="https://github.com/rust-lang/surveys/pull/348">#348</a>: Add question about error codes to the annual survey</li>
  <li><a href="https://github.com/rust-lang/surveys/pull/349">#349</a>: Change Windows 8 to Windows 8.1</li>
  <li><a href="https://github.com/rust-lang/surveys/pull/350">#350</a>: Add Rust Project contributor survey 2025</li>
  <li><a href="https://github.com/rust-lang/surveys/pull/351">#351</a>: Add Zed to editor question in annual survey</li>
  <li><a href="https://github.com/rust-lang/surveys/pull/356">#356</a>: Update translation guide</li>
  <li><a href="https://github.com/rust-lang/surveys/pull/360">#360</a>: Bootstrap State of Rust 2025 translations from SurveyHero</li>
  <li><a href="https://github.com/rust-lang/surveys/pull/365">#365</a>: Add support for ranking questions on SurveyHero</li>
  <li><a href="https://github.com/rust-lang/surveys/pull/367">#367</a>: Add support for input list questions</li>
  <li><a href="https://github.com/rust-lang/surveys/pull/384">#384</a>: Add safety critical survey</li>
</ul>

<h3 id="rust-langrust-forge-18-prs">rust-lang/rust-forge (18 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/rust-forge/pull/807">#807</a>: Add link to State of Rust FAQ to the surveys repo</li>
  <li><a href="https://github.com/rust-lang/rust-forge/pull/819">#819</a>: Remove mention of IP allowlist for bastion</li>
  <li><a href="https://github.com/rust-lang/rust-forge/pull/835">#835</a>: Clarify what enables r? in triagebot</li>
  <li><a href="https://github.com/rust-lang/rust-forge/pull/853">#853</a>: Document triagebot review queue tracking</li>
  <li><a href="https://github.com/rust-lang/rust-forge/pull/877">#877</a>: Document Zulip commands</li>
  <li><a href="https://github.com/rust-lang/rust-forge/pull/882">#882</a>: Fix link to MCP</li>
  <li><a href="https://github.com/rust-lang/rust-forge/pull/885">#885</a>: Add link checking on CI</li>
  <li><a href="https://github.com/rust-lang/rust-forge/pull/891">#891</a>: Extend guide for configuring legacy AWS access</li>
  <li><a href="https://github.com/rust-lang/rust-forge/pull/893">#893</a>: Make blacksmith optional</li>
  <li><a href="https://github.com/rust-lang/rust-forge/pull/894">#894</a>: Increase max width of content to 1000px</li>
  <li><a href="https://github.com/rust-lang/rust-forge/pull/896">#896</a>: Add “How to start contributing” page</li>
  <li><a href="https://github.com/rust-lang/rust-forge/pull/946">#946</a>: Replace hardcoded references to master</li>
  <li><a href="https://github.com/rust-lang/rust-forge/pull/972">#972</a>: Document <code class="language-plaintext highlighter-rouge">@rustbot reroll</code></li>
  <li><a href="https://github.com/rust-lang/rust-forge/pull/973">#973</a>: Update triagebot reroll docs</li>
  <li><a href="https://github.com/rust-lang/rust-forge/pull/974">#974</a>: Clarify maximum duration to invite members into a team</li>
  <li><a href="https://github.com/rust-lang/rust-forge/pull/976">#976</a>: Document that Zulip is the primary communication channel of the Project</li>
  <li><a href="https://github.com/rust-lang/rust-forge/pull/978">#978</a>: Document the t-all/private channel</li>
  <li><a href="https://github.com/rust-lang/rust-forge/pull/983">#983</a>: Remove outdated services from service infrastructure page</li>
</ul>

<h3 id="rust-langrust-analyzer-14-prs">rust-lang/rust-analyzer (14 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/rust-analyzer/pull/19582">#19582</a>: Distribute x64 and aarch64 Linux builds with PGO optimizations</li>
  <li><a href="https://github.com/rust-lang/rust-analyzer/pull/19583">#19583</a>: Do not perform PGO on Linux CI</li>
  <li><a href="https://github.com/rust-lang/rust-analyzer/pull/19585">#19585</a>: Allow training PGO on a custom crate and enable it Windows on CI</li>
  <li><a href="https://github.com/rust-lang/rust-analyzer/pull/19586">#19586</a>: Use a Docker container instead of Zig for building with old(er) glibc on x64 Linux</li>
  <li><a href="https://github.com/rust-lang/rust-analyzer/pull/19595">#19595</a>: Use PGO on Linux x64 builds</li>
  <li><a href="https://github.com/rust-lang/rust-analyzer/pull/19597">#19597</a>: Build aarch64 builds on CI with PGO</li>
  <li><a href="https://github.com/rust-lang/rust-analyzer/pull/19600">#19600</a>: Pin rustc used for the <code class="language-plaintext highlighter-rouge">proc-macro-src</code> CI job</li>
  <li><a href="https://github.com/rust-lang/rust-analyzer/pull/19602">#19602</a>: Use PGO for 32-bit ARM builds (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rust-analyzer/pull/19988">#19988</a>: Fix link to good first issues</li>
  <li><a href="https://github.com/rust-lang/rust-analyzer/pull/20280">#20280</a>: Switch to using josh-sync</li>
  <li><a href="https://github.com/rust-lang/rust-analyzer/pull/20282">#20282</a>: Add CI workflow for periodically performing josh pulls</li>
  <li><a href="https://github.com/rust-lang/rust-analyzer/pull/20330">#20330</a>: Configure triagebot to reopen bot PRs</li>
  <li><a href="https://github.com/rust-lang/rust-analyzer/pull/20335">#20335</a>: Use GH app for authenticating sync PRs</li>
  <li><a href="https://github.com/rust-lang/rust-analyzer/pull/20638">#20638</a>: Add a FAQ entry about RA and Cargo build lock/cache conflicts</li>
</ul>

<h3 id="rust-langstdarch-14-prs">rust-lang/stdarch (14 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/stdarch/pull/1828">#1828</a>: Remove stabilized features (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/stdarch/pull/1829">#1829</a>: Add lockfile</li>
  <li><a href="https://github.com/rust-lang/stdarch/pull/1833">#1833</a>: Add triagebot config for subtree syncs</li>
  <li><a href="https://github.com/rust-lang/stdarch/pull/1835">#1835</a>: Add Josh sync scripts</li>
  <li><a href="https://github.com/rust-lang/stdarch/pull/1842">#1842</a>: Rustc pull update (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/stdarch/pull/1843">#1843</a>: Remove <code class="language-plaintext highlighter-rouge">std_detect</code> dev dependency in <code class="language-plaintext highlighter-rouge">core_arch</code></li>
  <li><a href="https://github.com/rust-lang/stdarch/pull/1844">#1844</a>: Perform first rustc pull</li>
  <li><a href="https://github.com/rust-lang/stdarch/pull/1850">#1850</a>: Document that stdarch is managed by josh-sync</li>
  <li><a href="https://github.com/rust-lang/stdarch/pull/1853">#1853</a>: Perform the first rustc pull.. for the second time</li>
  <li><a href="https://github.com/rust-lang/stdarch/pull/1870">#1870</a>: Add rustc-pull CI automation workflow</li>
  <li><a href="https://github.com/rust-lang/stdarch/pull/1873">#1873</a>: [do not merge] Remove std_detect from CI (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/stdarch/pull/1883">#1883</a>: Rustc pull update</li>
  <li><a href="https://github.com/rust-lang/stdarch/pull/1891">#1891</a>: Use GitHub app for authenticating sync workflows</li>
  <li><a href="https://github.com/rust-lang/stdarch/pull/1950">#1950</a>: rustc-pull</li>
</ul>

<h3 id="rust-langsync-team-13-prs">rust-lang/sync-team (13 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/sync-team/pull/100">#100</a>: Improve CLI and make it possible to run on directory with JSON files</li>
  <li><a href="https://github.com/rust-lang/sync-team/pull/101">#101</a>: Only run the dry-run workflow in the rust-lang organization</li>
  <li><a href="https://github.com/rust-lang/sync-team/pull/103">#103</a>: [WIP] Refactor token generation logic (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/sync-team/pull/104">#104</a>: Refactor token generation logic (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/sync-team/pull/105">#105</a>: Change repository description to not be optional</li>
  <li><a href="https://github.com/rust-lang/sync-team/pull/106">#106</a>: Normalize order of branch protection checks</li>
  <li><a href="https://github.com/rust-lang/sync-team/pull/107">#107</a>: Fix deserialization of repository description</li>
  <li><a href="https://github.com/rust-lang/sync-team/pull/109">#109</a>: Switch CI to use a merge queue</li>
  <li><a href="https://github.com/rust-lang/sync-team/pull/112">#112</a>: [WIP] Testing GitHub deploy (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/sync-team/pull/113">#113</a>: Synchronization from GitHub (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/sync-team/pull/119">#119</a>: Store tokens as secret strings</li>
  <li><a href="https://github.com/rust-lang/sync-team/pull/127">#127</a>: Fix Zulip API request</li>
  <li><a href="https://github.com/rust-lang/sync-team/pull/132">#132</a>: Generalize organization handling in tests</li>
</ul>

<h3 id="rust-langsimpleinfra-10-prs">rust-lang/simpleinfra (10 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/simpleinfra/pull/689">#689</a>: Give kobzol access to bastion</li>
  <li><a href="https://github.com/rust-lang/simpleinfra/pull/696">#696</a>: Allow access to the new bors DB from bastion</li>
  <li><a href="https://github.com/rust-lang/simpleinfra/pull/738">#738</a>: Add configuration for the rustc-perf collector machine</li>
  <li><a href="https://github.com/rust-lang/simpleinfra/pull/743">#743</a>: Disable swap on rustc-perf</li>
  <li><a href="https://github.com/rust-lang/simpleinfra/pull/744">#744</a>: Install AWS CLI on the rustc-perf collector machines</li>
  <li><a href="https://github.com/rust-lang/simpleinfra/pull/745">#745</a>: Replace AWS CLI v2 with v1 on rustc-perf</li>
  <li><a href="https://github.com/rust-lang/simpleinfra/pull/748">#748</a>: Set default boot target to <code class="language-plaintext highlighter-rouge">multi-user</code></li>
  <li><a href="https://github.com/rust-lang/simpleinfra/pull/752">#752</a>: Pass web URL to bors</li>
  <li><a href="https://github.com/rust-lang/simpleinfra/pull/795">#795</a>: Add RDS access to the rustc-perf-two collector</li>
  <li><a href="https://github.com/rust-lang/simpleinfra/pull/869">#869</a>: Use <code class="language-plaintext highlighter-rouge">@bors</code> prefix for new bors (<span style="color: green;">open</span>)</li>
</ul>

<h3 id="rust-langcargo-9-prs">rust-lang/cargo (9 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/cargo/pull/15378">#15378</a>: Add support for <code class="language-plaintext highlighter-rouge">-Zembed-metadata</code></li>
  <li><a href="https://github.com/rust-lang/cargo/pull/15494">#15494</a>: Fix tracking issue template link</li>
  <li><a href="https://github.com/rust-lang/cargo/pull/15766">#15766</a>: Make timings graphs scalable to user’s window</li>
  <li><a href="https://github.com/rust-lang/cargo/pull/15780">#15780</a>: Add initial integration for <code class="language-plaintext highlighter-rouge">--json=timings</code> behing <code class="language-plaintext highlighter-rouge">-Zsection-timings</code></li>
  <li><a href="https://github.com/rust-lang/cargo/pull/15923">#15923</a>: Render individual compilation sections in <code class="language-plaintext highlighter-rouge">--timings</code> pipeline graph</li>
  <li><a href="https://github.com/rust-lang/cargo/pull/15924">#15924</a>: Add “Optimizing Build Performance” section to the Cargo book</li>
  <li><a href="https://github.com/rust-lang/cargo/pull/15970">#15970</a>: Add parallel frontend to the build performance guide</li>
  <li><a href="https://github.com/rust-lang/cargo/pull/15991">#15991</a>: Add alternative linker to the build performance guide</li>
  <li><a href="https://github.com/rust-lang/cargo/pull/16142">#16142</a>: Mention cargo wizard in build performance guide (<span style="color: green;">open</span>)</li>
</ul>

<h3 id="rust-langmeasureme-9-prs">rust-lang/measureme (9 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/measureme/pull/244">#244</a>: Add support for reading aggregated query cache hit counts</li>
  <li><a href="https://github.com/rust-lang/measureme/pull/245">#245</a>: Use merge queues for the repository</li>
  <li><a href="https://github.com/rust-lang/measureme/pull/246">#246</a>: Check <code class="language-plaintext highlighter-rouge">master</code> branch instead of <code class="language-plaintext highlighter-rouge">stable</code> in CI</li>
  <li><a href="https://github.com/rust-lang/measureme/pull/247">#247</a>: Document the release process</li>
  <li><a href="https://github.com/rust-lang/measureme/pull/248">#248</a>: Bump version to 12.0.2</li>
  <li><a href="https://github.com/rust-lang/measureme/pull/249">#249</a>: Configure crates.io publish using OIDC</li>
  <li><a href="https://github.com/rust-lang/measureme/pull/250">#250</a>: Add missing <code class="language-plaintext highlighter-rouge">id-token</code> permission</li>
  <li><a href="https://github.com/rust-lang/measureme/pull/252">#252</a>: Fix query cache hit aggregation</li>
  <li><a href="https://github.com/rust-lang/measureme/pull/253">#253</a>: Bump version to 12.0.3</li>
</ul>

<h3 id="rust-langgoogle-summer-of-code-8-prs">rust-lang/google-summer-of-code (8 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/google-summer-of-code/pull/13">#13</a>: Add AI notice</li>
  <li><a href="https://github.com/rust-lang/google-summer-of-code/pull/19">#19</a>: Remove obsolete projects</li>
  <li><a href="https://github.com/rust-lang/google-summer-of-code/pull/20">#20</a>: Update mentor of “Modernize libc” project</li>
  <li><a href="https://github.com/rust-lang/google-summer-of-code/pull/23">#23</a>: Add Zulip idea discussion links for new project ideas</li>
  <li><a href="https://github.com/rust-lang/google-summer-of-code/pull/27">#27</a>: Add idea about porting <code class="language-plaintext highlighter-rouge">stdarch</code> test suite</li>
  <li><a href="https://github.com/rust-lang/google-summer-of-code/pull/30">#30</a>: Add concurrent Rustup project idea</li>
  <li><a href="https://github.com/rust-lang/google-summer-of-code/pull/35">#35</a>: Add information about Google Summer of Code 2025 accepted projects</li>
  <li><a href="https://github.com/rust-lang/google-summer-of-code/pull/36">#36</a>: Add script for computing GSoC mentor reward distribution</li>
</ul>

<h3 id="rust-langrustup-7-prs">rust-lang/rustup (7 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/rustup/pull/4154">#4154</a>: Use ARM based runners for ARM CI targets</li>
  <li><a href="https://github.com/rust-lang/rustup/pull/4344">#4344</a>: Store dist manifest in JSON to improve load performance (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rustup/pull/4350">#4350</a>: Skip manifest loading if there are no components/targets to check</li>
  <li><a href="https://github.com/rust-lang/rustup/pull/4368">#4368</a>: Fix CI image names for downloading ARM and PowerPC artifacts</li>
  <li><a href="https://github.com/rust-lang/rustup/pull/4405">#4405</a>: Bump <code class="language-plaintext highlighter-rouge">toml</code> to 0.9</li>
  <li><a href="https://github.com/rust-lang/rustup/pull/4470">#4470</a>: Remove hardcoded dependency to the master branch</li>
  <li><a href="https://github.com/rust-lang/rustup/pull/4511">#4511</a>: Move the default branch from <code class="language-plaintext highlighter-rouge">master</code> to <code class="language-plaintext highlighter-rouge">main</code></li>
</ul>

<h3 id="rust-langcompiler-builtins-5-prs">rust-lang/compiler-builtins (5 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/compiler-builtins/pull/966">#966</a>: Switch to <code class="language-plaintext highlighter-rouge">rustc-josh-sync</code></li>
  <li><a href="https://github.com/rust-lang/compiler-builtins/pull/973">#973</a>: Add CI workflow for automatically performing subtree sync pulls</li>
  <li><a href="https://github.com/rust-lang/compiler-builtins/pull/975">#975</a>: Tell triagebot to reopen bot PRs to run CI on them</li>
  <li><a href="https://github.com/rust-lang/compiler-builtins/pull/978">#978</a>: Update <code class="language-plaintext highlighter-rouge">no-merges</code> PR title</li>
  <li><a href="https://github.com/rust-lang/compiler-builtins/pull/996">#996</a>: Switch to using a GH app for authenticating sync PRs</li>
</ul>

<h3 id="rust-langmiri-5-prs">rust-lang/miri (5 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/miri/pull/4198">#4198</a>: Remove GitHub job summaries</li>
  <li><a href="https://github.com/rust-lang/miri/pull/4490">#4490</a>: Use <code class="language-plaintext highlighter-rouge">josh-sync</code> instead of <code class="language-plaintext highlighter-rouge">miri-script</code> for Josh synchronization</li>
  <li><a href="https://github.com/rust-lang/miri/pull/4493">#4493</a>: Fix cronjob Zulip message</li>
  <li><a href="https://github.com/rust-lang/miri/pull/4505">#4505</a>: Use GH app for authenticating pull PRs</li>
  <li><a href="https://github.com/rust-lang/miri/pull/4602">#4602</a>: Use <code class="language-plaintext highlighter-rouge">rustc-josh-sync</code> merge commit message for pull PR description</li>
</ul>

<h3 id="rust-langci-mirrors-4-prs">rust-lang/ci-mirrors (4 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/ci-mirrors/pull/4">#4</a>: Validate that TOML files don’t contain name/URL/hash duplicates</li>
  <li><a href="https://github.com/rust-lang/ci-mirrors/pull/9">#9</a>: Add a command for adding entries to TOML files</li>
  <li><a href="https://github.com/rust-lang/ci-mirrors/pull/23">#23</a>: Mirror GCC 9.2.0 sources</li>
  <li><a href="https://github.com/rust-lang/ci-mirrors/pull/26">#26</a>: Mirror GCC 9.5.0 and add validation against paths starting with a slash</li>
</ul>

<h3 id="rust-langhomu-4-prs">rust-lang/homu (4 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/homu/pull/233">#233</a>: Move bors CI jobs from rust-lang-ci to rust-lang</li>
  <li><a href="https://github.com/rust-lang/homu/pull/234">#234</a>: Fix pinned dependencies on CI</li>
  <li><a href="https://github.com/rust-lang/homu/pull/236">#236</a>: Disable try builds</li>
  <li><a href="https://github.com/rust-lang/homu/pull/238">#238</a>: Generalize merge conflict message</li>
</ul>

<h3 id="rust-langinfra-team-4-prs">rust-lang/infra-team (4 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/infra-team/pull/228">#228</a>: Rename <code class="language-plaintext highlighter-rouge">master</code> to <code class="language-plaintext highlighter-rouge">main</code></li>
  <li><a href="https://github.com/rust-lang/infra-team/pull/237">#237</a>: Document triagebot and some smaller services</li>
  <li><a href="https://github.com/rust-lang/infra-team/pull/238">#238</a>: Fix toolstate link</li>
  <li><a href="https://github.com/rust-lang/infra-team/pull/241">#241</a>: Update rustc-perf deployment docs link (<span style="color: green;">open</span>)</li>
</ul>

<h3 id="rust-langcargo-bisect-rustc-3-prs">rust-lang/cargo-bisect-rustc (3 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/cargo-bisect-rustc/pull/381">#381</a>: Use <code class="language-plaintext highlighter-rouge">rust-lang/rust</code> instead of <code class="language-plaintext highlighter-rouge">rust-lang-ci/rust</code> for unrolled rollup build commit URLs</li>
  <li><a href="https://github.com/rust-lang/cargo-bisect-rustc/pull/388">#388</a>: Clarify documentation around usage of merge-base</li>
  <li><a href="https://github.com/rust-lang/cargo-bisect-rustc/pull/392">#392</a>: Remove hardcoded dependency on master</li>
</ul>

<h3 id="rust-langpromote-release-3-prs">rust-lang/promote-release (3 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/promote-release/pull/95">#95</a>: Remove hardcoded master references for rust-lang/rust</li>
  <li><a href="https://github.com/rust-lang/promote-release/pull/96">#96</a>: Do not hardcode the default branch of the blog repository</li>
  <li><a href="https://github.com/rust-lang/promote-release/pull/98">#98</a>: Trigger website redeploy after a stable version is published</li>
</ul>

<h3 id="rust-langrustc_codegen_cranelift-3-prs">rust-lang/rustc_codegen_cranelift (3 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/rustc_codegen_cranelift/pull/1614">#1614</a>: Rustc pull update (<span style="color: red;">closed</span>)</li>
  <li><a href="https://github.com/rust-lang/rustc_codegen_cranelift/pull/1617">#1617</a>: Integrate with Josh (<span style="color: green;">open</span>)</li>
  <li><a href="https://github.com/rust-lang/rustc_codegen_cranelift/pull/1618">#1618</a>: Update <code class="language-plaintext highlighter-rouge">rustup.sh</code> to use <code class="language-plaintext highlighter-rouge">rustc-josh-sync</code> (<span style="color: green;">open</span>)</li>
</ul>

<h3 id="rust-langthanks-3-prs">rust-lang/thanks (3 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/thanks/pull/81">#81</a>: Ignore enzyme submodule</li>
  <li><a href="https://github.com/rust-lang/thanks/pull/83">#83</a>: Normalize repository names</li>
  <li><a href="https://github.com/rust-lang/thanks/pull/86">#86</a>: Adapt to new rust-lang/rust default branch</li>
</ul>

<h3 id="tikvjemallocator-3-prs">tikv/jemallocator (3 PRs)</h3>
<ul>
  <li><a href="https://github.com/tikv/jemallocator/pull/119">#119</a>: Remove build directory once build of <code class="language-plaintext highlighter-rouge">jemalloc-sys</code> finishes</li>
  <li><a href="https://github.com/tikv/jemallocator/pull/120">#120</a>: Respect jobserver set by Cargo</li>
  <li><a href="https://github.com/tikv/jemallocator/pull/152">#152</a>: Reverse order of MAKEFLAGS priority</li>
</ul>

<h3 id="rust-langcalendar-2-prs">rust-lang/calendar (2 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/calendar/pull/88">#88</a>: Add panstromek to Performance triage rotation</li>
  <li><a href="https://github.com/rust-lang/calendar/pull/95">#95</a>: Remove rylev from performance triage rotation</li>
</ul>

<h3 id="rust-langcc-rs-2-prs">rust-lang/cc-rs (2 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/cc-rs/pull/1447">#1447</a>: Use <code class="language-plaintext highlighter-rouge">std::thread::available_parallelism</code> for determining the default number of jobs</li>
  <li><a href="https://github.com/rust-lang/cc-rs/pull/1619">#1619</a>: Add publish environment for publishing crate</li>
</ul>

<h3 id="rust-langrustc_codegen_gcc-2-prs">rust-lang/rustc_codegen_gcc (2 PRs)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/rustc_codegen_gcc/pull/614">#614</a>: Add CI success job</li>
  <li><a href="https://github.com/rust-lang/rustc_codegen_gcc/pull/619">#619</a>: Remove duplicated CI triggers</li>
</ul>

<h3 id="xampprockyoctocrab-1-pr">XAMPPRocky/octocrab (1 PR)</h3>
<ul>
  <li><a href="https://github.com/XAMPPRocky/octocrab/pull/842">#842</a>: Send body for retried requests</li>
</ul>

<h3 id="kennytmrustup-toolchain-install-master-1-pr">kennytm/rustup-toolchain-install-master (1 PR)</h3>
<ul>
  <li><a href="https://github.com/kennytm/rustup-toolchain-install-master/pull/64">#64</a>: Do not hardcode master branch name</li>
</ul>

<h3 id="release-plzrelease-plz-1-pr">release-plz/release-plz (1 PR)</h3>
<ul>
  <li><a href="https://github.com/release-plz/release-plz/pull/2493">#2493</a>: Clarify how to use trusted publishing</li>
</ul>

<h3 id="rust-crosscargo-zigbuild-1-pr">rust-cross/cargo-zigbuild (1 PR)</h3>
<ul>
  <li><a href="https://github.com/rust-cross/cargo-zigbuild/pull/334">#334</a>: Ignore <code class="language-plaintext highlighter-rouge">-znostart-stop-gc</code> linker flag</li>
</ul>

<h3 id="rust-lang-cirust-1-pr">rust-lang-ci/rust (1 PR)</h3>
<ul>
  <li><a href="https://github.com/rust-lang-ci/rust/pull/5">#5</a>: Mention rust-lang-ci/rust being unused</li>
</ul>

<h3 id="rust-langcompiler-team-1-pr">rust-lang/compiler-team (1 PR)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/compiler-team/pull/852">#852</a>: Fix link in MCP issue template</li>
</ul>

<h3 id="rust-langfunding-1-pr">rust-lang/funding (1 PR)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/funding/pull/1">#1</a>: Add Rust Foundation Maintainer Fund exploration document</li>
</ul>

<h3 id="rust-langgcc-1-pr">rust-lang/gcc (1 PR)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/gcc/pull/66">#66</a>: Use rust-lang mirror for downloading GCC dependencies</li>
</ul>

<h3 id="rust-langglob-1-pr">rust-lang/glob (1 PR)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/glob/pull/181">#181</a>: Cache filename for sorting in <code class="language-plaintext highlighter-rouge">fill_todo</code></li>
</ul>

<h3 id="rust-langleadership-council-1-pr">rust-lang/leadership-council (1 PR)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/leadership-council/pull/248">#248</a>: Add Rust Foundation Maintainer Fund Design committee</li>
</ul>

<h3 id="rust-langmdbook-1-pr">rust-lang/mdBook (1 PR)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/mdBook/pull/2965">#2965</a>: Add publish environment to deploy job</li>
</ul>

<h3 id="rust-langrfcs-1-pr">rust-lang/rfcs (1 PR)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/rfcs/pull/3809">#3809</a>: RFC: enable <code class="language-plaintext highlighter-rouge">derive(From)</code> for single-field structs</li>
</ul>

<h3 id="rust-langrust-clippy-1-pr">rust-lang/rust-clippy (1 PR)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/rust-clippy/pull/15542">#15542</a>: Remove profile from Cargo.toml (<span style="color: red;">closed</span>)</li>
</ul>

<h3 id="rust-langrust-log-analyzer-1-pr">rust-lang/rust-log-analyzer (1 PR)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/rust-log-analyzer/pull/88">#88</a>: Extract job documentation URL from logs</li>
</ul>

<h3 id="rust-langrustc-demangle-1-pr">rust-lang/rustc-demangle (1 PR)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/rustc-demangle/pull/83">#83</a>: Do not publish the <code class="language-plaintext highlighter-rouge">native-c</code> crate</li>
</ul>

<h3 id="rust-langrustfmt-1-pr">rust-lang/rustfmt (1 PR)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/rustfmt/pull/6720">#6720</a>: Rename default branch to main</li>
</ul>

<h3 id="rust-langthorin-1-pr">rust-lang/thorin (1 PR)</h3>
<ul>
  <li><a href="https://github.com/rust-lang/thorin/pull/44">#44</a>: Add publish environment</li>
</ul>

<h2 id="a-few-more-thoughts-on-my-2025-contributions">A few more thoughts on my 2025 contributions</h2>

<p>Looking back at the above list, I think I managed to do a lot of work. Even though after finally <a href="/research/2024/11/12/phd-postmortem.html">finishing my PhD</a> I had a lot of energy and time to work on Rust at the beginning of the year, due to some new family responsibilities, teaching at a university, and doing a lot of “non-technical” work (like surveys, mentoring or governance), I felt a bit unproductive in the second half of the year. At least in terms of actual coding, which is still something that I enjoy the most and that “feels” the best, even though other
 kinds of work are also important.</p>

<p>In fact, my personal Rust TODO list <em>grew</em> (not <em>shrank</em>) in 2025, which is a bit depressing. A large part of that was likely caused by me trying to finish initiatives already started year(s) ago, which I didn’t have the bandwidth to drive forward during my PhD studies, and so new ideas had to wait.</p>

<p>It is also clear that I maintain <del>perhaps</del> <em>definitely</em> too many projects and initiatives. I think that is a good
thing for the Rust Project, because I’m able to help maintain some things that would probably otherwise
be unmaintained or stuck. However, I’m not sure if it’s such a good thing for myself. Even though I think
that I’m relatively good at handling many initiatives at once<sup id="fnref:context-switching"><a href="#fn:context-switching" class="footnote" rel="footnote" role="doc-noteref">9</a></sup> and I’m used to context-switching many times per <del>day</del> hour, that doesn’t mean that I wouldn’t be (or at least feel) more productive and less stressed if I was working on a smaller set of projects at once. It is my short-to-medium-term goal to reduce
the number of Rust projects that I contribute to, so that I can focus on things that I find the most
impactful and most enjoyable for me to work on, such as compiler performance, which I didn’t get to
at all for the past few months. The hard part, as always, is to find people that would continue to
pass the torch so that things I “leave behind” don’t get stuck again.</p>

<p>One area where I’m thinking of scaling down my contributions is the <a href="https://rust-lang.org/governance/teams/launching-pad/#team-survey">Rust survey team</a>. I mostly only joined it because it seemed like no one else would do it at the time. And even though I created some scripts to partly automate survey analysis, doing surveys doesn’t exactly spark joy to me, so I would like to pass the torch to others. But it’s <a href="https://rust-lang.zulipchat.com/#narrow/channel/392734-council/topic/Onboarding.20people.20to.20teams.20with.20elevated.20privileges/with/558022595">tricky</a>…</p>

<h2 id="conclusion">Conclusion</h2>

<p>I hope you found this post interesting. I’m thinking of making this a regular yearly “look back” post
(for as long as I’m able to work on Rust), so maybe see you here in a year :)</p>

<p>And in case anyone is wondering, none of the PRs listed above were vibe-coded. I don’t use AI much
for normal development, although sometimes it is quite useful for creating one-off scripts, such as
those I used for gathering my GitHub statistics.</p>

<p>If you found some other interesting new year open-source data visualizations, let me know on <a href="https://www.reddit.com/r/rust/comments/1q4srtz/1160_prs_to_improve_rust_in_2025">Reddit</a>.</p>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:ecosystem">
      <p>It also includes a handful of contributions to the “Rust ecosystem”, i.e. Rust crates on crates.io that are outside the <code class="language-plaintext highlighter-rouge">rust-lang</code> organization. But there are not many of those, and I did not include PRs to my own crates, such as <a href="https://github.com/Kobzol/rust-delegate">delegate</a> or <a href="https://github.com/Kobzol/cargo-pgo">cargo-pgo</a>. <a href="#fnref:ecosystem" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:ratio">
      <p>It’s a bit funny how similar is the ratio of upstream Rust work in both my opened <em>and</em> reviewed PRs. <a href="#fnref:ratio" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:maintenance-blog">
      <p>I plan to blog Soon™ about my definition of software maintenance, either on this blog or on the official Rust blog. Stay tuned. <a href="#fnref:maintenance-blog" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:compiler-errors">
      <p>Although I know <a href="https://github.com/rust-lang/rust/pulls?q=is%3Amerged+is%3Apr+author%3Acompiler-errors+created%3A2024-01-01..2025-01-01+">people</a> who can :grin:. <a href="#fnref:compiler-errors" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:first-pr-2025">
      <p>The first one was <a href="https://github.com/rust-lang/rust/pull/134988">#134988</a>. On average, it is more than <code class="language-plaintext highlighter-rouge">28</code> PRs opened per day. <a href="#fnref:first-pr-2025" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:100-engineers">
      <p>I <a href="/rust/rustc/2025/06/09/why-doesnt-rust-care-more-about-compiler-performance.html">like to say</a> that if you gave me a hundred full-time engineers, I would find all of them something to work on in the Rust toolchain immediately. <a href="#fnref:100-engineers" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:fulltime">
      <p>On average, I spend around 70% of my work time on Rust, and I am funded for all that. <a href="#fnref:fulltime" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:proc-macro-stuck">
      <p>Which is sadly kind of stuck on finding more people that understand this part of the compiler (or on becoming such a person myself). <a href="#fnref:proc-macro-stuck" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:context-switching">
      <p>In reality, I probably suck at it the same as everyone else does, but I don’t know how to operate in any other way, so I don’t have a baseline to compare to. I also kind of suck at working on a single thing for a long time, which is probably why I cling to context switching so much, almost as a form of procrastination. <a href="#fnref:context-switching" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="rust" /><category term="rustc" /><summary type="html"><![CDATA[The end of the year is a time for reflection, so I thought that I should take a look back at the previous year. And since my main job is currently contributing to open-source, what better way to do that than enumerating all the Rust pull requests that I opened in 2025. In this post, I’ll share stats about my Rust contributions from the past year and also some thoughts about open-source (Rust) maintenance, as this topic is highly relevant right now.]]></summary></entry><entry><title type="html">Investigating and fixing a nasty clone bug</title><link href="https://kobzol.github.io/rust/2025/12/30/investigating-and-fixing-a-nasty-clone-bug.html" rel="alternate" type="text/html" title="Investigating and fixing a nasty clone bug" /><published>2025-12-30T14:00:00+00:00</published><updated>2025-12-30T14:00:00+00:00</updated><id>https://kobzol.github.io/rust/2025/12/30/investigating-and-fixing-a-nasty-clone-bug</id><content type="html" xml:base="https://kobzol.github.io/rust/2025/12/30/investigating-and-fixing-a-nasty-clone-bug.html"><![CDATA[<p>Recently I found myself battling with <a href="/rust/2025/02/23/tokio-plus-prctl-equals-nasty-bug.html">another</a> nasty bug. It took me several hours to understand what is happening, and once I found it, it turned out that the cause of the bug is relevant to the <a href="https://rust-lang.github.io/rust-project-goals/2025h2/ergonomic-rc.html">Ergonomic cloning</a> initiative that is currently being discussed a lot. So I thought that it would be a good candidate for a blog post.</p>

<h2 id="context">Context</h2>

<p>I’m currently trying to finally bring the <a href="https://github.com/rust-lang/bors">bors</a> GitHub merge bot to production, so I spent many days in the past weeks fixing various possible race conditions, making its implementation more robust and improving its test suite.</p>

<p>The <code class="language-plaintext highlighter-rouge">bors</code> test suite is mostly integration-based; it has very few what you could call “unit tests”. In most tests, we spawn the whole bors web application, connect it to a real running instance of Postgres, apply database migrations, send HTTP requests that emulate GitHub webhooks to it, and let it communicate over the network with a fake HTTP server that emulates GitHub endpoints<sup id="fnref:bors-testsuite"><a href="#fn:bors-testsuite" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>. The mocked HTTP GitHub endpoints are implemented using the cool <a href="https://docs.rs/wiremock/latest/wiremock/">wiremock</a> crate. A typical bors test looks something like this:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[sqlx::test]</span>
<span class="k">async</span> <span class="k">fn</span> <span class="nf">try_build_failed_modify_labels</span><span class="p">(</span><span class="n">pool</span><span class="p">:</span> <span class="nn">sqlx</span><span class="p">::</span><span class="n">PgPool</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">gh</span> <span class="o">=</span> <span class="nn">GitHub</span><span class="p">::</span><span class="nf">default</span><span class="p">()</span><span class="nf">.with_default_config</span><span class="p">(</span>
        <span class="s">r#"
[labels]
try_failed = ["+foo", "+bar", "-baz"]
"#</span><span class="p">,</span>
    <span class="p">);</span>
    <span class="nf">run_test</span><span class="p">((</span><span class="n">pool</span><span class="p">,</span> <span class="n">gh</span><span class="p">),</span> <span class="k">async</span> <span class="p">|</span><span class="n">ctx</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">BorsTester</span><span class="p">|</span> <span class="p">{</span>
        <span class="n">ctx</span><span class="nf">.post_comment</span><span class="p">(</span><span class="s">"@bors try"</span><span class="p">)</span><span class="k">.await</span><span class="o">?</span><span class="p">;</span>
        <span class="nn">insta</span><span class="p">::</span><span class="nd">assert_snapshot!</span><span class="p">(</span><span class="n">ctx</span><span class="nf">.get_next_comment_text</span><span class="p">(())</span><span class="k">.await</span><span class="o">?</span><span class="p">,</span> <span class="o">@</span><span class="s">r"
            :hourglass: Trying commit pr-1-sha with merge merge-0-pr-1…

            To cancel the try build, run the command `@bors try cancel`.
            "</span><span class="p">);</span>
        <span class="n">ctx</span><span class="nf">.pr</span><span class="p">(())</span><span class="k">.await</span><span class="nf">.expect_added_labels</span><span class="p">(</span><span class="o">&amp;</span><span class="p">[]);</span>
        <span class="n">ctx</span><span class="nf">.workflow_full_failure</span><span class="p">(</span><span class="n">ctx</span><span class="nf">.try_workflow</span><span class="p">())</span><span class="k">.await</span><span class="o">?</span><span class="p">;</span>
        <span class="n">ctx</span><span class="nf">.expect_comments</span><span class="p">((),</span> <span class="mi">1</span><span class="p">)</span><span class="k">.await</span><span class="p">;</span>
        <span class="n">ctx</span><span class="nf">.pr</span><span class="p">(())</span>
            <span class="k">.await</span>
            <span class="nf">.expect_added_labels</span><span class="p">(</span><span class="o">&amp;</span><span class="p">[</span><span class="s">"foo"</span><span class="p">,</span> <span class="s">"bar"</span><span class="p">])</span>
            <span class="nf">.expect_removed_labels</span><span class="p">(</span><span class="o">&amp;</span><span class="p">[</span><span class="s">"baz"</span><span class="p">]);</span>
        <span class="nf">Ok</span><span class="p">(())</span>
    <span class="p">})</span>
    <span class="k">.await</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="wheres-the-body">Where’s the body?</h2>

<p>While doing a particularly big refactoring, a few tests started failing in a very weird way, by panicking in one of the mocked GitHub <a href="https://github.com/rust-lang/bors/blob/main/src/tests/mock/repository.rs#L181">endpoints</a>. It was a <code class="language-plaintext highlighter-rouge">PATCH</code> endpoint that was trying to deserialize a JSON payload from the request body, but the body was empty, so the deserialization of course failed. That was very weird, because bors only sends a <code class="language-plaintext highlighter-rouge">PATCH</code> request at one place in its codebase, and there it does fill the request body. Also, the body was empty only <em>sometimes</em>, in some situations it was normally present. That’s usually the sign of a wonderful bughunt starting.</p>

<p>The receiving endpoint mocked with <code class="language-plaintext highlighter-rouge">wiremock</code> contained this deserialization code:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[derive(serde::Deserialize)]</span>
<span class="k">struct</span> <span class="n">SetRefRequest</span> <span class="p">{</span>
    <span class="n">sha</span><span class="p">:</span> <span class="nb">String</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">let</span> <span class="n">data</span><span class="p">:</span> <span class="n">SetRefRequest</span> <span class="o">=</span> <span class="n">req</span><span class="nf">.body_json</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">();</span> <span class="c1">// The test panicked here</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">bors</code> uses the <a href="https://github.com/XAMPPRocky/octocrab">octocrab</a> crate for communicating with GitHub. The code that is used to send the <code class="language-plaintext highlighter-rouge">PATCH</code> request looked like this:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">res</span> <span class="o">=</span> <span class="n">repo</span>
    <span class="nf">.client</span><span class="p">()</span>
    <span class="nf">._patch</span><span class="p">(</span>
        <span class="n">url</span><span class="nf">.as_str</span><span class="p">(),</span>
        <span class="nf">Some</span><span class="p">(</span><span class="o">&amp;</span><span class="nn">serde_json</span><span class="p">::</span><span class="nd">json!</span><span class="p">({</span>
            <span class="s">"sha"</span><span class="p">:</span> <span class="n">sha</span><span class="nf">.as_ref</span><span class="p">(),</span>
            <span class="s">"force"</span><span class="p">:</span> <span class="nd">matches!</span><span class="p">(</span><span class="n">force</span><span class="p">,</span> <span class="nn">ForcePush</span><span class="p">::</span><span class="n">Yes</span><span class="p">)</span>
        <span class="p">})),</span>
    <span class="p">)</span>
<span class="k">.await</span><span class="o">?</span><span class="p">;</span>
</code></pre></div></div>

<p>Well, <em>clearly</em> I was sending a body on the sender side (a JSON object with two fields, <code class="language-plaintext highlighter-rouge">sha</code> and <code class="language-plaintext highlighter-rouge">force</code>), and I was trying to deserialize a request type with the correct shape (the <code class="language-plaintext highlighter-rouge">sha</code> attribute) on the receiving side. But in some tests, and only after a few calls of the endpoint, the body was suddenly empty. That was super weird.</p>

<h2 id="investigation">Investigation</h2>

<p>My usual motto when dealing with weird bugs is “the problem is in your code, not in a dependency”, which is correct approximately 99% of the time. So I started debugging the failing tests, and the place where the request is sent in bors, but everything looked correct. I always seemed to send the request with a body, but sometimes it arrived without it. So I started suspecting that a problem might be in some dependency. I naturally suspected <code class="language-plaintext highlighter-rouge">wiremock</code> first, because it is a much less commonly used crate than <code class="language-plaintext highlighter-rouge">octocrab</code>, so I started digging into it. The issue tracker didn’t contain anything that would sound related, so I went back to the good ol’ debugger.</p>

<p>Debugging external Rust crates is not actually that easy. Rust is known for being very abstraction heavy (especially in library code), which can be particularly annoying for step-by-step debugging, and even more when it involves <code class="language-plaintext highlighter-rouge">async</code> code. Furthermore, if you want to modify the debugged code (e.g. to add more logging), it is not so easy as e.g. in Python, where I could just go to a virtual environment and modify the source code of the dependency manually. If I do that in Rust, Cargo won’t actually pick up that the source code of a crate in <code class="language-plaintext highlighter-rouge">$HOME/.cargo/registry/src/...</code> has been modified (and making modifications here is not a good idea anyway). So I <code class="language-plaintext highlighter-rouge">git clone</code>d <code class="language-plaintext highlighter-rouge">wiremock</code> and depended on it locally with <code class="language-plaintext highlighter-rouge">wiremock = { path = "..." }</code>, so that I could add more logging to it to see what’s going on<sup id="fnref:patch"><a href="#fn:patch" class="footnote" rel="footnote" role="doc-noteref">2</a></sup>.</p>

<p>But again, that did not end up with anything interesting. Once I went up the call stack, I saw that <code class="language-plaintext highlighter-rouge">wiremock</code> uses the <code class="language-plaintext highlighter-rouge">hyper</code> crate to create its HTTP server. And indeed, the <code class="language-plaintext highlighter-rouge">hyper</code> request arrived with an empty body (sometimes). I did not find it very probable that <code class="language-plaintext highlighter-rouge">hyper</code> would contain a bug that would just randomly remove request bodies, so I decided to take a look elsewhere.</p>

<p>I still wasn’t prepared to believe that this could be a bug in <code class="language-plaintext highlighter-rouge">octocrab</code>, so I pulled up <a href="https://www.wireshark.org/">wireshark</a>, to find a definitive proof whether the problem is in the sender or the receiver side. Luckily, the communication was on localhost via HTTP (without TLS), so investigating it with TCP/IP dumps was quite easy, and I finally found something useful. The HTTP stream looked something like this:</p>

<div class="language-http highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">PATCH</span> <span class="nn">/repos/a/b/git/refs/heads/main</span> <span class="k">HTTP</span><span class="o">/</span><span class="m">1.1</span>
<span class="na">x-github-api-version</span><span class="p">:</span> <span class="s">2022-11-28</span>
<span class="na">content-type</span><span class="p">:</span> <span class="s">application/json</span>
<span class="na">authorization</span><span class="p">:</span> <span class="s">Bearer foo</span>
<span class="na">user-agent</span><span class="p">:</span> <span class="s">octocrab</span>
<span class="na">host</span><span class="p">:</span> <span class="s">localhost:5555</span>
<span class="na">content-length</span><span class="p">:</span> <span class="s">28</span>

<span class="p">{</span><span class="nl">"sha"</span><span class="p">:</span><span class="s2">"foo"</span><span class="p">,</span><span class="w"> </span><span class="nl">"force"</span><span class="p">:</span><span class="kc">true</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<div class="language-http highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">500</span> <span class="ne">Internal Server Error</span>
<span class="na">content-length</span><span class="p">:</span> <span class="s">0</span>
</code></pre></div></div>

<div class="language-http highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">PATCH</span> <span class="nn">/repos/a/b/git/refs/heads/main</span> <span class="k">HTTP</span><span class="o">/</span><span class="m">1.1</span>
<span class="na">x-github-api-version</span><span class="p">:</span> <span class="s">2022-11-28</span>
<span class="na">content-type</span><span class="p">:</span> <span class="s">application/json</span>
<span class="na">authorization</span><span class="p">:</span> <span class="s">Bearer foo</span>
<span class="na">user-agent</span><span class="p">:</span> <span class="s">octocrab</span>
<span class="na">host</span><span class="p">:</span> <span class="s">localhost:5555</span>
</code></pre></div></div>

<div class="language-http highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">500</span> <span class="ne">Internal Server Error</span>
<span class="na">content-length</span><span class="p">:</span> <span class="s">0</span>
</code></pre></div></div>

<p>Notice something strange? The body is missing in the second request! So I finally knew for a fact that the sender caused the issue. So at this point, I had three things in mind:</p>

<ul>
  <li>Something else (other than my code) is somehow sending invalid HTTP requests to my mocked HTTP endpoints.</li>
  <li>My code contains UB.</li>
  <li>The problem is in <code class="language-plaintext highlighter-rouge">octocrab</code>.</li>
</ul>

<p>I found it quite hard to believe the first two options (especially since bors contains no <code class="language-plaintext highlighter-rouge">unsafe</code> code), so I finally set my sights on <code class="language-plaintext highlighter-rouge">octocrab</code>. I went to the place where I was creating the <code class="language-plaintext highlighter-rouge">PATCH</code> request and stepped through the internals of <code class="language-plaintext highlighter-rouge">octocrab</code>, but the body seemed to be there! Although it was not exactly trivial to check, because HTTP bodies are kind of difficult to inspect; in the general case, they can be streamed, so Rust networking APIs are usually built in a way where you can only consume the body once<sup id="fnref:foreshawdowing"><a href="#fn:foreshawdowing" class="footnote" rel="footnote" role="doc-noteref">3</a></sup>.</p>

<p>Since I still couldn’t find anything weird even in <code class="language-plaintext highlighter-rouge">octocrab</code>, I went back to the drawing board, and tried to approach the problem from a different viewpoint. After some more thinking, I asked myself: why is the request even being sent multiple times in the first place? Originally, I did not find that weird, because the failing tests were returning a 500 HTTP response from the mocked endpoint, to simulate a problem with GitHub, and bors implements request retrying, so it seemed like it was working as intended. However, after inspecting the test logs<sup id="fnref:traced-test"><a href="#fn:traced-test" class="footnote" rel="footnote" role="doc-noteref">4</a></sup> a bit more, I realized that my own retry code was not actually being triggered! That started sounding really suspicious - why was the request retried then?</p>

<p>After taking another look at the <code class="language-plaintext highlighter-rouge">octocrab</code> codebase, I realized that it actually implements its own mechanism for request retries, which I had no idea about. And it is enabled by <a href="https://github.com/XAMPPRocky/octocrab/blob/4ffda40672944f6d56f84113621df987f014b72b/Cargo.toml#L96">default</a>! And sure enough, when I disabled the retry mechanism:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">client</span> <span class="o">=</span> <span class="nn">Octocrab</span><span class="p">::</span><span class="nf">builder</span><span class="p">()</span>
    <span class="o">...</span>
    <span class="nf">.add_retry_config</span><span class="p">(</span><span class="nn">RetryConfig</span><span class="p">::</span><span class="nb">None</span><span class="p">)</span>
    <span class="nf">.build</span><span class="p">()</span>
</code></pre></div></div>

<p>The problem with the empty body was gone! :tada: So I finally fixed the problem on the bors side, and since bors was (unintentionally) doing double retries before, disabling the octocrab retry mechanism actually fixed two bugs at once.
I think that this was possibly the first time I found an actual bug in a Rust crate dependency that broke my code in ~7 years of using Rust. Pretty good track record for the Rust ecosystem in my experience so far!</p>

<p>But of course, I couldn’t just stop there. I <em>had</em> to know what is the original cause of the bug, so that I could report it and fix it.</p>

<h2 id="origin-of-the-bug">Origin of the bug</h2>

<p>Once I finally knew that the bug was caused by the <code class="language-plaintext highlighter-rouge">octocrab</code> retry mechanism, creating a minimum reproducible example was relatively easy:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">async</span> <span class="k">fn</span> <span class="nf">handler</span><span class="p">(</span><span class="n">body</span><span class="p">:</span> <span class="nb">String</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">impl</span> <span class="n">IntoResponse</span> <span class="p">{</span>
    <span class="nd">eprintln!</span><span class="p">(</span><span class="s">"Received data: {body}"</span><span class="p">);</span>
    <span class="nn">StatusCode</span><span class="p">::</span><span class="n">INTERNAL_SERVER_ERROR</span>
<span class="p">}</span>

<span class="nd">#[tokio::main]</span>
<span class="k">async</span> <span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">port</span> <span class="o">=</span> <span class="mi">5555</span><span class="p">;</span>
    <span class="nn">std</span><span class="p">::</span><span class="nn">thread</span><span class="p">::</span><span class="nf">spawn</span><span class="p">(</span><span class="k">move</span> <span class="p">||</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">runtime</span> <span class="o">=</span> <span class="nn">tokio</span><span class="p">::</span><span class="nn">runtime</span><span class="p">::</span><span class="nn">Builder</span><span class="p">::</span><span class="nf">new_multi_thread</span><span class="p">()</span>
            <span class="nf">.enable_all</span><span class="p">()</span>
            <span class="nf">.build</span><span class="p">()</span>
            <span class="nf">.unwrap</span><span class="p">();</span>
        <span class="n">runtime</span><span class="nf">.block_on</span><span class="p">(</span><span class="k">async</span> <span class="k">move</span> <span class="p">{</span>
            <span class="k">let</span> <span class="n">app</span> <span class="o">=</span> <span class="nn">Router</span><span class="p">::</span><span class="nf">new</span><span class="p">()</span><span class="nf">.route</span><span class="p">(</span><span class="s">"/foo"</span><span class="p">,</span> <span class="nf">patch</span><span class="p">(</span><span class="n">handler</span><span class="p">));</span>

            <span class="k">let</span> <span class="n">listener</span> <span class="o">=</span> <span class="nn">tokio</span><span class="p">::</span><span class="nn">net</span><span class="p">::</span><span class="nn">TcpListener</span><span class="p">::</span><span class="nf">bind</span><span class="p">(</span>
                <span class="nd">format!</span><span class="p">(</span><span class="s">"127.0.0.1:{port}"</span><span class="p">)</span>
            <span class="p">)</span>
                <span class="k">.await</span>
                <span class="nf">.unwrap</span><span class="p">();</span>
            <span class="nn">axum</span><span class="p">::</span><span class="nf">serve</span><span class="p">(</span><span class="n">listener</span><span class="p">,</span> <span class="n">app</span><span class="p">)</span><span class="k">.await</span><span class="nf">.unwrap</span><span class="p">();</span>
        <span class="p">});</span>
    <span class="p">});</span>

    <span class="k">let</span> <span class="n">client</span> <span class="o">=</span> <span class="nn">octocrab</span><span class="p">::</span><span class="nn">OctocrabBuilder</span><span class="p">::</span><span class="nf">new</span><span class="p">()</span>
        <span class="nf">.personal_token</span><span class="p">(</span><span class="s">"foo"</span><span class="p">)</span>
        <span class="c1">// .add_retry_config(RetryConfig::None) // &lt;- this fixes the issue</span>
        <span class="nf">.base_uri</span><span class="p">(</span><span class="nd">format!</span><span class="p">(</span><span class="s">"http://localhost:{port}"</span><span class="p">))</span>
        <span class="nf">.unwrap</span><span class="p">()</span>
        <span class="nf">.build</span><span class="p">()</span>
        <span class="nf">.unwrap</span><span class="p">();</span>

    <span class="nd">#[derive(serde::Serialize)]</span>
    <span class="k">struct</span> <span class="n">Request</span> <span class="p">{</span>
        <span class="n">force</span><span class="p">:</span> <span class="nb">bool</span><span class="p">,</span>
    <span class="p">}</span>
    <span class="k">let</span> <span class="n">response</span> <span class="o">=</span> <span class="n">client</span>
        <span class="nf">._patch</span><span class="p">(</span>
            <span class="s">"/foo"</span><span class="p">,</span>
            <span class="nf">Some</span><span class="p">(</span><span class="o">&amp;</span><span class="n">Request</span> <span class="p">{</span> <span class="n">force</span><span class="p">:</span> <span class="k">true</span> <span class="p">}),</span>
        <span class="p">)</span>
        <span class="k">.await</span>
        <span class="nf">.unwrap</span><span class="p">();</span>
    <span class="nd">eprintln!</span><span class="p">(</span><span class="s">"Response: {}"</span><span class="p">,</span> <span class="n">response</span><span class="nf">.status</span><span class="p">());</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This code printed the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Received data: {"force":true}
Received data: 
Received data: 
Received data: 
Response: 500 Internal Server Error
</code></pre></div></div>

<p>Exactly as in bors. When the request was sent for the first time, the body was correctly sent
over the wire. But when <code class="language-plaintext highlighter-rouge">octocrab</code> tried to retry the request, suddenly the body disappeared.</p>

<p>Originally, I did not notice this problem when debugging <code class="language-plaintext highlighter-rouge">octocrab</code>, because I sort of stopped too soon,
at a place where the HTTP request was passed from <code class="language-plaintext highlighter-rouge">octocrab</code> to a stack of <code class="language-plaintext highlighter-rouge">tower</code> middleware.
And at that point the request was still fine! But now it became clear that the problem was somewhere inside
that middleware, which is why it was so hard to find originally.</p>

<p>I wasn’t aware of how retrying works in <code class="language-plaintext highlighter-rouge">tower</code>, and if this is a bug in <code class="language-plaintext highlighter-rouge">octocrab</code>
or in <code class="language-plaintext highlighter-rouge">tower</code> (although now I suspected the former). After a bit of digging, I found that
retrying is indeed <a href="https://github.com/XAMPPRocky/octocrab/blob/35f78b5c07a20ea31f4b4648d1ecc65bf6b09863/src/service/middleware/retry.rs#L49">implemented</a> in <code class="language-plaintext highlighter-rouge">octocrab</code> with a retry <a href="https://docs.rs/tower/0.5.2/tower/retry/trait.Policy.html"><code class="language-plaintext highlighter-rouge">Policy</code></a>, which tells <code class="language-plaintext highlighter-rouge">tower</code> how to retry a given HTTP request. The most important part is the <code class="language-plaintext highlighter-rouge">clone_request</code> method:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">clone_request</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">req</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Request</span><span class="o">&lt;</span><span class="n">OctoBody</span><span class="o">&gt;</span><span class="p">)</span>
  <span class="k">-&gt;</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="n">Request</span><span class="o">&lt;</span><span class="n">OctoBody</span><span class="o">&gt;&gt;</span> <span class="p">{</span>
    <span class="k">match</span> <span class="k">self</span> <span class="p">{</span>
        <span class="nn">RetryConfig</span><span class="p">::</span><span class="nb">None</span> <span class="k">=&gt;</span> <span class="nb">None</span><span class="p">,</span>
        <span class="n">_</span> <span class="k">=&gt;</span> <span class="p">{</span>
            <span class="c1">// `Request` can't be cloned</span>
            <span class="k">let</span> <span class="k">mut</span> <span class="n">new_req</span> <span class="o">=</span> <span class="nn">Request</span><span class="p">::</span><span class="nf">builder</span><span class="p">()</span>
                <span class="nf">.uri</span><span class="p">(</span><span class="n">req</span><span class="nf">.uri</span><span class="p">())</span>
                <span class="nf">.method</span><span class="p">(</span><span class="n">req</span><span class="nf">.method</span><span class="p">())</span>
                <span class="nf">.version</span><span class="p">(</span><span class="n">req</span><span class="nf">.version</span><span class="p">());</span>
            <span class="k">for</span> <span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span> <span class="k">in</span> <span class="n">req</span><span class="nf">.headers</span><span class="p">()</span> <span class="p">{</span>
                <span class="n">new_req</span> <span class="o">=</span> <span class="n">new_req</span><span class="nf">.header</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">value</span><span class="p">);</span>
            <span class="p">}</span>

            <span class="k">let</span> <span class="n">body</span> <span class="o">=</span> <span class="n">req</span><span class="nf">.body</span><span class="p">()</span><span class="nf">.clone</span><span class="p">();</span>
            <span class="k">let</span> <span class="n">new_req</span> <span class="o">=</span> <span class="n">new_req</span><span class="nf">.body</span><span class="p">(</span><span class="n">body</span><span class="p">)</span><span class="nf">.expect</span><span class="p">(</span>
                <span class="s">"This should never panic,
                 as we are cloning a components from existing request"</span><span class="p">,</span>
            <span class="p">);</span>
            <span class="nf">Some</span><span class="p">(</span><span class="n">new_req</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Notice how the code says “Request can’t be cloned”, yet it clones the request body like it’s not a big
deal :thinking: That looked suspicious, so I looked at how the request body type looks like, at which point the
bug became obvious:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[derive(Debug)]</span>
<span class="k">pub</span> <span class="k">struct</span> <span class="nf">OctoBody</span><span class="p">(</span><span class="nb">Arc</span><span class="o">&lt;</span><span class="n">RwLock</span><span class="o">&lt;</span><span class="n">BoxBody</span><span class="o">&gt;&gt;</span><span class="p">);</span>
</code></pre></div></div>

<p>Here is the <code class="language-plaintext highlighter-rouge">Clone</code> impl, if you haven’t noticed the issue yet:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="nb">Clone</span> <span class="k">for</span> <span class="n">OctoBody</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">clone</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="nf">OctoBody</span><span class="p">(</span><span class="nn">Arc</span><span class="p">::</span><span class="nf">clone</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="na">.0</span><span class="p">))</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Yes, cloning the body just performs a shallow clone of an <code class="language-plaintext highlighter-rouge">Arc</code>, instead of performing a deep copy of
the whole body (!). And since the body is consumed when the request is sent for the first time, the code
simply started sending an empty body when the request was retried.</p>

<p>This is where the relation to the ergonomic cloning initiative comes in. If we had a clearer distinction
between performing a deep copy vs just cloning a (reference-counted) <a href="https://smallcultfollowing.com/babysteps/blog/2025/10/13/ergonomic-explicit-handles/">Handle</a> to something, it would likely be much more
obvious that there is a problem with the <code class="language-plaintext highlighter-rouge">.clone()</code> call.</p>

<p>It still seemed suspicious why this was a silent bug though. Shouldn’t it panic somewhere if we try to reuse the same body again? :thinking: If we take a look inside the <a href="https://docs.rs/http-body/1.0.1/http_body/trait.Body.html"><code class="language-plaintext highlighter-rouge">Body</code></a> trait <a href="https://github.com/XAMPPRocky/octocrab/blob/v0.48.1/src/body.rs#L107">implementation</a> of the <code class="language-plaintext highlighter-rouge">OctoBody</code>, we see that in the <code class="language-plaintext highlighter-rouge">poll_frame</code> method, it locks the <code class="language-plaintext highlighter-rouge">RwLock</code> and forwards the <code class="language-plaintext highlighter-rouge">poll_frame</code> call into its inner boxed body that it abstracts over:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="nn">http_body</span><span class="p">::</span><span class="n">Body</span> <span class="k">for</span> <span class="n">OctoBody</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Data</span> <span class="o">=</span> <span class="n">Bytes</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">Error</span> <span class="o">=</span> <span class="k">crate</span><span class="p">::</span><span class="n">Error</span><span class="p">;</span>

    <span class="nd">#[inline]</span>
    <span class="k">fn</span> <span class="nf">poll_frame</span><span class="p">(</span>
        <span class="k">self</span><span class="p">:</span> <span class="nb">Pin</span><span class="o">&lt;&amp;</span><span class="k">mut</span> <span class="k">Self</span><span class="o">&gt;</span><span class="p">,</span>
        <span class="n">cx</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">Context</span><span class="o">&lt;</span><span class="nv">'_</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="p">)</span> <span class="k">-&gt;</span> <span class="n">Poll</span><span class="o">&lt;</span><span class="nb">Option</span><span class="o">&lt;</span><span class="nb">Result</span><span class="o">&lt;</span><span class="n">Frame</span><span class="o">&lt;</span><span class="k">Self</span><span class="p">::</span><span class="n">Data</span><span class="o">&gt;</span><span class="p">,</span> <span class="k">Self</span><span class="p">::</span><span class="n">Error</span><span class="o">&gt;&gt;&gt;</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">inner</span> <span class="o">=</span> <span class="nn">Pin</span><span class="p">::</span><span class="nf">into_inner</span><span class="p">(</span><span class="k">self</span><span class="p">);</span>
        <span class="k">let</span> <span class="k">mut</span> <span class="n">boxed_body</span> <span class="o">=</span> <span class="n">inner</span><span class="na">.0</span><span class="nf">.write</span><span class="p">()</span>
            <span class="nf">.expect</span><span class="p">(</span><span class="s">"RwLock write lock failed"</span><span class="p">);</span>
        <span class="nn">Pin</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="o">*</span><span class="n">boxed_body</span><span class="p">)</span><span class="nf">.poll_frame</span><span class="p">(</span><span class="n">cx</span><span class="p">)</span> <span class="c1">// &lt;-- poll forwarding</span>
    <span class="p">}</span>
    <span class="o">...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>That sounds like it could cause an issue if the body is being polled even after it has been “consumed” while sending the request for the first time. You might think that if <code class="language-plaintext highlighter-rouge">OctoBody</code> stored an <code class="language-plaintext highlighter-rouge">RwLock&lt;Option&lt;...&gt;&gt;</code>, which would be replaced by <code class="language-plaintext highlighter-rouge">None</code> once the body is consumed, it would make this into a noticeable panic. But <code class="language-plaintext highlighter-rouge">OctoBody</code> actually already mostly does that! It usually stores a <a href="https://docs.rs/http-body-util/latest/http_body_util/struct.Full.html"><code class="language-plaintext highlighter-rouge">http_body_util::Full</code></a> struct as its boxed <code class="language-plaintext highlighter-rouge">Body</code> implementation, and that does <a href="https://docs.rs/http-body-util/0.1.3/src/http_body_util/full.rs.html#13">exactly that</a>. So why haven’t the code panicked, instead of sending an empty body in a retry?</p>

<p>Well, as it turns out, the <code class="language-plaintext highlighter-rouge">poll_frame</code> method is actually not ever called again once the request is consumed for the first time. When <code class="language-plaintext highlighter-rouge">hyper</code> <a href="https://github.com/hyperium/hyper/blob/v1.8.1/src/proto/h1/dispatch.rs#L339">polls the body</a>, it first uses the <a href="https://docs.rs/http-body/1.0.1/http_body/trait.Body.html"><code class="language-plaintext highlighter-rouge">Body::is_end_stream</code></a> method to check if the body is at its end, and if it is, it does not ever poll it again. And the <code class="language-plaintext highlighter-rouge">http_body_util::Full</code> implementation of course <a href="https://docs.rs/http-body-util/0.1.3/src/http_body_util/full.rs.html#47">returns <code class="language-plaintext highlighter-rouge">true</code></a> from this method once its inner body has been consumed. So when the body is consumed for the first time, it will start returning <code class="language-plaintext highlighter-rouge">true</code> from <code class="language-plaintext highlighter-rouge">is_end_stream</code>, and when we then try to reuse the same body in another request, <code class="language-plaintext highlighter-rouge">hyper</code> will just skip reading the body contents and send an empty body instead. Kind of footgunny if bodies are ever reused!</p>

<p>It is interesting to note that this could have caused an issue only because the <code class="language-plaintext highlighter-rouge">Arc</code> was combined
with interior mutability (<code class="language-plaintext highlighter-rouge">RwLock</code>). If there was no interior mutability, performing a shallow
(instead of a deep) clone would likely result at most in a performance degradation, but not a logical
bug. Interior mutability is useful, but it also relaxes some of the benefits we are normally
used to when using Rust!</p>

<p>It was also surprising to me that this bug has existed in the <code class="language-plaintext highlighter-rouge">octocrab</code> crate for more than <a href="https://github.com/XAMPPRocky/octocrab/pull/297">two years</a>,
but it does not seem like anyone noticed it. I suppose that sending a request to the GitHub API that
both has a body (so it is not a simple <code class="language-plaintext highlighter-rouge">GET</code> request) and that gets retried (so GitHub returned e.g. a 500 error
the first time the request was sent) is just not that common. It seems like my heavy usage of HTTP integration
testing in bors is uncovering all sorts of interesting issues :laughing:</p>

<p>The bug might look obvious in retrospect, but it took me several hours to debug it, and I was getting pretty desperate after that time. I’m certainly not used to spending so much time debugging a single issue when working with Rust! In hindsight, I should have tried to send the <code class="language-plaintext highlighter-rouge">PATCH</code> request manually, without <code class="language-plaintext highlighter-rouge">octocrab</code>. That would give me a hint much sooner that the problem is indeed within <code class="language-plaintext highlighter-rouge">octocrab</code>. I guess avoided it because of my usual thought of “the bug has to be in my code”. Well, the next time, I will probably start suspecting dependencies a bit sooner :sweat_smile:.</p>

<p>After finally understanding what was going on, I reported the issue in the octocrab <a href="https://github.com/XAMPPRocky/octocrab/issues/835">issue tracker</a> and went to sleep. It was a long day :zzz:.</p>

<h2 id="fixing-the-bug">Fixing the bug</h2>

<p>After I rested for a bit and regained enough motivation to take a look at <code class="language-plaintext highlighter-rouge">octocrab</code> again a few days later, I took a
shot at fixing the actual issue. The root of the issue was in performing a shallow copy, instead of
copying the whole body contents. That is actually easier said than done, as HTTP request bodies are not exactly
known to be easy to clone, as they could be streamed. However, since <code class="language-plaintext highlighter-rouge">octocrab</code> does not necessarily has
to be the most blazingly fast :rocket: zero-copy :fire: library under the sun, I figured that a bit of
copying in the common case won’t hurt that much.</p>

<p>What I did was to add a separate reference-counted copy of the request body, using the venerable <a href="https://docs.rs/bytes/1.11.0/bytes"><code class="language-plaintext highlighter-rouge">bytes</code></a> crate:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[derive(Debug)]</span>
<span class="k">pub</span> <span class="k">struct</span> <span class="n">OctoBody</span> <span class="p">{</span>
    <span class="n">body</span><span class="p">:</span> <span class="nb">Arc</span><span class="o">&lt;</span><span class="n">RwLock</span><span class="o">&lt;</span><span class="n">BoxBody</span><span class="o">&gt;&gt;</span><span class="p">,</span>
    <span class="c1">// Copy of the whole body, used for retrying requests</span>
    <span class="n">buffered</span><span class="p">:</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="n">Bytes</span><span class="o">&gt;</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">buffered</code> field is an <code class="language-plaintext highlighter-rouge">Option</code>, because in the most general case, the body might be streamed, in which case I can’t easily turn it into <code class="language-plaintext highlighter-rouge">Bytes</code> outside of an <code class="language-plaintext highlighter-rouge">async</code> context. However, at that case, we will now at least no longer send an empty body by mistake, but rather do not perform a retry at all.</p>

<p>In cases where the body is available in a buffered way (which I assume will be in 99% of cases when using <code class="language-plaintext highlighter-rouge">octocrab</code>), we copy the body contents into <code class="language-plaintext highlighter-rouge">Bytes</code> when wrapping it into <code class="language-plaintext highlighter-rouge">OctoBody</code>:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="nb">From</span><span class="o">&lt;</span><span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">u8</span><span class="o">&gt;&gt;</span> <span class="k">for</span> <span class="n">OctoBody</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">from</span><span class="p">(</span><span class="n">buf</span><span class="p">:</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">u8</span><span class="o">&gt;</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">buffered</span><span class="p">:</span> <span class="n">Bytes</span> <span class="o">=</span> <span class="nn">Bytes</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="n">buf</span><span class="nf">.clone</span><span class="p">());</span>
        <span class="k">Self</span><span class="p">::</span><span class="nf">create</span><span class="p">(</span><span class="nn">http_body_util</span><span class="p">::</span><span class="nn">Full</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="n">buf</span><span class="p">),</span> <span class="nf">Some</span><span class="p">(</span><span class="n">buffered</span><span class="p">))</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This pessimistically clones the body contents even if we never actually do a retry, but at least it makes the code work as it should. It might be better to only perform this copy if the <code class="language-plaintext highlighter-rouge">retry</code> feature of <code class="language-plaintext highlighter-rouge">octocrab</code> is enabled. Could be an interesting follow-up pull request, if you’re interested!</p>

<p>The final piece of the puzzle was adding a method for deep copying the body, which I called <code class="language-plaintext highlighter-rouge">try_clone</code>:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cd">/// Try to perform a deep clone of this body</span>
<span class="k">pub</span> <span class="k">fn</span> <span class="nf">try_clone</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="k">Self</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">self</span><span class="py">.buffered</span><span class="nf">.as_ref</span><span class="p">()</span><span class="nf">.map</span><span class="p">(|</span><span class="n">buffered</span><span class="p">|</span> <span class="p">{</span>
        <span class="k">Self</span><span class="p">::</span><span class="nf">create</span><span class="p">(</span>
            <span class="nn">http_body_util</span><span class="p">::</span><span class="nn">Full</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="n">buffered</span><span class="nf">.clone</span><span class="p">()),</span>
            <span class="nf">Some</span><span class="p">(</span><span class="n">buffered</span><span class="nf">.clone</span><span class="p">()),</span>
        <span class="p">)</span>
    <span class="p">})</span>
<span class="p">}</span>
</code></pre></div></div>

<p>and then using this method in the <code class="language-plaintext highlighter-rouge">clone_request</code> method:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">clone_request</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">req</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Request</span><span class="o">&lt;</span><span class="n">OctoBody</span><span class="o">&gt;</span><span class="p">)</span>
    <span class="k">-&gt;</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="n">Request</span><span class="o">&lt;</span><span class="n">OctoBody</span><span class="o">&gt;&gt;</span> <span class="p">{</span>
    <span class="k">match</span> <span class="k">self</span> <span class="p">{</span>
        <span class="nn">RetryConfig</span><span class="p">::</span><span class="nb">None</span> <span class="k">=&gt;</span> <span class="nb">None</span><span class="p">,</span>
        <span class="n">_</span> <span class="k">=&gt;</span> <span class="p">{</span>
            <span class="k">let</span> <span class="n">body</span> <span class="o">=</span> <span class="n">req</span><span class="nf">.body</span><span class="p">()</span><span class="nf">.try_clone</span><span class="p">()</span><span class="o">?</span><span class="p">;</span>
            <span class="o">...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Notice how <code class="language-plaintext highlighter-rouge">try_clone</code> returns an <code class="language-plaintext highlighter-rouge">Option</code>. If the request body cannot be cloned, the <code class="language-plaintext highlighter-rouge">clone_request</code>
method will also return <code class="language-plaintext highlighter-rouge">None</code>, and the request simply will not be retried. Which is much better than
sending an invalid request!</p>

<p>My fix was sent in <a href="https://github.com/XAMPPRocky/octocrab/pull/842">this</a> pull request, which was
merged soon after, and included in the <a href="https://github.com/XAMPPRocky/octocrab/releases/tag/v0.49.1">0.49.1</a> <code class="language-plaintext highlighter-rouge">octocrab</code> release. If you use <code class="language-plaintext highlighter-rouge">octocrab</code> and do not disable the automatic retry mechanism, you might want to update to it.</p>

<h2 id="bonus-could-llms-find-the-bug-sooner">Bonus: could LLMs find the bug (sooner)?</h2>

<p>While writing this blog post, I wondered if LLMs could actually find this bug sooner, and save me the hours of debugging pain.
I’m not a very skilled vibe-coder, but I do use Claude sometimes, so out of curiosity, I asked it to locate the bug.</p>

<p>First, I showed it the minimal reproducible example from my <code class="language-plaintext highlighter-rouge">octocrab</code> <a href="https://github.com/XAMPPRocky/octocrab/issues/835">issue</a>. It found out that the problem is with retries pretty much immediately, although with this example that was already quite obvious. After asking it to dig deeper, it also found out the exact cause of the underlying <code class="language-plaintext highlighter-rouge">clone</code> bug directly in <code class="language-plaintext highlighter-rouge">octocrab</code>. That was relatively impressive, although it also pointed me to the patched <code class="language-plaintext highlighter-rouge">0.49.1</code> release, and cited text that was suspiciously similar to my original issue and my PR. So it clearly saw my fix :laughing: And thus it’s unclear whether it would be able to find the issue even before I actually fixed it. Well, too late to test that now.</p>

<p>In a more realistic scenario, I reverted <code class="language-plaintext highlighter-rouge">bors</code> to a commit right before I disabled the retries, and asked Claude to try to find the issue there. At first, it produced some nonsense suggestions, such as claiming that I send an empty string for the <code class="language-plaintext highlighter-rouge">sha</code> field (which is quite different from sending an empty request body…). Then it kind of forgot that I’m using Rust and not C++ and claimed that the issue is a “subtle temporary lifetime problem with async/await”:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">I</span> <span class="n">think</span> <span class="n">I</span> <span class="n">found</span> <span class="n">the</span> <span class="nd">issue!</span> <span class="n">It</span><span class="nv">'s</span> <span class="n">a</span> <span class="n">subtle</span> <span class="n">temporary</span> <span class="n">lifetime</span> <span class="n">problem</span>
<span class="n">with</span> <span class="k">async</span><span class="o">/</span><span class="k">await</span> <span class="k">in</span> <span class="n">Rust</span><span class="py">.

Look</span> <span class="n">at</span> <span class="n">this</span> <span class="n">code</span> <span class="k">in</span> <span class="n">src</span><span class="o">/</span><span class="n">github</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">operations</span><span class="py">.rs</span><span class="p">:</span><span class="mi">172</span><span class="o">-</span><span class="mi">181</span><span class="p">:</span>

<span class="k">let</span> <span class="n">res</span> <span class="o">=</span> <span class="n">repo</span>
  <span class="nf">.client</span><span class="p">()</span>
  <span class="nf">._patch</span><span class="p">(</span>
    <span class="n">url</span><span class="nf">.as_str</span><span class="p">(),</span>
    <span class="nf">Some</span><span class="p">(</span><span class="o">&amp;</span><span class="nn">serde_json</span><span class="p">::</span><span class="nd">json!</span><span class="p">({</span>
      <span class="s">"sha"</span><span class="p">:</span> <span class="n">sha</span><span class="nf">.as_ref</span><span class="p">(),</span>
      <span class="s">"force"</span><span class="p">:</span> <span class="nd">matches!</span><span class="p">(</span><span class="n">force</span><span class="p">,</span> <span class="nn">ForcePush</span><span class="p">::</span><span class="n">Yes</span><span class="p">)</span>
    <span class="p">})),</span>
<span class="p">)</span>
<span class="k">.await</span><span class="o">?</span><span class="p">;</span>

<span class="n">The</span> <span class="n">problem</span> <span class="n">is</span> <span class="n">that</span> <span class="nn">serde_json</span><span class="p">::</span><span class="nd">json!</span><span class="p">(</span><span class="o">...</span><span class="p">)</span> <span class="n">creates</span> <span class="n">a</span> <span class="n">temporary</span> <span class="n">value</span><span class="p">,</span>
<span class="n">and</span> <span class="n">you</span><span class="nv">'re</span> <span class="n">passing</span> <span class="n">a</span> <span class="n">reference</span> <span class="n">to</span> <span class="nf">it</span> <span class="p">(</span><span class="o">&amp;</span><span class="nn">serde_json</span><span class="p">::</span><span class="nd">json!</span><span class="p">(</span><span class="o">...</span><span class="p">))</span><span class="py">.
Even</span> <span class="n">though</span> <span class="n">octocrab</span><span class="nv">'s</span> <span class="n">build_request</span> <span class="n">is</span> <span class="n">synchronous</span> <span class="n">and</span> <span class="n">serializes</span>
<span class="n">the</span> <span class="n">body</span> <span class="n">before</span> <span class="n">the</span> <span class="k">.await</span><span class="p">,</span> <span class="n">there</span> <span class="n">can</span> <span class="n">be</span> <span class="n">subtle</span> <span class="n">issues</span> <span class="n">with</span> <span class="n">temporary</span> <span class="n">lifetime</span>
<span class="n">extension</span> <span class="k">in</span> <span class="k">async</span> <span class="n">contexts</span><span class="p">,</span> <span class="n">especially</span> <span class="n">across</span> <span class="n">different</span> <span class="n">compiler</span> <span class="n">versions</span>
<span class="n">or</span> <span class="n">optimization</span> <span class="n">levels</span><span class="py">.

Here</span><span class="nv">'s</span> <span class="n">what</span> <span class="n">might</span> <span class="n">be</span> <span class="n">happening</span><span class="p">:</span>
<span class="mf">1.</span> <span class="n">The</span> <span class="nd">json!</span> <span class="k">macro</span> <span class="n">creates</span> <span class="n">a</span> <span class="n">temporary</span> <span class="nn">serde_json</span><span class="p">::</span><span class="n">Value</span>
<span class="mf">2.</span> <span class="n">A</span> <span class="n">reference</span> <span class="n">to</span> <span class="n">this</span> <span class="n">temporary</span> <span class="n">is</span> <span class="n">passed</span> <span class="n">to</span> <span class="n">_patch</span>
<span class="mf">3.</span> <span class="n">Due</span> <span class="n">to</span> <span class="n">the</span> <span class="k">async</span> <span class="n">context</span><span class="p">,</span> <span class="n">there</span><span class="nv">'s</span> <span class="n">a</span> <span class="n">possibility</span> <span class="n">the</span> <span class="n">temporary</span>
   <span class="n">gets</span> <span class="n">dropped</span> <span class="n">before</span> <span class="n">serialization</span> <span class="n">completes</span>
<span class="mf">4.</span> <span class="n">This</span> <span class="n">results</span> <span class="k">in</span> <span class="n">either</span> <span class="n">garbage</span> <span class="n">data</span> <span class="n">or</span> <span class="n">an</span> <span class="n">empty</span> <span class="n">body</span> <span class="n">being</span> <span class="n">sent</span>

<span class="n">The</span> <span class="n">Fix</span>

<span class="n">Store</span> <span class="n">the</span> <span class="n">JSON</span> <span class="n">value</span> <span class="k">in</span> <span class="n">a</span> <span class="n">variable</span> <span class="n">first</span> <span class="n">to</span> <span class="n">ensure</span> <span class="n">it</span> <span class="n">lives</span> <span class="n">long</span> <span class="n">enough</span><span class="p">:</span>

<span class="k">let</span> <span class="n">body</span> <span class="o">=</span> <span class="nn">serde_json</span><span class="p">::</span><span class="nd">json!</span><span class="p">({</span>
  <span class="s">"sha"</span><span class="p">:</span> <span class="n">sha</span><span class="nf">.as_ref</span><span class="p">(),</span>
  <span class="s">"force"</span><span class="p">:</span> <span class="nd">matches!</span><span class="p">(</span><span class="n">force</span><span class="p">,</span> <span class="nn">ForcePush</span><span class="p">::</span><span class="n">Yes</span><span class="p">)</span>
<span class="p">});</span>

<span class="k">let</span> <span class="n">res</span> <span class="o">=</span> <span class="n">repo</span>
  <span class="nf">.client</span><span class="p">()</span>
  <span class="nf">._patch</span><span class="p">(</span><span class="n">url</span><span class="nf">.as_str</span><span class="p">(),</span> <span class="nf">Some</span><span class="p">(</span><span class="o">&amp;</span><span class="n">body</span><span class="p">))</span>
  <span class="k">.await</span><span class="o">?</span><span class="p">;</span>
</code></pre></div></div>

<p>After I patiently explained that this is not the kind of issue that can just happen (without <code class="language-plaintext highlighter-rouge">unsafe</code>) in Rust, and hinted that the problem might also be in a dependency, it finally started looking at <code class="language-plaintext highlighter-rouge">octocrab</code>, and actually did find the shallow <code class="language-plaintext highlighter-rouge">clone</code> problem (although it is again unclear whether that would happen without it knowing about my fix):</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">Ah!</span> <span class="n">I</span> <span class="n">found</span> <span class="n">something</span> <span class="n">suspicious</span><span class="py">. Look</span> <span class="n">at</span> <span class="n">the</span> <span class="n">OctoBody</span> <span class="n">implementation</span> <span class="o">-</span>
<span class="n">it</span> <span class="n">uses</span> <span class="nb">Arc</span><span class="o">&lt;</span><span class="n">RwLock</span><span class="o">&lt;</span><span class="n">BoxBody</span><span class="o">&gt;&gt;</span> <span class="n">and</span> <span class="n">implements</span> <span class="nb">Clone</span> <span class="n">by</span> <span class="n">cloning</span> <span class="n">the</span> <span class="nb">Arc</span><span class="p">:</span>

<span class="k">impl</span> <span class="nb">Clone</span> <span class="k">for</span> <span class="n">OctoBody</span> <span class="p">{</span>
  <span class="k">fn</span> <span class="nf">clone</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
    <span class="nf">OctoBody</span><span class="p">(</span><span class="nn">Arc</span><span class="p">::</span><span class="nf">clone</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="na">.0</span><span class="p">))</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="n">This</span> <span class="n">means</span> <span class="n">when</span> <span class="n">the</span> <span class="n">body</span> <span class="n">is</span> <span class="n">cloned</span><span class="p">,</span> <span class="n">both</span> <span class="n">clones</span> <span class="n">share</span> <span class="n">the</span> <span class="n">same</span> <span class="n">underlying</span>
<span class="n">body</span> <span class="nd">stream!</span> <span class="n">If</span> <span class="n">one</span> <span class="n">clone</span> <span class="n">consumes</span> <span class="n">the</span> <span class="nf">body</span> <span class="p">(</span><span class="n">by</span> <span class="n">polling</span> <span class="n">it</span><span class="p">),</span> <span class="n">the</span> <span class="n">other</span> <span class="n">clone</span>
<span class="n">will</span> <span class="n">have</span> <span class="n">an</span> <span class="n">empty</span> <span class="nd">body!</span>
</code></pre></div></div>

<p>However, even though it found the actual bug, it then started to lose confidence that this bug is the culprit, in a very funny way. It examined my <code class="language-plaintext highlighter-rouge">Cargo.toml</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Wait, I don't see the "retry" feature enabled for octocrab in
bors's Cargo.toml. Let me check if bors has its own retry logic
that might be cloning requests:
</code></pre></div></div>

<p>It failed to realize that <code class="language-plaintext highlighter-rouge">retry</code> is a <em>default</em> feature of <code class="language-plaintext highlighter-rouge">octocrab</code>, so even if I do not enable it myself, it was actually enabled by default. This was enough for it to stop suspecting <code class="language-plaintext highlighter-rouge">octocrab</code>, and go on a hunt to find (hopefully fictitious) retry bugs in the logic of bors. At that point I lost my patience with it and stopped it.</p>

<p>I think that this shows both the advantages and disadvantages of debugging using LLMs. They can sometimes find even pretty tricky issues, but they can also get sidetracked, at which point they need <em>someone else</em> (either a human that actually understands what is going on, or another LLM :) ) to guide them. I can imagine someone less knowledgeable about Rust wasting quite some time debugging imaginary issues in bors found by Claude, in an attempt to locate what might be causing the empty request bodies.</p>

<p>Anyway, next time I start a similar bughunt, I will probably just start Claude in a terminal and let it run concurrently. Then we can see who can get to the core of the bug sooner :)</p>

<h2 id="conclusion">Conclusion</h2>

<p>I’m really glad that I managed to find the underlying issue, as it was a satisfying conclusion to
an otherwise pretty maddening debugging experience. And in the end, it helped fix other pre-existing
bugs in bors and its test suite and also fix a nasty bug in a popular Rust crate at the same time.
So, a win-win?</p>

<p>By the way, this post is not taking a shot at <code class="language-plaintext highlighter-rouge">octocrab</code> in any way. It’s a very useful library that
saved me a lot of time over the years! Kudos to <a href="https://github.com/xampprocky">XAMPPRocky</a> for creating
it and for merging my bugfix PR and releasing a new version so promptly.</p>

<p>If you have similar Rust bug stories, perhaps related to wrong usage of <code class="language-plaintext highlighter-rouge">clone</code>, I’d be glad to hear them on <a href="https://www.reddit.com/r/rust/comments/1pzjj94/investigating_and_fixing_a_nasty_clone_bug/">Reddit</a>.</p>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:bors-testsuite">
      <p>I recently had a talk about my experiences with Rust testing, inspired primarily by the bors test suite. Since the recording will probably not be available online, I plan to turn that talk into a blog post Soon™. Stay tuned. <a href="#fnref:bors-testsuite" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:patch">
      <p>If it was a transitive dependency, I could use the <a href="https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html#the-patch-section"><code class="language-plaintext highlighter-rouge">[patch]</code></a> section in <code class="language-plaintext highlighter-rouge">Cargo.toml</code>. <a href="#fnref:patch" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:foreshawdowing">
      <p>This is actually kind of foreshadowing the cause of the bug :) <a href="#fnref:foreshawdowing" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:traced-test">
      <p>Using the <a href="https://crates.io/crates/traced-test">traced-test</a> crate. <a href="#fnref:traced-test" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="rust" /><summary type="html"><![CDATA[Recently I found myself battling with another nasty bug. It took me several hours to understand what is happening, and once I found it, it turned out that the cause of the bug is relevant to the Ergonomic cloning initiative that is currently being discussed a lot. So I thought that it would be a good candidate for a blog post.]]></summary></entry><entry><title type="html">Reducing binary size of (Rust) programs with debuginfo</title><link href="https://kobzol.github.io/rust/2025/09/22/reducing-binary-size-of-rust-programs-with-debuginfo.html" rel="alternate" type="text/html" title="Reducing binary size of (Rust) programs with debuginfo" /><published>2025-09-22T11:00:00+00:00</published><updated>2025-09-22T11:00:00+00:00</updated><id>https://kobzol.github.io/rust/2025/09/22/reducing-binary-size-of-rust-programs-with-debuginfo</id><content type="html" xml:base="https://kobzol.github.io/rust/2025/09/22/reducing-binary-size-of-rust-programs-with-debuginfo.html"><![CDATA[<p><strong>TL;DR</strong>: There are some <a href="#how-can-we-reduce-the-binary-size">ways</a> how you can reduce the binary size of (Rust) programs that contain debug information.</p>

<p>Perhaps contrary to popular belief, Rust binaries can actually be <a href="https://github.com/johnthagen/min-sized-rust">quite small</a> if you spend some effort on it. However, once you start including debug information (debuginfo) in them, the binary sizes typically start increasing at an alarming rate.</p>

<blockquote>
  <p>If you wonder why we should care about binary size when debuginfo is enabled, as that’s typically used for debug builds, it is also commonly used for distributing release/production binaries that include at least debug line tables, which are needed to produce quality backtraces.</p>
</blockquote>

<p>In this post, I’ll shortly describe why debuginfo takes so much space in Rust programs and how we can optimize it. Note that I will only talk about the DWARF debuginfo format and Linux ELF binaries. Furthermore, I will only be considering debuginfo embedded directly in the binary. If you distribute “split” debuginfo in a separate file, you might not care about its size so much, but that approach has other trade-offs, and I won’t consider it in this post.</p>

<h2 id="why-does-debuginfo-take-so-much-space">Why does debuginfo take so much space?</h2>

<p>Apart from (DWARF) debuginfo generally taking quite a lot of space on its own, there is a less obvious reason why debuginfo in binaries can be so large, and that is because linkers typically do not “tree-shake” unused debuginfo (unlike unused code, which gets removed). I only found out about this recently, which was the motivation for writing this post.</p>

<p>Let’s try to demonstrate that with a simple example. Consider the following crate (let’s call it <code class="language-plaintext highlighter-rouge">dep</code>), which has a single function <code class="language-plaintext highlighter-rouge">foo</code>:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">fn</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span> <span class="nd">println!</span><span class="p">(</span><span class="s">"foo"</span><span class="p">);</span> <span class="p">}</span>
</code></pre></div></div>
<p>and another crate called <code class="language-plaintext highlighter-rouge">mycrate</code> that depends on <code class="language-plaintext highlighter-rouge">dep</code> and calls its <code class="language-plaintext highlighter-rouge">foo</code> function:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="nn">dep</span><span class="p">::</span><span class="nf">foo</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>
<p>When compiled with Rust <code class="language-plaintext highlighter-rouge">1.90.0</code> on x64 Linux in <code class="language-plaintext highlighter-rouge">--release</code> mode, the resulting <code class="language-plaintext highlighter-rouge">mycrate</code> binary has exactly <code class="language-plaintext highlighter-rouge">449328</code> bytes.</p>

<p>If I add another function bar to the <code class="language-plaintext highlighter-rouge">dep</code> crate, without changing anything else:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">fn</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span> <span class="nd">println!</span><span class="p">(</span><span class="s">"foo"</span><span class="p">);</span> <span class="p">}</span>
<span class="k">pub</span> <span class="k">fn</span> <span class="nf">bar</span><span class="p">()</span> <span class="p">{</span> <span class="nd">println!</span><span class="p">(</span><span class="s">"bar"</span><span class="p">);</span> <span class="p">}</span>
</code></pre></div></div>
<p>and recompile, the resulting binary still has the exact same size as before. And that is what I would expect, because the <code class="language-plaintext highlighter-rouge">bar</code> function is not called anywhere, so it should not take space in my binary.</p>

<p>However, the situation changes if I add debuginfo. If I compile the main crate with two versions of <code class="language-plaintext highlighter-rouge">dep</code> (just <code class="language-plaintext highlighter-rouge">foo</code> vs <code class="language-plaintext highlighter-rouge">foo + bar</code>) in release mode with <code class="language-plaintext highlighter-rouge">debug = "line-tables-only"</code>, the size of the binary jumps from <code class="language-plaintext highlighter-rouge">3 874 080</code> bytes to <code class="language-plaintext highlighter-rouge">3 874 232</code> bytes! So even if the <code class="language-plaintext highlighter-rouge">bar</code> function is not used anywhere, its debuginfo is still taking space in my binary :angry: This is not so bad for a single function, but if you consider the amount of code that Rust programs typically depend on, and how much of that code is actually unused, it starts to quickly add up.</p>

<p>This also explains why the binary size suddenly jumped to almost 4 MiB with debuginfo enabled; that is the debuginfo of the standard library being included in it. Even though a lot of it is actually unused/dead code (because my program doesn’t use these parts of the stdlib), its whole debuginfo is still included. Even though the stdlib is a bit special in this regard, because it is precompiled, the same would actually happen with a “normal” crate dependency that you build from scratch.</p>

<p>I’m not really an expert on linkers, but from what I understand, when linkers see debuginfo entries that are unused, they do not remove them, but merely mark them with special “tombstone” values. So they effectively become just dead weight that takes space in the binary, but don’t do anything. At least this is what MaskRay (who is probably one of the most knowledgeable persons about linkers) <a href="https://maskray.me/blog/2021-02-28-linker-garbage-collection#debug-sections">says</a>. Apparently, removing these debuginfo sections in linkers could be very costly in terms of performance, due to DWARF’s tree representation. I found <a href="https://groups.google.com/g/llvm-dev/c/gcEs4ITJI_A/m/lwy9_pR4CQAJ?pli=1">this</a> forum post where someone claims that they tried to implement it in the LLD linker, and that made it seven times slower. I’m somewhat dubious that this is the best that we could achieve, but for now it seems that linkers simply do not remove unused debuginfo entries and we have to live with that. Maybe a <a href="https://github.com/davidlattimore/wild">sufficiently smart</a> linker will be able to do it one day :)</p>

<p>Of course, this problem is not unique to Rust, as it also happens for other “natively compiled” languages, such as C or C++. However, for Rust I think that the issue is typically worse than for C/C++, because Rust programs tend to both have a lot of dependencies, and crucially if you compile them with debuginfo, by default you will be building debuginfo for every. single. dependency. that you have, even though most of that debuginfo will likely never be needed. I think that this is not so prevalent for C or C++, because there people often use prebuilt libraries for which they either might not have debuginfo available, or it is fetched through some external mechanism (e.g. <code class="language-plaintext highlighter-rouge">-dbg</code> packages from the distro package repository) and is thus not embedded directly into the final binary.</p>

<h2 id="how-can-we-reduce-the-binary-size">How can we reduce the binary size</h2>

<p>As usually, I’ll use <a href="https://github.com/It4innovations/hyperqueue">hyperqueue</a> as a test subject for demonstrating how we can reduce the binary size when debuginfo is included. I tried it in two configurations, the first is simply compiling with <code class="language-plaintext highlighter-rouge">--release</code>, while the second is compiling with <code class="language-plaintext highlighter-rouge">--profile dist</code>, which in HQ’s case enables more optimizations (1 <abbr title="Codegen Unit">CGU</abbr>, thin <abbr title="Link-Time Optimizations">LTO</abbr>, etc.). This is the actual configuration that we use for distributing <code class="language-plaintext highlighter-rouge">hyperqueue</code> to end-users. Both configurations include <code class="language-plaintext highlighter-rouge">line-tables-only</code> debuginfo.</p>

<h3 id="garbage-collection">Garbage collection</h3>

<p>One approach that can be used to reduce debuginfo binary size is to perform garbage collection. That finds the unused placeholder (“tombstone”) values and gets rid of them. There are probably more ways of doing that, but I found that the <code class="language-plaintext highlighter-rouge">llvm-dwarfutil</code><sup id="fnref:llvm-dwarfutil"><a href="#fn:llvm-dwarfutil" class="footnote" rel="footnote" role="doc-noteref">1</a></sup> tool can do it, so I tried it:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>llvm-dwarfutil hq hq-gc
</code></pre></div></div>

<p>Here are the results:</p>

<table>
  <thead>
    <tr>
      <th><strong>Version</strong></th>
      <th style="text-align: right"><code class="language-plaintext highlighter-rouge">release</code> profile</th>
      <th style="text-align: right"><code class="language-plaintext highlighter-rouge">dist</code> profile</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Original</td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">70 924 912</code> (100%)</td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">50 832 304</code> (100%)</td>
    </tr>
    <tr>
      <td>GC</td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">59 046 440</code> (83%)</td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">53 162 184</code>  (104%)</td>
    </tr>
  </tbody>
</table>

<p>For the <code class="language-plaintext highlighter-rouge">release</code> profile, it resulted in a nice 17% binary improvement, not bad! However, for the more optimized (and smaller) <code class="language-plaintext highlighter-rouge">dist</code> profile, the resulting binary is actually larger. So it seems that sometimes it doesn’t really help.</p>

<p>However, when it does help, this approach doesn’t really have any disadvantages, as far as I am aware (but if you know of any, I’d be glad to learn!), so it still seems quite useful. It did print a warning about not supporting the <code class="language-plaintext highlighter-rouge">.debug_gdb_scripts</code> section, and some other warnings, but the resulting binary seems to work and produce correct backtraces. The garbage collection took under two seconds.</p>

<p>When I tried to apply the garbage collection to a hello world Rust application compiled in <code class="language-plaintext highlighter-rouge">--release</code> with debuginfo, it reduces the size from the original ~4 MiB down to ~2 MiB. Not bad at all! Seems like it is able to remove a lot of the unused debuginfo from the Rust standard library.</p>

<h3 id="debuginfo-compression">Debuginfo compression</h3>

<p>The second approach, which we actually use on our CI, and which is probably more widely known, is using debuginfo compression. This can be done using the venerable <code class="language-plaintext highlighter-rouge">objcopy</code> tool:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>objcopy <span class="nt">--compress-debug-sections</span><span class="o">=</span>zlib hq hq-zlib
</code></pre></div></div>

<p>I tried both <code class="language-plaintext highlighter-rouge">zlib</code> compression (which took ~1s to compress) and <code class="language-plaintext highlighter-rouge">zstd</code> (which took ~0.1s to compress). Here are the results:</p>

<table>
  <thead>
    <tr>
      <th><strong>Version</strong></th>
      <th style="text-align: right"><code class="language-plaintext highlighter-rouge">release</code> profile</th>
      <th style="text-align: right"><code class="language-plaintext highlighter-rouge">dist</code> profile</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Original</td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">70 924 912</code> (100%)</td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">50 832 304</code> (100%)</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">zlib</code></td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">24 775 848</code> (35%)</td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">18 751 280</code>  (37%)</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">zstd</code></td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">23 413 992</code> (33%)</td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">17 472 960</code>  (34%)</td>
    </tr>
  </tbody>
</table>

<p>The results are pretty great! Going from ~50 MiB down to ~18 MiB is really impressive.</p>

<p>I suppose that the disadvantage of this approach is the risk that some tools that work with debuginfo (such as debuggers, profilers or symbolizers) won’t be able to understand the compressed debuginfo sections or won’t be able to decompress them. This is especially problematic for <code class="language-plaintext highlighter-rouge">zstd</code>, which is less supported “in the wild”. Even here, when I ran the <code class="language-plaintext highlighter-rouge">zstd</code> compressed binary and it panicked, the stacktrace no longer contained proper line numbers (I guess that <code class="language-plaintext highlighter-rouge">gimli</code>, which is included in the stdlib and AFAIK deals with decoding the backtraces, can’t deal with <code class="language-plaintext highlighter-rouge">zstd</code>?). That is why we use <code class="language-plaintext highlighter-rouge">zlib</code><sup id="fnref:zlib-gnu"><a href="#fn:zlib-gnu" class="footnote" rel="footnote" role="doc-noteref">2</a></sup> for our distributed <code class="language-plaintext highlighter-rouge">hyperqueue</code> artifacts, even though it produces slightly larger binaries than <code class="language-plaintext highlighter-rouge">zstd</code>.</p>

<p><strong>EDIT</strong>: <code class="language-plaintext highlighter-rouge">thecakeisalie16</code> <a href="https://www.reddit.com/r/rust/comments/1nnm6di/comment/nflopg6/?utm_source=share&amp;utm_medium=web3x&amp;utm_name=web3xcss&amp;utm_term=1&amp;utm_content=share_button">showed</a> on Reddit that you can also perform debuginfo compression directly using the linker with <code class="language-plaintext highlighter-rouge">RUSTFLAGS="-Clink-arg=-Wl,--compress-debug-sections=zlib"</code>. Didn’t know about that, very cool!</p>

<p>Interestingly, these approaches can be combined to produce even smaller binaries, by first running gargage collection and then compression (the other direction doesn’t really make sense). And funnily enough, compressing the garbage-collected <code class="language-plaintext highlighter-rouge">dist</code> binary, which is actually larger than the <em>original</em> binary, produces a smaller file than compressing the original <code class="language-plaintext highlighter-rouge">dist</code> binary!</p>

<p>Here are the final results with all the approaches I described above:</p>

<table>
  <thead>
    <tr>
      <th><strong>Version</strong></th>
      <th style="text-align: right"><code class="language-plaintext highlighter-rouge">release</code> profile</th>
      <th style="text-align: right"><code class="language-plaintext highlighter-rouge">dist</code> profile</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Original</td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">70 924 912</code> (100%)</td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">50 832 304</code> (100%)</td>
    </tr>
    <tr>
      <td>GC</td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">59 046 440</code> (83%)</td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">53 162 184</code> (105%)</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">zlib</code></td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">24 775 848</code> (35%)</td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">18 751 280</code> (37%)</td>
    </tr>
    <tr>
      <td>GC + <code class="language-plaintext highlighter-rouge">zlib</code></td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">20 457 568</code> (29%)</td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">17 318 760</code> (34%)</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">zstd</code></td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">23 413 992</code> (33%)</td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">17 472 960</code> (34%)</td>
    </tr>
    <tr>
      <td>GC + <code class="language-plaintext highlighter-rouge">zstd</code></td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">19 777 752</code> (28%)</td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">16 492 768</code> (32%)</td>
    </tr>
  </tbody>
</table>

<p>You can find the script that I used to generate this table <a href="https://gist.github.com/Kobzol/71f040d6d3a4b356afcdde20fc47dc81">here</a>, in case you wanted to perform a similar experiment on your own binaries.</p>

<p>On hyperqueue’s CI, we currently only <a href="https://github.com/It4innovations/hyperqueue/blob/main/.github/workflows/build.yml#L62">compress</a> the debuginfo using <code class="language-plaintext highlighter-rouge">objcopy</code>, but based on these results, we might want to also use the debuginfo GC in the future (although getting ahold of <code class="language-plaintext highlighter-rouge">llvm-dwarfutil</code> on CI might be a bit annoying).</p>

<h2 id="should-we-do-this-by-default">Should we do this by default?</h2>

<p>A natural question to ask is whether the Rust compiler should do any of the above by default. Last year, I made <code class="language-plaintext highlighter-rouge">--release</code> Rust binaries <a href="/rust/cargo/2024/01/23/making-rust-binaries-smaller-by-default.html">smaller by default</a> by stripping the standard library debuginfo from them when it wasn’t required. I think that was a pretty successful initiative, and it is relatively similar to what I described in this post (the compiler invokes a <code class="language-plaintext highlighter-rouge">strip</code> binary on the binary to remove the debuginfo, while here we invoke <code class="language-plaintext highlighter-rouge">llvm-dwarfutil</code>/<code class="language-plaintext highlighter-rouge">objcopy</code>), so it might not be too far-fetched?</p>

<p>That being said, I think that compressing debuginfo goes too far in what I would expect that a compiler invocation would do for me, and has the trade-offs mentioned above. Maybe GC would be a better fit (in fact I expected that the linker already does it for me), but the linkers that we currently use don’t seem to have this ability, and doing it post-compilation using e.g. <code class="language-plaintext highlighter-rouge">llvm-dwarfutil</code> might be unnecessarily slow? Testing this on some ludicrously large binary would probably be needed to get a better intuition about trade-offs involved in this.</p>

<p>Of course, if we ever wanted to do this, we would have to decide whether to do it by default (which would likely increase compile times) or whether we want to make it configurable. But in general, it probably seems too specialized to me to be in <code class="language-plaintext highlighter-rouge">rustc</code>. Maybe we could at least document it e.g. in the Cargo book or in <a href="https://github.com/johnthagen/min-sized-rust">min-sized-rust</a>.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Anyway, I wanted to share this post to increase the awareness of compressing and garbage collecting debuginfo in (Rust) binaries, so that we can all ship less data to our users and avoid needless waste. Hopefully you found it useful.</p>

<p>I’m definitely not an expert in this area though, so if you have any suggestions/corrections/tips, I’d be glad to hear from you on <a href="https://www.reddit.com/r/rust/comments/1nnm6di/reducing_binary_size_of_rust_programs_with">Reddit</a>.</p>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:llvm-dwarfutil">
      <p>Sadly, this binary is currently not distributed in the rustup <code class="language-plaintext highlighter-rouge">llvm-tools</code> component, so you’ll need to get it from elsewhere. On Ubuntu it seems to be contained in the <code class="language-plaintext highlighter-rouge">llvm-&lt;version&gt;</code> packages installable through <code class="language-plaintext highlighter-rouge">apt</code>. <a href="#fnref:llvm-dwarfutil" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:zlib-gnu">
      <p>Actually, we use <code class="language-plaintext highlighter-rouge">zlib-gnu</code>, but I don’t remember why anymore :sweat_smile:. <a href="#fnref:zlib-gnu" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="rust" /><summary type="html"><![CDATA[TL;DR: There are some ways how you can reduce the binary size of (Rust) programs that contain debug information.]]></summary></entry><entry><title type="html">Adding #[derive(From)] to Rust</title><link href="https://kobzol.github.io/rust/2025/09/02/adding-derive-from-to-rust.html" rel="alternate" type="text/html" title="Adding #[derive(From)] to Rust" /><published>2025-09-02T11:00:00+00:00</published><updated>2025-09-02T11:00:00+00:00</updated><id>https://kobzol.github.io/rust/2025/09/02/adding-derive-from-to-rust</id><content type="html" xml:base="https://kobzol.github.io/rust/2025/09/02/adding-derive-from-to-rust.html"><![CDATA[<p><strong>TL;DR</strong>: <code class="language-plaintext highlighter-rouge">#[derive(From)]</code> can be used in nightly now. See <a href="#how-to-use-it">here</a> on how to use it.
You can follow <a href="https://github.com/rust-lang/rust/issues/144889">this tracking issue</a> for more updates.</p>

<p>I use the newtype pattern in Rust <a href="/rust/2025/09/01/combining-struct-literal-syntax-with-read-only-field-access.html">a lot</a>,
and I would like to make certain use-cases around it (even) simpler to express. A typical thing to
do with newtypes is that you implement a bunch of standard traits for them, which will delegate
to the inner field. The easiest way of achieveing that is of course with a <code class="language-plaintext highlighter-rouge">#[derive(...)]</code>:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[derive(Debug,</span> <span class="nd">Copy,</span> <span class="nd">Clone,</span> <span class="nd">PartialEq,</span> <span class="nd">Eq,</span> <span class="nd">PartialOrd,</span> <span class="nd">Ord,</span> <span class="nd">Hash,</span> <span class="nd">Default)]</span>
<span class="k">struct</span> <span class="nf">WorkerId</span><span class="p">(</span><span class="nb">u32</span><span class="p">);</span>
</code></pre></div></div>

<p>However, this approach cannot be currently used with all “built-in” traits from the standard library.
One of those traits is the <a href="https://doc.rust-lang.org/std/convert/trait.From.html"><code class="language-plaintext highlighter-rouge">From</code></a> trait,
which is used to convert between values of two types. Even though sometimes it might not be a good idea
to implement <code class="language-plaintext highlighter-rouge">From</code> when using a newtype (more on that later), in other cases it’s quite useful,
as it makes it easier to go from a value of the inner field to a value of the newtype (especially
in generic contexts). A manual implementation of the trait will usually look something like this:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="nb">From</span><span class="o">&lt;</span><span class="nb">u32</span><span class="o">&gt;</span> <span class="k">for</span> <span class="n">WorkerId</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">from</span><span class="p">(</span><span class="n">value</span><span class="p">:</span> <span class="nb">u32</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="k">Self</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This code is trivial to implement, of course, and there’s not much space for bugs here, but it’s also
pure boilerplate, and especially if you have a lot of newtypes in your crate, it just seems wasteful,
as it’s clear that the compiler should be able to generate this implementation easily.</p>

<p>In fact, writing this impl over and over again usually annoys me enough that I end up writing a
macro to generate newtypes, so that I don’t have to copy this (and other similar) code all over the
place. But writing and using such macros has trade-offs (same as using third-party crates for
this, such as <a href="https://docs.rs/derive_more/latest/derive_more/derive.From.html">derive_more</a>), and
I thought that rustc should just give me the option to <code class="language-plaintext highlighter-rouge">#[derive(From)]</code>.</p>

<h2 id="writing-an-rfc">Writing an <abbr title="Request For Comments">RFC</abbr></h2>

<p>After running into this missing feature yet again, I realized that… I could just add it to Rust :laughing:
Making language changes can be daunting, but this specific change seemed quite simple, mostly because
the behavior of the feature seems <em>obvious</em>. And so I decided to
try it and write an <a href="https://github.com/rust-lang/rfcs"><abbr title="Request For Comments">RFC</abbr></a>. To test the waters, I first
drafted a <a href="https://internals.rust-lang.org/t/pre-rfc-derive-from-for-newtypes/22567">“Pre-<abbr title="Request For Comments">RFC</abbr>”</a> on
the <a href="https://internals.rust-lang.org/">IRLO</a> forum. It received a relatively positive response,
even from a member of the Language team, which made me confident enough to turn it into an actual <abbr title="Request For Comments">RFC</abbr>,
which I did a few weeks <a href="https://github.com/rust-lang/rfcs/pull/3809">later</a>.</p>

<p>I have to say that I was quite pleasantly surprised by the positive response to my <abbr title="Request For Comments">RFC</abbr>. I’m used to
reading big controversial RFCs that generate hundreds of comments and many arguments, but here the
response was mostly “yep, this sounds like a good idea” (with a few exceptions that I will discuss later).
I even received a few suggestions on how this feature could be improved in a future <abbr title="Request For Comments">RFC</abbr>.</p>

<p>The PR <a href="https://github.com/rust-lang/rfcs/pull/3809">itself</a> also received a lot of positive emojis!
At the time of writing of this blog post, it has over one hundred emoji reactions, which actually
makes it into the top ~60 most “liked” RFCs in the repository, even though it’s very recent. Emojis
are of course not a good way to decide which RFCs should be accepted, but it is a signal that many people seem to share the same annoyance of lacking an easy way to derive <code class="language-plaintext highlighter-rouge">From</code> impls.</p>

<p>I think that one of the strengths of this <abbr title="Request For Comments">RFC</abbr> is that it is arguably not even a <em>new feature</em>, per se.
It is one of those things that “just” fill a gap in Rust; it allows combining two features that were working
separately before (the <code class="language-plaintext highlighter-rouge">From</code> trait and deriving standard library traits), but couldn’t be used together.</p>

<p>One thing that was a bit unclear with the <abbr title="Request For Comments">RFC</abbr> though was determining which Rust team(s) should actually
be responsible for deciding its fate. The first decision process (<abbr title="Final Comment Period">FCP</abbr>) was <a href="https://github.com/rust-lang/rfcs/pull/3809#issuecomment-2917056764">started</a> for the Language team. Then it was <a href="https://github.com/rust-lang/rfcs/pull/3809#issuecomment-2963675737">suggested</a> that it should actually be handled solely by the
Libs API team. And in the end we did a dual Language + Libs API <a href="https://github.com/rust-lang/rfcs/pull/3809#issuecomment-2964594802"><abbr title="Final Comment Period">FCP</abbr></a>. This is actually a recurrent problem in the
Rust Project (and I’m sure in many/most other organizational structures), where we are relatively
often unsure <em>who</em> should actually be responsible for deciding (or doing) something, which can sometimes block
progress.</p>

<p>Luckily, that didn’t happen here, and I can happily announce that after a few minor road bumps,
the <abbr title="Final Comment Period">FCP</abbr> has been finished, and my first ever Rust <abbr title="Request For Comments">RFC</abbr> was accepted :tada:.</p>

<h2 id="design-of-the-feature">Design of the feature</h2>

<p>Now that I have spoiled the outcome of the <abbr title="Request For Comments">RFC</abbr>, I should probably talk about the actual design of
the feature. Even though it sounds simple, there are a few things to consider.</p>

<p>First, let’s make it clear what is the goal of <code class="language-plaintext highlighter-rouge">#[derive(From)]</code>. When applied on an <abbr title="Algebraic Data Type">ADT</abbr>, i.e. a struct
or an enum<sup id="fnref:unions"><a href="#fn:unions" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>, it should generate an implementation of the <code class="language-plaintext highlighter-rouge">From</code> trait for it. Let’s start
with structs – when you apply <code class="language-plaintext highlighter-rouge">#[derive(From)]</code> to a struct, you should get a <code class="language-plaintext highlighter-rouge">From</code> impl for the
struct that creates it from a value of its field, like this:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[derive(From)]</span>
<span class="k">struct</span> <span class="nf">Foo</span><span class="p">(</span><span class="nb">u32</span><span class="p">);</span>

<span class="c1">// generates:</span>
<span class="k">impl</span> <span class="nb">From</span><span class="o">&lt;</span><span class="nb">u32</span><span class="o">&gt;</span> <span class="k">for</span> <span class="n">Foo</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">from</span><span class="p">(</span><span class="n">value</span><span class="p">:</span> <span class="nb">u32</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="k">Self</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>If you look at the example above, an obvious problem presents itself: what to do if there is not exactly
one field in the struct?</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="nf">Foo</span><span class="p">(</span><span class="nb">u32</span><span class="p">,</span> <span class="nb">bool</span><span class="p">);</span>
</code></pre></div></div>

<p>There are three solutions that come to mind:</p>
<ol>
  <li>Generate a <code class="language-plaintext highlighter-rouge">From</code> impl from all the values at once, i.e. <code class="language-plaintext highlighter-rouge">From&lt;(u32, bool)&gt;</code></li>
  <li>Force the user to specify which field should be used for the <code class="language-plaintext highlighter-rouge">From</code> impl</li>
  <li>Forbid using <code class="language-plaintext highlighter-rouge">#[derive(From)]</code> in this case</li>
</ol>

<p>I think that option 1) would be quite unintuitive, and it would run into an issue for the most common
use-case where you have a single field - should we generate <code class="language-plaintext highlighter-rouge">From&lt;FieldType&gt;</code>, and thus break consistency
with other field counts, or <code class="language-plaintext highlighter-rouge">From&lt;(FieldType,)&gt;</code> (i.e. generate the struct from a tuple
of size one), which is quite unergonomic and uncommon? This approach also wouldn’t really work for
structs with named fields.</p>

<p>I left option 2) as a future possibility. One of the suggestions that were proposed was that we could
annotate a single field with an attribute (e.g. <code class="language-plaintext highlighter-rouge">#[from]</code>), and then create the rest of the fields
using their <code class="language-plaintext highlighter-rouge">Default</code> impl:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[derive(From)]</span>
<span class="k">struct</span> <span class="nf">Foo</span><span class="p">(</span><span class="nd">#[from]</span> <span class="nb">u32</span><span class="p">,</span> <span class="nb">bool</span><span class="p">);</span>

<span class="k">impl</span> <span class="nb">From</span><span class="o">&lt;</span><span class="nb">u32</span><span class="o">&gt;</span> <span class="k">for</span> <span class="n">Foo</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">from</span><span class="p">(</span><span class="n">value</span><span class="p">:</span> <span class="nb">u32</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="k">Self</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="nn">Default</span><span class="p">::</span><span class="nf">default</span><span class="p">())</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I like this option a lot, and I think that we should explore it in the future. A similar
approach is also being used for selecting the default enum variant using the <code class="language-plaintext highlighter-rouge">#[default]</code> attribute
when deriving <code class="language-plaintext highlighter-rouge">Default</code>, so there is a precedent for doing something like this. However, since I wanted
to keep this <abbr title="Request For Comments">RFC</abbr> minimal in order to improve its chances of being accepted without falling into
deep “design holes”, I didn’t include it in the <abbr title="Request For Comments">RFC</abbr> and left it as a “future improvement”.</p>

<p>And thus I chose to go with option 3), which means that you can currently only use <code class="language-plaintext highlighter-rouge">#[derive(From)]</code> on
structs that have exactly one field. I don’t think that it’s a big limitation though, because I expect
that in most cases this derive will be used for newtypes (which typically store exactly one field).
In general, the <code class="language-plaintext highlighter-rouge">From</code> trait is most commonly used for converting a single value of one type into a
single value of another type, so it makes sense to have this limitation in the initial design.</p>

<p>In terms of enums, the situation is more complicated. In theory, we could allow the macro to be used
on enums that have exactly one variant with exactly one field, but that doesn’t sound like a very
useful scenario. For simplicity, I thus also decided to forbid using <code class="language-plaintext highlighter-rouge">#[derive(From)]</code> on enums,
at least for now.</p>

<p>Apart from this main design question, some concerns were expressed by several people on the <abbr title="Request For Comments">RFC</abbr>.</p>

<h3 id="is-the-direction-for-the-from-impl-the-right-one">Is the “direction” for the From impl the right one?</h3>

<p>One noted concern was whether <code class="language-plaintext highlighter-rouge">#[derive(From)] struct Foo(u32);</code> should generate <code class="language-plaintext highlighter-rouge">From&lt;u32&gt; for Foo</code>,
<code class="language-plaintext highlighter-rouge">From&lt;Foo&gt; for u32</code>, or both. To me, the answer is obvious (it should be <code class="language-plaintext highlighter-rouge">From&lt;u32&gt; for Foo</code>),
because that is how all the other derive macros work; they always generate an impl for the type on
which the derive macro is applied. I think that this behavior is the most intuitive one, and we should
keep consistency with the previous <code class="language-plaintext highlighter-rouge">derive</code>able macros. This direction also makes the newtype use-case
work, and I consider that to be the most important driving use-case for this feature.</p>

<p>However, it is actually interesting to also consider how we could support the second direction, because
it can also be useful to go from a value of the struct back into the inner field, of course. I omitted
this functionality from my <abbr title="Request For Comments">RFC</abbr>, but one of the proposed solutions was to add something like <code class="language-plaintext highlighter-rouge">#[derive(Into)]</code>,
which would generate <code class="language-plaintext highlighter-rouge">From&lt;StructType&gt; for FieldType</code>. Of course, this naming could be quite confusing,
because we would not <em>actually</em> generate an impl of the <code class="language-plaintext highlighter-rouge">Into</code> trait, because that is generated
automatically by a <a href="https://doc.rust-lang.org/src/core/convert/mod.rs.html#756">blanket impl</a> in the
standard library. But that is a discussion for another <abbr title="Request For Comments">RFC</abbr>.</p>

<h3 id="are-we-closing-the-door-on-other-solutions">Are we closing the door on other solutions?</h3>

<p>This is the usual worry with any (Rust) <abbr title="Request For Comments">RFC</abbr> that adds a new feature – are we sure that we got the right
design for it, that it will be extensible in the future, and won’t close doors to a better solution for
the same problem?</p>

<p>Since I kept my <abbr title="Request For Comments">RFC</abbr> minimal, this is a valid concern. Maybe we should first figure out how to support
the <code class="language-plaintext highlighter-rouge">From</code> impl in the other direction? Or flesh out the <code class="language-plaintext highlighter-rouge">#[from]</code> extensions? Some reviewers wanted
to see the “grand picture” of this feature before it will be stabilized (or even before the <abbr title="Request For Comments">RFC</abbr> would
get accepted).</p>

<p>I sympathize with those views, as I am myself also often worried about accepting a feature <em>too soon</em>,
when we don’t know all the details yet. However, in this specific case, I am quite confident that the
feature that I proposed will be perfectly compatible with any relevant extension, and thus we should
land it as soon as possible, without waiting on the more complicated discussions about the possible
extensions, to achieve incremental progress.</p>

<p>My confidence comes from the fact that (at least to me), the behavior of <code class="language-plaintext highlighter-rouge">#[derive(From)]</code>
is simply <em>obvious</em>, and there is no other way how it could reasonably behave.
If <code class="language-plaintext highlighter-rouge">rustc</code> generated anything else than the following for <code class="language-plaintext highlighter-rouge">#[derive(From)] struct Foo(u32)</code>:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="nb">From</span><span class="o">&lt;</span><span class="nb">u32</span><span class="o">&gt;</span> <span class="k">for</span> <span class="n">Foo</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">from</span><span class="p">(</span><span class="n">value</span><span class="p">:</span> <span class="nb">u32</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="k">Self</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Then I think that the feature would be wrong. It might have some other extensions or configuration
points, but this core functionality is in my opinion set in stone, and there is no other way how it
could work. And thus I wanted to get my minimalistic <abbr title="Request For Comments">RFC</abbr> out the door,
so that we can start testing the feature in the wild, and mainly so that it can start providing
value to Rust users.</p>

<p>It should be possible to lift the limitations (such as allowing it on structs with multiple fields
and implementing the <code class="language-plaintext highlighter-rouge">#[from]</code> attribute), or add related functionality (such as <code class="language-plaintext highlighter-rouge">#[derive(Into)]</code>)
to this feature in the future in a backwards-compatible way<sup id="fnref:famous-last-words"><a href="#fn:famous-last-words" class="footnote" rel="footnote" role="doc-noteref">2</a></sup>.</p>

<h3 id="is-using-from-on-newtypes-even-a-good-idea">Is using <code class="language-plaintext highlighter-rouge">From</code> on newtypes even a good idea?</h3>

<p>One additional, a more philosophical, concern discussed on my <abbr title="Request For Comments">RFC</abbr> was whether implementing
<code class="language-plaintext highlighter-rouge">From</code> for newtypes is even a good idea in the first place, and thus whether we should teach <code class="language-plaintext highlighter-rouge">rustc</code>
to do it.</p>

<p>There are two angles to this discussion. The first one is whether newtypes should implement the
<code class="language-plaintext highlighter-rouge">From</code> trait or not. To answer that question, I’ll try to examine the various motivations
for using newtypes in a bit more detail.</p>

<blockquote>
  <p>I stress motivations here so much because newtype is a <em>pattern</em>, and the motivation for using a pattern is one of the things that defines it. You can have two identical pieces of code, where one upholds a certain pattern, and the other doesn’t, which can happen if those pieces of code were created because of a different motivation. That is why there are some patterns in <a href="https://en.wikipedia.org/wiki/Design_Patterns">Gang of Four</a> that can have pretty much the same source code representation, but they still form different patterns because they have a different motivation.</p>
</blockquote>

<p>The primary motivation for using a newtype, one that is upheld by <em>every</em> usage of a newtype, is to
create a new type in the type system (d’uh). This allows us distinguish between e.g. <code class="language-plaintext highlighter-rouge">WorkerId</code> and
<code class="language-plaintext highlighter-rouge">TaskId</code>, so that it is harder to accidentally combine values of different domain types, even if they
are backed by the same “in-memory” data type (e.g. an integer). I’ll call this the “avoid type confusion”
motivation.</p>

<p>Another very common motivation (which is a subset of the first motivation!) is to ensure that values
of the newtype will uphold some invariant, which is not necessarily true for all values of the type
inside the newtype. A typical example is something like <code class="language-plaintext highlighter-rouge">struct Email(String)</code>. An <code class="language-plaintext highlighter-rouge">Email</code> is backed
by a <code class="language-plaintext highlighter-rouge">String</code>, but not all values of strings are valid e-mails! I’ll call this the “ensure invariants”
motivation.</p>

<p>For avoiding type confusion, it is perfectly valid to implement the <code class="language-plaintext highlighter-rouge">From</code> trait for the newtype, as it
gives you a standardized way of initializing the newtype from the wrapped value.</p>

<p>For ensuring invariants, implementing <code class="language-plaintext highlighter-rouge">From</code> is of course not the right thing to do! <code class="language-plaintext highlighter-rouge">From</code> should
always be infallible, and should be able to convert <em>all</em> values of a type into some value of another
type. But in the case of e.g. <code class="language-plaintext highlighter-rouge">Email</code>, that of course won’t work. It might make sense to implement
<code class="language-plaintext highlighter-rouge">TryFrom</code> in this case, but that is not something that we can easily <code class="language-plaintext highlighter-rouge">derive</code> anyway.</p>

<p>It seemed to me that people who expressed concerns about whether we should make implementing
<code class="language-plaintext highlighter-rouge">From</code> easier were mostly using newtypes to ensure invariants. In that case I can understand why
they think that implementing <code class="language-plaintext highlighter-rouge">From</code> is a bad idea! However, I personally use newtypes more often
for avoiding type confusion (my estimate is that the ratio between the two use-cases in my code is
something like 75:25 in favour of avoiding type confusion). So for most of my newtype use-cases,
implementing <code class="language-plaintext highlighter-rouge">From</code> is genuinely useful (which is why I wanted to add this feature to Rust in the
first place :) ).</p>

<p>The second angle to this discussion is more general and unrelated to newtypes.
I personally think that Rust should not dictate what is and what isn’t “good program design”,
of course as long as makes sure that it is <em>sound</em> (in the sense of memory/data race/etc. safety).
Obviously, Rust already makes some strong decisions about program design, for example it does not
allow you to use inheritance, and the borrow checker also makes
expressing certain ownership patterns (like the famous “soup of pointers”) annoying. But in this specific case,
you can already implement <code class="language-plaintext highlighter-rouge">From</code> by hand, so forbidding users from deriving this trait, even
though it is technically possible, just to make sure that they don’t do something that might be considered
bad practice, would seem to be a bit silly to me.</p>

<h2 id="implementing-the-feature-in-rustc">Implementing the feature in rustc</h2>

<p>Normally, after an <abbr title="Request For Comments">RFC</abbr> is accepted, we have to wait until <em>someone</em> actually implements it (which is
not a given, we shouldn’t forget that!). But luckily, since I’m a <a href="https://www.rust-lang.org/governance/teams/compiler">compiler contributor</a>, I was able to help myself here :sweat_smile: And after some trial and error, I
managed to <a href="https://github.com/rust-lang/rust/pull/144922">implement</a> the feature. It was a pretty
fun exercise, because the <code class="language-plaintext highlighter-rouge">From</code> trait is different enough from all the previous traits that
were <code class="language-plaintext highlighter-rouge">derive</code>able, so I also had to make some small changes to the <code class="language-plaintext highlighter-rouge">derive</code> infrastructure to make it work.
It was also a chance to work with <a href="http://github.com/nnethercote">Nick Nethercote</a> again, which is always awesome.
He even made some <a href="https://github.com/rust-lang/rust/pull/145550">improvements</a> to the feature
after my initial PR was merged. Thanks Nick!</p>

<p>While implementing this feature, I understood how the derive macros are actually wired between
the compiler and the standard library, which is actually pretty interesting. The compiler <a href="https://github.com/rust-lang/rust/blob/62227334ae04429f4b1196c8f852d666ae56204b/compiler/rustc_builtin_macros/src/lib.rs#L128">defines</a>
the traits that are deriveable, along with the actual logic to generate the derived code. However,
when I added a new <code class="language-plaintext highlighter-rouge">From</code> macro derive implementation to this list, <code class="language-plaintext highlighter-rouge">#[derive(From)]</code> still didn’t work.</p>

<p>After digging and tracing of the compiler’s behavior on small Rust programs, and asking the
<a href="https://github.com/petrochenkov">Foremost Rust Name Resolution Expert™</a> for help, I finally realized
that the answer is <em>name resolution</em>. When the compiler sees something like <code class="language-plaintext highlighter-rouge">#[derive(Trait)]</code>, it uses
name resolution to understand what <code class="language-plaintext highlighter-rouge">Trait</code> is. One option is that the trait is resolved to a
(derive) proc macro, in which case rustc will call it<sup id="fnref:derive-proc-macro-cache"><a href="#fn:derive-proc-macro-cache" class="footnote" rel="footnote" role="doc-noteref">3</a></sup> to generate the expanded code.
However, another option is that it will resolve the trait to a macro in the standard library marked
with the <code class="language-plaintext highlighter-rouge">#[rustc_builtin_macro]</code> <a href="https://github.com/rust-lang/rust/pull/144922/files#diff-7fdf8ef3b0e02b28e3caa4cc144046f9510df7c8a1f524124f4921601a3d7456R1779">attribute</a>.
And that is the piece of magic that ties the name <code class="language-plaintext highlighter-rouge">From</code> to the code that actually generates the right
derive logic. So after I added this:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[rustc_builtin_macro]</span>
<span class="nd">#[unstable(feature</span> <span class="nd">=</span> <span class="s">"derive_from"</span><span class="nd">,</span> <span class="nd">issue</span> <span class="nd">=</span> <span class="s">"144889"</span><span class="nd">)]</span>
<span class="k">pub</span> <span class="k">macro</span> <span class="nf">From</span><span class="p">(</span><span class="err">$</span><span class="n">item</span><span class="p">:</span> <span class="n">item</span><span class="p">)</span> <span class="p">{</span>
    <span class="cm">/* compiler built-in */</span>
<span class="p">}</span>
</code></pre></div></div>

<p>to the standard library, <code class="language-plaintext highlighter-rouge">#[derive(From)]</code> finally started working :tada:</p>

<p>However, name resolution (in Rust, but also in general) is a VERY gnarly beast, and my joy from defeating
it did not last long. The very next day after my PR was merged, someone created an <a href="https://github.com/rust-lang/rust/issues/145524">issue</a> that my PR broke their code in nightly because of name resolution
errors. Exactly the thing you want to see when you wake up :laughing:</p>

<p>The issue was (as usually) with glob imports. To make <code class="language-plaintext highlighter-rouge">#[derive(From)]</code> work “out of the box”, I added
the <code class="language-plaintext highlighter-rouge">From</code> macro to the standard library prelude, so that it can be implicitly resolved, and users
thus wouldn’t have to import anything to use the new <code class="language-plaintext highlighter-rouge">derive</code>. This is what makes e.g. <code class="language-plaintext highlighter-rouge">#[derive(Hash)]</code>
work even if you don’t have the <code class="language-plaintext highlighter-rouge">std::hash::Hash</code> trait imported.</p>

<p>However, adding new things to the prelude is always tricky. Consider this code:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">mod</span> <span class="n">foo</span> <span class="p">{</span>
    <span class="k">pub</span> <span class="k">use</span> <span class="nn">derive_more</span><span class="p">::</span><span class="nb">From</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">use</span> <span class="nn">foo</span><span class="p">::</span><span class="o">*</span><span class="p">;</span>

<span class="nd">#[derive(From)]</span> <span class="c1">// ERROR: `From` is ambiguous</span>
<span class="k">struct</span> <span class="nf">S</span><span class="p">(</span><span class="nb">u32</span><span class="p">);</span>
</code></pre></div></div>

<p>The code uses a glob import to import a macro named <code class="language-plaintext highlighter-rouge">From</code> from the <code class="language-plaintext highlighter-rouge">foo</code> module. After my PR landed,
this module also received another macro named <code class="language-plaintext highlighter-rouge">From</code> (the one from the standard library), which was
imported with a glob import from the std prelude, which caused an ambiguity. In other words, my change
broke existing (stable) code, even without people using the new feature. That is obviously Very Bad.</p>

<p>To <a href="https://github.com/rust-lang/rust/pull/145563">fix</a> this, I had to remove the <code class="language-plaintext highlighter-rouge">From</code> macro
from the standard library prelude, and thus for now, it will have to be imported explicitly. I added
the macro to the <code class="language-plaintext highlighter-rouge">std::from</code> module; it had to be added to a <em>new</em> module, because no previously
stable Rust code could have had a previous import from a module that did not exist, and thus this
should not break anyone. It is a bit confusing though that the <code class="language-plaintext highlighter-rouge">From</code> macro is in <code class="language-plaintext highlighter-rouge">std::from</code>, while
the <code class="language-plaintext highlighter-rouge">From</code> trait is in <a href="https://doc.rust-lang.org/std/convert/trait.From.html"><code class="language-plaintext highlighter-rouge">std::convert</code></a>.</p>

<p>To be honest, I don’t know if there is a way to resolve the import ambiguity without an edition bump,
so maybe this feature will have to wait until the next edition before it can be used ergonomically.
Maybe <a href="https://github.com/rust-lang/rust/pull/139493">#139493</a> might help with issues like this in the future.</p>

<p>It is interesting to note that if the code looked like this instead:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">use</span> <span class="nn">derive_more</span><span class="p">::</span><span class="nb">From</span><span class="p">;</span>

<span class="nd">#[derive(From)]</span> <span class="c1">// Uses `From` from `derive_more`</span>
<span class="k">struct</span> <span class="nf">S</span><span class="p">(</span><span class="nb">u32</span><span class="p">);</span>
</code></pre></div></div>

<p>Then there would be no ambiguity, because the explicit import from <code class="language-plaintext highlighter-rouge">derive_more</code> would be prioritized
before the <code class="language-plaintext highlighter-rouge">From</code> glob import from the std prelude. But having two macros imported <em>both</em> with a
glob import (with one coming from the prelude) causes a problem.</p>

<h2 id="how-to-use-it">How to use it</h2>

<p>To experiment with this feature, update to a recent nightly release of the compiler, and use <code class="language-plaintext highlighter-rouge">#[derive(From)]</code>
on a struct of your choice. Note that it will only work on structs with
exactly a single field, as mentioned before! You will also have to import the macro explicitly,
because of the ambiguity problem described above.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#![feature(derive_from)]</span>

<span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">from</span><span class="p">::</span><span class="nb">From</span><span class="p">;</span>

<span class="nd">#[derive(From)]</span>
<span class="k">struct</span> <span class="nf">Foo</span><span class="p">(</span><span class="nb">u32</span><span class="p">);</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">f</span><span class="p">:</span> <span class="n">Foo</span> <span class="o">=</span> <span class="mi">1u32</span><span class="nf">.into</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>You can try the feature and let me know if it works fine for you! Note that I don’t expect that
many things could break here, provided that there won’t be any further name resolution errors…</p>

<h2 id="future-improvements">Future improvements</h2>

<p>As I already discussed above, there are various improvements and extensions that we could do with
<code class="language-plaintext highlighter-rouge">#[derive(From)]</code>. But even more generally, I think that we should make it possible to <code class="language-plaintext highlighter-rouge">derive</code>
as many standard library traits as possible. It should be easily possible to generate impls of
<code class="language-plaintext highlighter-rouge">AsRef</code>, <code class="language-plaintext highlighter-rouge">Deref</code> or even traits like <code class="language-plaintext highlighter-rouge">Iterator</code>, as long as you do it on structs with a single field.</p>

<p>If/once <code class="language-plaintext highlighter-rouge">#[derive(From)]</code> becomes stabilized, I might try to write RFCs to also support these other
traits, to reduce even more boilerplate.</p>

<h2 id="conclusion">Conclusion</h2>

<p>I hope you enjoyed my adventures with writing an <abbr title="Request For Comments">RFC</abbr> and designing and implementing a Rust feature!
You can let me know what you think about <code class="language-plaintext highlighter-rouge">#[derive(From)]</code>, and which other traits would you like to
<code class="language-plaintext highlighter-rouge">#[derive]</code>, on <a href="https://www.reddit.com/r/rust/comments/1n6kxgf/adding_derivefrom_to_rust">Reddit</a>.</p>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:unions">
      <p>I’m purposefully omitting <code class="language-plaintext highlighter-rouge">union</code>s here, as I mostly want to ignore those :laughing: <a href="#fnref:unions" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:famous-last-words">
      <p>Famous last words…? :laughing: <a href="#fnref:famous-last-words" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:derive-proc-macro-cache">
      <p>And hopefully also <a href="https://github.com/rust-lang/rust/pull/145354">cache</a> it one day… <a href="#fnref:derive-proc-macro-cache" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="rust" /><summary type="html"><![CDATA[TL;DR: #[derive(From)] can be used in nightly now. See here on how to use it. You can follow this tracking issue for more updates.]]></summary></entry><entry><title type="html">Combining struct literal syntax with read-only field access</title><link href="https://kobzol.github.io/rust/2025/09/01/combining-struct-literal-syntax-with-read-only-field-access.html" rel="alternate" type="text/html" title="Combining struct literal syntax with read-only field access" /><published>2025-09-01T11:00:00+00:00</published><updated>2025-09-01T11:00:00+00:00</updated><id>https://kobzol.github.io/rust/2025/09/01/combining-struct-literal-syntax-with-read-only-field-access</id><content type="html" xml:base="https://kobzol.github.io/rust/2025/09/01/combining-struct-literal-syntax-with-read-only-field-access.html"><![CDATA[<p>Recently, I found myself struggling with a small annoyance related to struct field visibility
and struct initialization in Rust. It’s no rocket science, but I thought about it long enough that
I might as well turn it into a blog post.</p>

<p>In <a href="https://github.com/it4innovations/hyperqueue">HyperQueue</a>, we have a struct describing
parameters of a queue (for the purpose of this post, it doesn’t really matter what a queue is),
which contains a bunch of fields. Something like this:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[derive(Serialize,</span> <span class="nd">Deserialize,</span> <span class="nd">Debug,</span> <span class="nd">Clone)]</span>
<span class="k">pub</span> <span class="k">struct</span> <span class="n">QueueParameters</span> <span class="p">{</span>
    <span class="k">pub</span> <span class="n">manager</span><span class="p">:</span> <span class="n">ManagerType</span><span class="p">,</span>
    <span class="k">pub</span> <span class="n">max_workers_per_alloc</span><span class="p">:</span> <span class="nb">u32</span><span class="p">,</span>
    <span class="k">pub</span> <span class="n">backlog</span><span class="p">:</span> <span class="nb">u32</span><span class="p">,</span>
    <span class="k">pub</span> <span class="n">timelimit</span><span class="p">:</span> <span class="n">Duration</span><span class="p">,</span>
    <span class="k">pub</span> <span class="n">name</span><span class="p">:</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="k">pub</span> <span class="n">max_worker_count</span><span class="p">:</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="nb">u32</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="k">pub</span> <span class="n">min_utilization</span><span class="p">:</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="nb">f32</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="k">pub</span> <span class="n">additional_args</span><span class="p">:</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="c1">// ... more fields</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This struct is just (Plain Old) data, a bag of fields that can be (de)serialized, sent over the
network, etc. However, at some point in the program, I want to store these parameters in a way
that would prevent other parts of the codebase from modifying them. Of course, I could do that easily
by making its fields private, but that would prevent me from using the struct literal syntax for
creating values of this struct outside the module (and its children) that defines it.</p>

<p>In short, I want to be able to both:</p>
<ul>
  <li>Initialize the struct using the struct literal syntax (<code class="language-plaintext highlighter-rouge">QueueParameters { manager: ..., backlog: ... }</code>)
anywhere in the codebase, so that I can see which fields are initialized with what values (as we
don’t have named parameters in Rust).</li>
  <li>Make it impossible to modify the values of the struct fields (outside the module of the struct) after initialization.</li>
</ul>

<p>It would be nice to have something like “init-only” fields, but as far as I know, Rust doesn’t really
have anything like that today. So I had to use some workaround. A common approach to sort of emulate
named parameters in Rust is to use the builder pattern, but that seemed like overkill to me in this use-case,
and its syntax would be more verbose than the normal struct literal syntax anyway. I also didn’t
want to complicate the matter with any kind of macro magic.</p>

<p>My original “solution” to avoid the field mutation was to introduce a separate struct that has the
same fields as <code class="language-plaintext highlighter-rouge">QueueParameters</code> (let’s call it <code class="language-plaintext highlighter-rouge">ReadOnlyQueueParameters</code>),
just with the fields being private, so that they cannot be modified from the outside after initialization.
Because the fields were private, I also had to create a constructor function <code class="language-plaintext highlighter-rouge">ReadOnlyQueueParameters::new</code>,
which received values for all the fields. Of course, this meant that I couldn’t use the nice struct
literal syntax anymore, which made the initialization quite awkward, as there are a lot of fields:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">params</span> <span class="o">=</span> <span class="nn">ReadOnlyQueueParameters</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span>
    <span class="nn">ManagerType</span><span class="p">::</span><span class="n">Slurm</span><span class="p">,</span>
    <span class="mi">1</span><span class="p">,</span>
    <span class="mi">4</span><span class="p">,</span>
    <span class="nn">Duration</span><span class="p">::</span><span class="nf">from_secs</span><span class="p">(</span><span class="mi">3600</span><span class="p">),</span>
    <span class="nb">None</span><span class="p">,</span>
    <span class="nf">Some</span><span class="p">(</span><span class="mi">10</span><span class="p">),</span>
    <span class="nb">None</span><span class="p">,</span>
    <span class="o">...</span>
<span class="p">);</span>
</code></pre></div></div>

<p>This code was bothering me for a long time, because everytime I had to add a new field to the
struct, I would have to extend this <code class="language-plaintext highlighter-rouge">new</code> function with yet another parameter, and the call-sites
would become ever more confusing. This is how such a change would typically look as a diff:</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gd">-   None,
-   None
- )
</span><span class="gi">+   None,
+   None,
+   None,
+ )
</span></code></pre></div></div>

<p>Yeah, not great.</p>

<h2 id="the-solution">The solution</h2>

<p>After a few years of working with the annoying read-only wrapper, I finally lost my patience with
it and decided to refactor it. I quickly realized that the behavior I want can be achieved in a
very straightforward and elegant way by not duplicating the fields, but simply storing the mutable
struct directly as a field inside a read-only struct. The read-only struct would then expose the
fields of the mutable struct in a read-only way:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="nf">ReadOnlyQueueParameters</span><span class="p">(</span><span class="n">QueueParameters</span><span class="p">);</span>

<span class="k">impl</span> <span class="n">ReadOnlyQueueParameters</span> <span class="p">{</span>
    <span class="k">pub</span> <span class="k">fn</span> <span class="nf">new</span><span class="p">(</span><span class="n">params</span><span class="p">:</span> <span class="n">QueueParameters</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="k">Self</span><span class="p">(</span><span class="n">params</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">pub</span> <span class="k">fn</span> <span class="nf">backlog</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">u32</span> <span class="p">{</span>
        <span class="k">self</span><span class="na">.0</span><span class="py">.backlog</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>With this approach, I can construct the parameters using the struct literal syntax:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">queue</span> <span class="o">=</span> <span class="nn">ReadOnlyQueueParameters</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">QueueParameters</span> <span class="p">{</span>
    <span class="n">manager</span><span class="p">:</span> <span class="o">...</span><span class="p">,</span>
    <span class="n">backlog</span><span class="p">:</span> <span class="o">...</span><span class="p">,</span>
    <span class="o">...</span>
<span class="p">});</span>
</code></pre></div></div>

<p>But at the same time, I cannot modify the fields from the outside after construction anymore.
Truly a “we have init-only fields at home” solution. I could even remove the accessor methods by
implementing the <a href="https://doc.rust-lang.org/std/ops/trait.Deref.html"><code class="language-plaintext highlighter-rouge">Deref</code></a> trait for the wrapper, but here I decided to keep the code more explicit.</p>

<p>This is, of course, yet another instance of the <a href="https://rust-unofficial.github.io/patterns/patterns/behavioural/newtype.html">newtype pattern</a>, which I use all the time, and even <a href="https://github.com/Kobzol/rust-course-fei">explain</a> it to my students. And the solution is really quite trivial in hindsight. So it was a bit
embarrassing to me that it took me so long to realize that it can be used to solve this annoyance.
Although frankly, the main issue was to find the motivation to spend five minutes to refactor code
that was bothering me for years :)</p>

<blockquote>
  <p>For these simple newtype wrappers, it would be nice to be able to simplify the implementation of the
<code class="language-plaintext highlighter-rouge">From</code> trait, to go from the inner field to a value of the newtype. More about that <a href="https://github.com/rust-lang/rfcs/pull/3809">soon</a>.</p>
</blockquote>

<h2 id="conclusion">Conclusion</h2>

<p>Anyway, that’s all. The newtype pattern just keeps on giving! Do you have other useful use-cases for
it? Let me know on <a href="https://www.reddit.com/r/rust/comments/1n5nkzu/combining_struct_literal_syntax_with_readonly">Reddit</a>.</p>]]></content><author><name></name></author><category term="rust" /><summary type="html"><![CDATA[Recently, I found myself struggling with a small annoyance related to struct field visibility and struct initialization in Rust. It’s no rocket science, but I thought about it long enough that I might as well turn it into a blog post.]]></summary></entry><entry><title type="html">Why doesn’t Rust care more about compiler performance?</title><link href="https://kobzol.github.io/rust/rustc/2025/06/09/why-doesnt-rust-care-more-about-compiler-performance.html" rel="alternate" type="text/html" title="Why doesn’t Rust care more about compiler performance?" /><published>2025-06-09T11:00:00+00:00</published><updated>2025-06-09T11:00:00+00:00</updated><id>https://kobzol.github.io/rust/rustc/2025/06/09/why-doesnt-rust-care-more-about-compiler-performance</id><content type="html" xml:base="https://kobzol.github.io/rust/rustc/2025/06/09/why-doesnt-rust-care-more-about-compiler-performance.html"><![CDATA[<p>Perhaps the most often repeated complaint about Rust is its slow feedback loop and long compilation times. I hear about it all the time; in Rust podcasts, <a href="https://loglog.games/blog/leaving-rust-gamedev">blog</a> <a href="https://gist.github.com/rtfeldman/77fb430ee57b42f5f2ca973a3992532f">posts</a>, <a href="https://blog.rust-lang.org/2025/02/13/2024-State-Of-Rust-Survey-results/#challenges">surveys</a>, conference talks or offline discussions. I also regularly complain about it, being a Rust user myself!</p>

<p>Recently, in addition to the usual compile times complaints, I also started noticing the following sentiments being expressed by frustrated Rust developers: “Why doesn’t the Rust Project <em>care more</em> about this pressing and known issue? Why don’t they <em>do something</em> about it?”. As a member of the <a href="https://www.rust-lang.org/governance/teams/compiler#team-wg-compiler-performance">Rust compiler performance working group</a>, I take these questions very seriously, and I of course have some opinions on the topic. In this blog post, I would like to offer some thoughts that might serve as answers to these (and other similar) questions.</p>

<blockquote>
  <p>Disclaimer: all opinions expressed in this post <strong>are mine only</strong> and do not necessarily represent the broader views of the Rust Project (the diverse set of contributors and maintainers that contribute to the Rust toolchain).</p>
</blockquote>

<h3 id="do-we-even-care">Do we even care?</h3>

<p>First, let me assure you - yes, we (as in, the Rust Project) <em>absolutely do care</em> about the performance of our beloved compiler, and we put in a lot of effort to improve it. We <a href="https://this-week-in-rust.org/blog/2025/05/28/this-week-in-rust-601/#rust-compiler-performance-triage">triage</a> performance improvements and regressions each week. We run a comprehensive <a href="https://github.com/rust-lang/rustc-perf">benchmark suite</a> after every <a href="https://github.com/rust-lang/rust/pull/141545#issuecomment-2908028887">merged <abbr title="Pull Request">PR</abbr></a>. We enthusiatically welcome any performance improvements (unless they have complicated trade-offs, more on that later), and try to quickly revert (or fix) PRs that introduce performance regressions. Very smart people are continously working on finding bottlenecks and <a href="https://nnethercote.github.io/2025/03/19/how-to-speed-up-the-rust-compiler-in-march-2025.html">speeding up the compiler</a>. And some significant improvements to make compilation faster by default are currently <a href="https://github.com/rust-lang/rust/pull/140525">in the works</a>.</p>

<p>And all this effort shows results; Rust build performance has improved quite a lot over the past few years! When we talk about the long-term trends, we usually show <a href="https://perf.rust-lang.org/dashboard.html">this dashboard</a>, but I find it a bit dull, because it averages results of several benchmarks together and the benchmarks are quite short, so they start running into diminishing returns. Instead, I did a little experiment to show how did the build performance evolve on my favourite test subject, <a href="https://github.com/it4innovations/hyperqueue">hyperqueue</a>. I took its first commit (from March 2021<sup id="fnref:old-commit"><a href="#fn:old-commit" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>) and compiled it with a few different Rust compiler versions, with ~1 year increments between them<sup id="fnref:experiment"><a href="#fn:experiment" class="footnote" rel="footnote" role="doc-noteref">2</a></sup>. I did this on a relatively modest laptop with 8 AMD cores running Linux. Here are the results:</p>

<table>
  <thead>
    <tr>
      <th><code class="language-plaintext highlighter-rouge">rustc</code> version</th>
      <th style="text-align: right">Clean build [s]</th>
      <th style="text-align: right">Incremental rebuild [s]</th>
      <th style="text-align: right">Speedup (clean build)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">1.61.0</code> (May 2022)</td>
      <td style="text-align: right">26.1s</td>
      <td style="text-align: right">~0.39</td>
      <td style="text-align: right">-</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">1.70.0</code> (June 2023)</td>
      <td style="text-align: right">20.2s</td>
      <td style="text-align: right">~0.37</td>
      <td style="text-align: right">1.29x</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">1.78.0</code> (May 2024)</td>
      <td style="text-align: right">17.0s</td>
      <td style="text-align: right">~0.30</td>
      <td style="text-align: right">1.53x</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">1.87.0</code> (May 2025)</td>
      <td style="text-align: right">14.7s</td>
      <td style="text-align: right">~0.26</td>
      <td style="text-align: right">1.77x</td>
    </tr>
  </tbody>
</table>

<p>On this benchmark, the compiler is almost twice as fast than it was three years ago. Of course, this is just a single data point, in different cases the speedup will be larger or smaller, and it will likely be smaller in general on other platforms than x64 Linux, because we spend the most effort optimizing that specific target. But the fact remains that the performance of the compiler is continuously improving.</p>

<h3 id="its-still-not-fast-enough-though">It’s still not <em>fast enough</em> though</h3>

<p>Now, all that is nice, but it doesn’t change the fact that for many Rust developers, compilation performance is still a big bottleneck, and they could be much more productive if the feedback loop was an order of magnitude (or more) faster. To be fair, this will depend on who you ask, e.g. some C++ developers don’t mind Rust’s compilation times at all, as they are used to the same (or worse) build times, while Python programmers likely aren’t very impressed by Rust’s feedback loop speed. But it’s definitely not a stretch to say that for many users, Rust compilation just isn’t <em>fast enough</em> today, and that is a problem.</p>

<p>Before we examine that problem in more detail, I think that it’s interesting to think about whether the problem is even fundamentally solvable, and what is the end goal that we would eventually like to achieve. Can Rust ever hope to achieve near-instant (re)build speeds, with its complex type system, borrow checking, monomorphization, proc macros and build scripts, large translation units, machine code generation and a “rebuild the world from source” compilation model? When it has a historical track record of favoring runtime (rather than compilation) performance at <a href="https://www.pingcap.com/blog/rust-compilation-model-calamity/">nearly every opportunity</a>?</p>

<p>I think that it depends on the use-case. If we’re talking about building projects with hundreds of dependencies from scratch or doing super-duper <abbr title="Link-time optimization">LTO</abbr>-optimized release builds, I think that we won’t ever get to a point where the build will be truly <em>instant</em>. On the other hand, I personally believe (although others might disagree!) that it is not fundamentally impossible to have a Rust compiler that will be able to almost instantly rebuild a Rust project of nearly any size in incremental (and at least moderately optimized) mode after making a small change to the source code, one where rebuilds are truly <code class="language-plaintext highlighter-rouge">O(number of performed changes)</code> and not <code class="language-plaintext highlighter-rouge">O(size of the codebase)</code>. We might have to make some trade-offs, particularly around the resulting runtime performance, but I think that we can get there. In fact, we can even see examples that can already achieve something akin to this (in somewhat limited scenarios for now)
<a href="https://www.reddit.com/r/rust/comments/1j8z3yb/media_dioxus_subsecond_rust_hotpatch_engine/">today</a>!</p>

<p>And it’s not like we don’t have some ideas on how to get (at least closer) to this goal. There are several “northstar” approaches for speeding up the compilation process in various ways, like the <a href="https://blog.rust-lang.org/2023/11/09/parallel-rustc/">parallel frontend</a>, alternative <a href="https://github.com/rust-lang/rustc_codegen_cranelift">codegen backends</a>, defaulting to a <a href="https://blog.rust-lang.org/2024/05/17/enabling-rust-lld-on-linux/">faster linker</a>, deferring code generation (<a href="https://github.com/rust-lang/rust/issues/38913">MIR-only rlibs</a>, <a href="https://github.com/rust-lang/compiler-team/issues/829">-Zhint-mostly-unused</a>), <a href="https://github.com/rust-lang/compiler-team/issues/790">avoiding useless workspace rebuilds</a>, having smarter incremental compilation (including <a href="https://github.com/davidlattimore/wild">incremental linking</a> or even binary hot-patching), and many others.</p>

<p>Some of these approaches are not prepared for wider usage yet, but others are, and you can <a href="https://github.com/Kobzol/cargo-wizard">opt into</a> them today. They usually (but not always) help. I believe that if we had a magic wand and we could land all these changes today, we could get very snappy incremental rebuilds for a large (certainly much larger than today) fraction of Rust projects.</p>

<p>And it’s not like “only” the broader Rust community would benefit from this. Improving build performance makes Rust toolchain contributors themselves much more productive. It reduces the time required to build the compiler itself when making changes to it, it speeds up our <abbr title="Continous Integration">CI</abbr> workflows, reduces the time that we have to wait for tests and compiler performance benchmarks… Everyone would benefit! So what’s blocking us from making progress faster? <del>WHy dON’t We dO SOmEtHIng ABoUt iT??!</del><sup id="fnref:resist"><a href="#fn:resist" class="footnote" rel="footnote" role="doc-noteref">3</a></sup></p>

<h3 id="so-why-dont-we-do-more">So why don’t we do more?</h3>

<p>This is a tough question, one that doesn’t have a simple answer. I will try to provide a variety of reasons (in mostly arbitrary order) why I think we have not been making progress faster, based on my personal experiences working within the Rust Project.</p>

<h4 id="technical-reasons">Technical reasons</h4>
<p>First, let’s start with the technical reasons, which are in a way the most straightforward. Simply put, making non-trivial improvements to the performance of the Rust compiler is hard! It is a large codebase<sup id="fnref:compiler-loc"><a href="#fn:compiler-loc" class="footnote" rel="footnote" role="doc-noteref">4</a></sup> that has a lot of technical <del>debt</del> <a href="https://jyn.dev/technical-debt-is-different-from-technical-risk">risk</a>, as probably all large (compiler) codebases do, and although we provide a <a href="https://rustc-dev-guide.rust-lang.org">guide</a> for contributing to it, it still takes time before you can understand what’s going on. In fact, there is probably not a single person that understands the whole compiler codebase (maybe except for <a href="https://github.com/rust-lang/rust/pulls?q=is%3Apr+author%3Acompiler-errors+is%3Amerged">compiler-errors</a> :laughing:).</p>

<p>While it is possible to make performance improvements even without deeply understanding how the compiler works, by profiling and micro-optimizing its various parts, this approach can only get you so far, and most of the low-hanging fruit has already been picked<sup id="fnref:gem"><a href="#fn:gem" class="footnote" rel="footnote" role="doc-noteref">5</a></sup>. A lot of things have been optimized and overfitted to reach a “local performance minimum” on a variety of benchmarks and use-cases, so even if an improvement is made for some use-cases, it often regresses something else, which can lead to that localized improvement not being accepted.</p>

<p>We also have to consider various trade-offs when deciding whether to land certain performance optimizations. For example, we know a few <a href="/rust/rustc/2023/10/21/make-rust-compiler-5percent-faster.html">tricks</a> that could make the compiler a bit faster on x64 Linux, the most popular target. We could compile <code class="language-plaintext highlighter-rouge">rustc</code> with support for “newer” instruction architectures, e.g. AVX256, which seems to provide <a href="https://perf.rust-lang.org/compare.html?start=2efb0cd4b2240c878c36112a5ec207837c15928a&amp;end=18256ff35e7885ae108986756552f14a16d240d0&amp;stat=instructions:u">some wins</a>. However, that would mean that the compiler would no longer work on older x64 CPUs that we still <a href="https://blog.rust-lang.org/2022/08/01/Increasing-glibc-kernel-requirements/">officially support</a>! Or we could use a different memory allocator (e.g. <a href="https://github.com/purpleprotocol/mimalloc_rust">mimalloc</a>), which also seems to provide some modest <a href="https://perf.rust-lang.org/compare.html?
start=6c8347b9588a302afebb81a1ae6daa64ec37abd0&amp;end=2ae4c6dae2c8206758e06df89d1da08c5eb52f11&amp;stat=instructions:u">performance wins</a>, but at the cost of <a href="https://perf.rust-lang.org/compare.html?
start=6c8347b9588a302afebb81a1ae6daa64ec37abd0&amp;end=2ae4c6dae2c8206758e06df89d1da08c5eb52f11&amp;stat=max-rss">increased 
memory usage</a>, which could 
cause <code class="language-plaintext highlighter-rouge">rustc</code> to go <abbr title="Out-of-memory">OOM</abbr> on devices with smaller amounts of <abbr title="Random-access Memory">RAM</abbr>. Maybe you’re thinking why can’t we just ship multiple versions of the compiler (for the same target) with different optimizations and then let people (or <code class="language-plaintext highlighter-rouge">rustup</code>) choose which one to use? Well, building the most optimized variant of the compiler for Linux already takes an enormous amount of <abbr title="Continous Integration">CI</abbr> resources right now, not to mention that distributing the toolchain in multiple variants could balloon the amount of data we ship to users over the network, which is already gigantic. There are simply trade-offs everywhere.</p>

<p>I think that to achieve truly significant build performance wins, there are mostly two ways forward. The first one, which I find highly promising, is improving certain compilation workflows. We don’t necessarily have to make the whole compiler faster if we can vastly improve the performance (or reduce the amount of work required) of specific workflows that currently limit the productivity of a large fraction of Rust developers. An example of this is the <a href="https://github.com/rust-lang/compiler-team/issues/790">Relink, don’t rebuild</a> proposal, which has the potential of reducing the amount of compilation that has to be performed when you modify a crate in a large Cargo workspace. Approaches such as this one do not necessarily make the compiler faster <em>per se</em>, rather they make the compilation process smarter, which is an area in which <code class="language-plaintext highlighter-rouge">rustc</code> could be improved a lot. Many of these potential workflow improvements are also not strictly tied only to the compiler itself, but they happen at 
the intersection of <code class="language-plaintext highlighter-rouge">rustc</code> and the used build system (most often Cargo).</p>

<p>I hope that some of these ideas will make the most common Rust compilation workflows much faster in the short-to-medium term, without requiring massive changes to the implementation of the compiler. Nevertheless, even such targeted optimizations are not easy to land. Achieving an impressive performance win with a proof-of-concept is the “easy” part. But then comes the long tail of the really hard work - making sure that your change supports all the edge-cases and quirks of the Rust compilation process, works on all targets, doesn’t regress important use-cases and benchmarks, is not a maintenance nightmare, keeps backwards compatibility, etc.</p>

<p>The second way forward, as you might have guessed, is… performing massive changes and/or refactorings to the implementation of the compiler. However, that is of course challenging, for a number of reasons. First, you need to do a “vibe check”<sup id="fnref:vibeck"><a href="#fn:vibeck" class="footnote" rel="footnote" role="doc-noteref">6</a></sup> with the rest of the compiler team with a <a href="https://github.com/rust-lang/rfcs/blob/master/text/2904-compiler-major-change-process.md">Major Change Proposal</a>, to make sure that other compiler maintainers are on board with you performing such a major change. Then you will obviously need to implement these changes, which can take a lot of effort. For example, if you change one thing at the “bottom” layer of the compiler, you will then have to go through hundreds of places and fix them up, and also potentially fix many test cases, which can be very time-consuming<sup id="fnref:ai-help"><a href="#fn:ai-help" class="footnote" rel="footnote" role="doc-noteref">7</a></sup>. You will also need to find reviewer(s) with bandwidth to approve your changes, which can span over many PRs and weeks, months or even years of work.</p>

<p>Performing large cross-cutting changes is also tricky because it will necessarily conflict with a lot of other changes being done to the compiler in the meantime. You can try to perform the modifications outside the main compiler tree, but that is almost doomed to fail, given how fast the compiler changes<sup id="fnref:rollups"><a href="#fn:rollups" class="footnote" rel="footnote" role="doc-noteref">8</a></sup>. Alternatively, you try to land the changes in a single massive <abbr title="Pull Request">PR</abbr>, which might lead to endless rebases (and might make it harder to find a reviewer). Or, you will need to do the migration incrementally, which might require maintaining two separate implementations of the same thing for a long time, which can be exhausting.</p>

<p>An example of a large internal change (albeit one that is not <em>directly</em> related to performance) is the reimplementation of the compiler’s <a href="https://github.com/rust-lang/trait-system-refactor-initiative">trait solver</a>. Even though it is being worked on by very smart and incredibly skilled people, this is a process that takes <em>several years</em> to finish. That’s the scale that we’re dealing with if we would like to perform some massive refactoring of the compiler internals, and it should set the expectations for people that ask us when are we finally going to rewrite the whole compiler using <a href="https://www.youtube.com/watch?v=IroPQ150F6c">Data-oriented Design</a> to make it a bazillion times faster.</p>

<p>Speaking of <abbr title="Data-oriented Design">DoD</abbr>, an additional thing to consider is the maintainability of the compiler codebase. Imagine that we swung our magic wand again, and rewrote everything over the night using <abbr title="Data-oriented Design">DoD</abbr>, <abbr title="Single Instruction Multiple Data">SIMD</abbr> vectorization, hand-rolled assembly, etc. It would (possibly) be way faster, yay! However, we do not only care about immediate performance, but also about our ability to make long-term improvements to it. If we overfitted the performance using code that is hard to evolve, understand, debug or maintain, it wouldn’t serve us well in the long term. The compiler receives contributions from hundreds of volunteers, and we want to keep the codebase (at least somewhat) approachable to them. So the long-term maintaintability of the codebase is also something that we need to keep in mind.</p>

<p>While the challenges presented above are very real, they are of course not exactly <em>unique</em> to <code class="language-plaintext highlighter-rouge">rustc</code>.  The fact that making major changes to a large (compiler) codebase is difficult is probably not very surprising. I’m sure that GCC/LLVM/Clang maintainers also have a lot of ideas on how to improve or speed up things, but it takes a lot of effort and time to get these done. But there are also other reasons why compiler performance is not the only thing we focus on.</p>

<h4 id="prioritization">Prioritization</h4>

<p>Even though many Rust users seem to want compiler performance to be <strong>the</strong> primary priority of the Rust Project, we should not forget that there are also other priorities that we have to keep in mind.</p>

<p>The Rust compiler is (at least in my opinion) very stable and dependable, and I can count on a new version being released every six weeks. That is no small feat! And we should not take it for granted. It does not happen simply by starting from a working state and then not doing anything. There is so much work that goes into simply making sure that things are not on fire, our infrastructure and <abbr title="Continous Integration">CI</abbr> works, really bad bugs are being fixed in a timely manner, incoming issues are being triaged, backwards compatibility is still being upheld, PRs are being reviewed, security issues are being quickly resolved, we stay on top of changes in external dependencies, such as LLVM, and much, much more. And we have to ensure all of that works on all of our <a href="https://doc.rust-lang.org/nightly/rustc/platform-support.html">supported targets</a>! At this moment, that’s 8 Tier 1 targets (those have to build and all tests have to pass) and 91 (!) Tier 2 targets (those have to at least build). By a large part,
that work is being done by volunteers who care so much about Rust and its toolchain (:heart:) and want to keep it working as well as it was so far. There is simply a ton of work to do on Rust<sup id="fnref:100-engineers"><a href="#fn:100-engineers" class="footnote" rel="footnote" role="doc-noteref">9</a></sup>.</p>

<p>And all that work is of course eating away from time that could be spent e.g. on making the compiler faster. If you do not consider that work to be all that important, ask yourself if you would enjoy a twice faster compiler if it randomly started miscompiling your code :)</p>

<p>Another thing is that both the language and the compiler are getting new features all the time. New <a href="https://github.com/rust-lang/compiler-team/issues/748">compiler flags</a> to support projects like <a href="https://rust-for-linux.com/">Rust for Linux</a>, <a href="https://github.com/rust-lang/rust/pull/132833">syntactical improvements</a>, <a href="https://github.com/rust-lang/rust/pull/132706">complex language features</a>, and so many other things. In general, the language seems to be far from “done”, and so many things are in process; there are almost <a href="https://github.com/rust-lang/rfcs/pulls?q=is%3Aopen+is%3Apr+-label%3Anot-rfc+">200 open RFCs pull requests</a>, and many other RFCs were accepted, but haven’t been implemented or stabilized yet. At any given point, we have ~700 <a href="https://github.com/rust-lang/rust/pulls">opened PRs</a> on the main Rust repository, we have over 10 thousand <a href="https://github.com/rust-lang/rust/issues">open issues</a>, there’s just so much going on.</p>

<p>And it <a href="https://blog.rust-lang.org/2025/02/13/2024-State-Of-Rust-Survey-results/what-do-you-think-about-rust-evolution.png">seems</a> that Rust users want Rust to evolve as fast as it does today, or even faster, which means more changes! When was the last time you “just 
wanted this small feature X to be finally stabilized” so that you could make your code nicer? It is perfectly fine to want new features from a language, but we should not forget that (amongst other things, such as design discussions) it requires a lot of experimentation, refactoring, bug fixes, testing and implementation effort in the compiler, which again takes time from optimizing performance. Not to mention that adding new features usually makes compiler performance slightly worse, as it tend to do more work, rather than the opposite.</p>

<h4 id="contributors">Contributors</h4>

<p>In the end, the amount of effort that gets spent on optimizing the performance of the compiler ultimately depends on the people who contribute to it and maintain it. They are often volunteers, and they have diverse interests. Some (compiler) contributors are not interested in doing performance optimizations at all, and that is perfectly fine! Remember, <a href="https://blog.m-ou.se/rust-is-not-a-company/">Rust is not a company</a> (this is a great blog post by Mara Bos, I recommend you to go read it!). We don’t tell people what they should work on, nor do we assign them tasks or tickets. If there won’t be people interested in making the compiler faster, it mostly likely will not get done, it’s as simple as that.</p>

<p>That being said, there are certain ways of how we can <em>empower</em> people to work on compiler performance. The Rust Project now has the <a href="https://blog.rust-lang.org/2024/08/12/Project-goals/">Project Goals</a> program, which specifies a set of goals and projects that we consider to be important, and we explicitly welcome contributions (and promise to provide reviews and support) in these areas. I’m going to try to make compiler performance be one of the flagship goals in the near future, as I think that it really deserves to be one. That could perhaps help motivate more people to work on it.</p>

<p>Even though it may sound a bit banal, making progress faster is also (at least partially) a matter of funding. Sometimes I hear people complaining about certain projects designed to improve compilation performance taking too long to be ready. It sounds like they expect that there is a fully staffed team of well-paid engineers working on these projects. Well, we should not forget that the Rust Project is still mostly built on volunteers, and many people contribute to it in their free time. Many improvements (including some important compiler performance projects) to Rust were literally driven by a single university student who works tirelessly to make Rust better day by day, without maybe even getting paid for it in any way, and the reason why progress pauses can be (for example) that they must finish university exams or do some actual paid work so that they could afford rent and food.</p>

<p>During the recent <a href="https://rustweek.org/">RustWeek</a> conference and the associated All Hands event, I saw an increased interest from various companies to invest into improving the performance of the Rust compiler, which is great! Improving the performance of <code class="language-plaintext highlighter-rouge">rustc</code> requires long-term concentrated effort, and providing stable funding for contributors doing that work is definitely one of the ways of how we could make progress faster. However, it is also important that making improvements is not just about doing the implementation work; someone will also need to review it, and then maintain it in the future. In an ideal world, companies would fund long-term ongoing work on the maintenance of the compiler, rather than paying their employees to implement an optimization that scratches their itch and then disappearing. Sometimes, the best way to help something move forward in Rust is not to implement it, but gather knowledge, become an expert in some compiler area, and help review the code of others, so that you can unblock other people to do the actual work :) But that of course takes a long-term investment (both in terms of time and money).</p>

<p>Based on my experience, the interest amongst Rust compiler contributors to work on compiler performance does not correspond to the amount of discourse this topic gets online (and again, this is fine!). If I used the magic wand again, and could persuade more contributors to care about it, would I do it? Probably yes. Would it be a good thing for Rust, long-term? I don’t know! So far, the development of the language and compiler seems to be going fine, and who am I to determine priorities. I trust the people in the Project to collectively do what’s best for Rust, both today and in the future.</p>

<p>One thing that I find interesting is that maybe more people would actually do performance work if they didn’t feel the need to work on so many other things at the same time. Case in point: when I started contributing to Rust back in 2021, my primary interest was compiler performance. So I started doing some optimization work. Then I noticed that the <a href="https://github.com/rust-lang/rustc-perf">compiler benchmark suite</a> could use some maintenance, so I started working on that. Then I noticed that we don’t compile the compiler itself with as many optimizations as we could, so I started working on adding support for <a href="/rust/rustc/2022/10/27/speeding-rustc-without-changing-its-code.html"><abbr title="Link-time optimization">LTO</abbr>/PGO/BOLT</a>, which further led to improving our <abbr title="Continous Integration">CI</abbr> infrastructure. Then I noticed that we wait quite a long time for our <abbr title="Continous Integration">CI</abbr> workflows, and started <a href="/rust/rustc/2023/07/30/optimizing-rust-ci-2023.html">optimizing them</a>. Then I started running the <a href="https://www.rust-lang.org/governance/teams/launching-pad#team-community-survey">Rust Annual Survey</a>, 
then our <a href="https://blog.rust-lang.org/2025/05/08/gsoc-2025-selected-projects/">GSoC program</a>, then improving our <a href="https://github.com/rust-lang/bors">bots</a>, then… Many of these contributions did (at least I hope) affect the performance of compiler in some indirect way, either by unblocking or supporting other contributors or improving our infrastructure (in various meanings of that word). However, only now, after a few years of getting sidetracked, I’m finally trying to get back to doing direct compiler performance work again.</p>

<p>In other words, the more help we can get with maintaining Rust (regardless in which specific area!), the better the chance that we will collectively have more time to improve the performance of the compiler :)</p>

<h2 id="conclusion">Conclusion</h2>

<p>Funnily enough, after I wrote this post, I realized that I could probably replace the term “compiler performance” with pretty much anything else that people want from Rust (“why doesn’t Rust do X”), and a lot of the same reasoning would still apply. In this regard, compiler performance is not that special, it’s just one of (so) many (very important!) things that we care about and that we try to improve to the best of our ability.</p>

<p>In terms of future compiler performance work, I’m hopeful about several initiatives that were discussed at the All Hands events, and also about <a href="https://github.com/rust-lang/rust/pull/140525">making LLD the default linker on Linux</a>, which will hopefully finally land in the upcoming months. I’m also planning to run a compiler performance survey <a href="https://github.com/rust-lang/surveys/pull/337">soon</a>, so that we can find out what workflows cause bottlenecks for Rust users, and I also want to build better infrastructure for profiling what the compiler is actually doing during the compilation process.</p>

<p>I hope that this blog post provided a bit of insight into why making progress in build performance is not so easy. If you’d like to join the fray and help with it, we would be <a href="https://rustc-dev-guide.rust-lang.org/">happy to see you</a>! Let me know what you think on <a href="https://www.reddit.com/r/rust/comments/1l77jso/why_doesnt_rust_care_more_about_compiler">Reddit</a>.</p>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:old-commit">
      <p>I used an old commit so that it can be compiled even with old(er) versions of <code class="language-plaintext highlighter-rouge">rustc</code>. <a href="#fnref:old-commit" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:experiment">
      <p>Luckily, I still had all the stable compiler versions downloaded from my recent <a href="/rust/rustc/2025/05/16/evolution-of-rustc-errors.html">compiler errors experiment</a> :sweat_smile: <a href="#fnref:experiment" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:resist">
      <p>Sorry, couldn’t resist. <a href="#fnref:resist" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:compiler-loc">
      <p>At least for some definition of large. The compiler consists of ~600k lines of Rust code, and the standard library has a similar size. <a href="#fnref:compiler-loc" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:gem">
      <p>Not all of it though. Check out this recent <a href="https://github.com/rust-lang/rust/pull/142095"><em>gem</em></a> of a <abbr title="Pull Request">PR</abbr>. <a href="#fnref:gem" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:vibeck">
      <p>I recently saw someone call this <em>vibeck</em> (named after the abbreviated terms typeck and borrowck used in the compiler), which I find super cute. <a href="#fnref:vibeck" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:ai-help">
      <p>If you are thinking “why can’t AI do this?”, it probably could help with a lot of things in this area, but especially differences in compiler diagnostics (which is a large part of what our test suite checks) often come down to judgement calls that we still (luckily) leave to humans. <a href="#fnref:ai-help" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:rollups">
      <p>On average, we merge around 25-30 PRs <a href="https://rust-lang.github.io/rustc-pr-tracking/">each day</a>. <a href="#fnref:rollups" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:100-engineers">
      <p>I like to say that if you gave me a hundred full-time engineers, I would find all of them something to work on in the Rust toolchain immediately. <a href="#fnref:100-engineers" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="rust" /><category term="rustc" /><summary type="html"><![CDATA[Perhaps the most often repeated complaint about Rust is its slow feedback loop and long compilation times. I hear about it all the time; in Rust podcasts, blog posts, surveys, conference talks or offline discussions. I also regularly complain about it, being a Rust user myself!]]></summary></entry><entry><title type="html">Reducing Cargo target directory size with -Zno-embed-metadata</title><link href="https://kobzol.github.io/rust/rustc/2025/06/02/reduce-cargo-target-dir-size-with-z-no-embed-metadata.html" rel="alternate" type="text/html" title="Reducing Cargo target directory size with -Zno-embed-metadata" /><published>2025-06-02T12:00:00+00:00</published><updated>2025-06-02T12:00:00+00:00</updated><id>https://kobzol.github.io/rust/rustc/2025/06/02/reduce-cargo-target-dir-size-with-z-no-embed-metadata</id><content type="html" xml:base="https://kobzol.github.io/rust/rustc/2025/06/02/reduce-cargo-target-dir-size-with-z-no-embed-metadata.html"><![CDATA[<p>Disk usage of the <code class="language-plaintext highlighter-rouge">target</code> directory is a commonly cited annoyance with Rust (and Cargo)
– in the last year’s <a href="https://blog.rust-lang.org/2025/02/13/2024-State-Of-Rust-Survey-results/#challenges">Annual Survey</a>,
it was the third most pressing issue of Rust users, right after slow compilation<sup id="fnref:disk-vs-compilation"><a href="#fn:disk-vs-compilation" class="footnote" rel="footnote" role="doc-noteref">1</a></sup> and subpar
debugging experience. Given the “build everything from source” compilation model of Rust, and both debuginfo and incremental compilation being enabled by default in the <code class="language-plaintext highlighter-rouge">dev</code> Cargo profile, it is unlikely that the <code class="language-plaintext highlighter-rouge">target</code> directory will ever become <em>lean</em> and small. However, there are still ways of how we could reduce the <code class="language-plaintext highlighter-rouge">target</code> directory size by a non-trivial amount. I will describe a brand-new method of achieving that in this blog post.</p>

<p>Note that there are also initiatives to reduce the size of the <code class="language-plaintext highlighter-rouge">target</code> directory along the temporal axis, i.e. prevent it from ballooning over time (see <a href="https://github.com/rust-lang/cargo/issues/12633">Cargo Garbage Collection</a>). This post is more about how to reduce the size of the target directory overall.</p>

<h2 id="what-takes-up-the-space-anyway">What takes up the space, anyway?</h2>

<p>It is not the focus of this post to dive deep into exploring what exactly takes space in the <code class="language-plaintext highlighter-rouge">target</code>
directory, but I still think that it is useful to provide at least some background on this. I took
my favourite work project, <a href="https://github.com/It4innovations/hyperqueue">hyperqueue</a>, and compiled
it in several modes to compare the resulting <code class="language-plaintext highlighter-rouge">target</code> directory sizes<sup id="fnref:jemalloc"><a href="#fn:jemalloc" class="footnote" rel="footnote" role="doc-noteref">2</a></sup>, using <code class="language-plaintext highlighter-rouge">rustc 1.89.0-nightly (2eef47813 2025-05-22)</code>:</p>

<table>
  <thead>
    <tr>
      <th>Optimizations</th>
      <th>Incremental</th>
      <th>Debuginfo</th>
      <th style="text-align: right"><code class="language-plaintext highlighter-rouge">target</code> size [MiB]</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>No (<code class="language-plaintext highlighter-rouge">dev</code>)</td>
      <td>No</td>
      <td>No</td>
      <td style="text-align: right">462</td>
    </tr>
    <tr>
      <td>No (<code class="language-plaintext highlighter-rouge">dev</code>)</td>
      <td>Yes</td>
      <td>No</td>
      <td style="text-align: right">677</td>
    </tr>
    <tr>
      <td>No (<code class="language-plaintext highlighter-rouge">dev</code>)</td>
      <td>No</td>
      <td>Yes</td>
      <td style="text-align: right">870</td>
    </tr>
    <tr>
      <td>No (<code class="language-plaintext highlighter-rouge">dev</code>)</td>
      <td>Yes</td>
      <td>Yes</td>
      <td style="text-align: right">1316</td>
    </tr>
    <tr>
      <td>Yes (<code class="language-plaintext highlighter-rouge">release</code>)</td>
      <td>No</td>
      <td>No</td>
      <td style="text-align: right">396</td>
    </tr>
  </tbody>
</table>

<p>From the results, it’s clear that both debuginfo and incremental compilation caches take a lot of
space on disk. It might be that we could do something to reduce either of these, but there is one
additional thing that (kind of unnecessarily) causes <code class="language-plaintext highlighter-rouge">target</code> directory bloat, which is not
easily observable in this table, and that is the metadata of the compiled Rust crates.</p>

<h2 id="cargo-pipelining--metadata-duplication">Cargo pipelining &amp; metadata duplication</h2>

<p>First, we need to briefly talk about how Rust compilation works (in very, very simplified terms).
When a Rust library crate (<code class="language-plaintext highlighter-rouge">rlib</code>) is compiled in a “standard” way (i.e. no <abbr title="Link-time optimizations">LTO</abbr> or other funny
business) with Cargo, the compiler generates (amongst other things) two main outputs:</p>

<ul>
  <li>Metadata, which contains information necessary to link to that crate from other Rust code.</li>
  <li>Object code, which is the compiled assembly code of the Rust crate.</li>
</ul>

<p>The interesting thing about this is that the final object code isn’t actually needed in order to start
compiling Rust code that depends on a said library, metadata is enough (the object code will only be
needed at the final linking step). Cargo makes use of this fact, and when it compiles your crate graph,
it uses a technique called <a href="https://internals.rust-lang.org/t/evaluating-pipelined-rustc-compilation/10199">pipelining</a>.
Imagine that you have a binary that depends on crate <code class="language-plaintext highlighter-rouge">B</code>, which itself depends on crate <code class="language-plaintext highlighter-rouge">A</code>. When
compiling <code class="language-plaintext highlighter-rouge">A</code>, Cargo will tell the compiler (using <code class="language-plaintext highlighter-rouge">--emit=metadata,...</code>) to emit a <code class="language-plaintext highlighter-rouge">.rmeta</code> file
containing the metadata of <code class="language-plaintext highlighter-rouge">A</code> as soon as possible. Once that file is ready, we can start compiling
<code class="language-plaintext highlighter-rouge">B</code> by passing it the <code class="language-plaintext highlighter-rouge">.rmeta</code> file of <code class="language-plaintext highlighter-rouge">A</code>, even though the object code of <code class="language-plaintext highlighter-rouge">A</code> is not ready yet,
and thus partially overlap the compilation of both crates, which improves compile times. I will borrow
(and slightly extend) a cute ASCII diagram from <a href="https://internals.rust-lang.org/t/evaluating-pipelined-rustc-compilation/10199">Alex Crichton’s internals post</a> about pipelining
to succintly show how it works:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>          --- .rmeta of A generated
          v
[-libA----|--------]
          [-libB----|--------]
                             [-binary-----------]
0s        5s       10s       15s                25s
</code></pre></div></div>

<p>Pipelining has been enabled in Cargo for many years, so it’s nothing new, and if you use Cargo, you
are using pipelining every day, perhaps without even knowing about it. However, there is one
suboptimal thing in regard to disk space usage with the pipelining approach.</p>

<p>The problem is that once the process finishes, you will be left with two copies of the metadata of
each library on disk. The first copy is in the final <code class="language-plaintext highlighter-rouge">.rlib</code> file (along with the object code), and
the second copy is in the <code class="language-plaintext highlighter-rouge">.rmeta</code> file. As is usual with Cargo, this problem will balloon if you build
your project with multiple configuration options (different Cargo profiles, different compiler/linker
flags, etc.).</p>

<p>A related issue is that if you are compiling a library as a <code class="language-plaintext highlighter-rouge">dylib</code>,
so that you generate a Rust dynamic library (e.g. <code class="language-plaintext highlighter-rouge">.so</code> on Linux), that will also contain the
metadata, even though it should not be required to actually consume (call functions from) the
library at runtime. This can be annoying for people who ship Rust <code class="language-plaintext highlighter-rouge">dylib</code>s, even though that’s
usually not very common due to Rust currently not having a stable <abbr title="Application Binary Interface">ABI</abbr>.</p>

<h2 id="avoiding-duplicated-metadata">Avoiding duplicated metadata</h2>

<p>So, what can we do about it? Well, last year, I noticed that many years ago, the illustrious <code class="language-plaintext highlighter-rouge">@bjorn3</code> has
<a href="https://github.com/rust-lang/rust/issues/57076">proposed</a> an idea to introduce a compiler flag
that would cause the metadata to <em>only</em> be included in the <code class="language-plaintext highlighter-rouge">.rmeta</code> files, and no longer be present
in the <code class="language-plaintext highlighter-rouge">.rlib/.so</code> library files. I liked this idea, and since there already was a <a href="https://github.com/rust-lang/rust/pull/93945">prototype</a>
<a href="https://github.com/rust-lang/rust/pull/120855">implementation</a> prepared, it didn’t seem that hard to push it over the finish line. So I
nerd-sniped myself into finishing the implementation of the flag under bjorn3’s mentorship.
I didn’t actually make a lot of progress on it last year (because of… <a href="/research/2024/11/12/phd-postmortem.html">stuff</a>),
but this year I finally filed an <a href="https://github.com/rust-lang/compiler-team/issues/851"><abbr title="Major Change Proposal">MCP</abbr></a>, which is the usual process
for introducing new compiler flags, refactored the implementation a bit, added tests, and got it
<a href="https://github.com/rust-lang/rust/pull/137535">merged</a>. So long story short, you can now use an
unstable compiler flag in nightly Rust called <code class="language-plaintext highlighter-rouge">-Zembed-metadata=no</code>, which avoids the metadata
duplication.</p>

<p>When the flag is used, the <code class="language-plaintext highlighter-rouge">.rlib</code> file will only store a “metadata stub”, which contains
the bare minimum of information necessary for the <code class="language-plaintext highlighter-rouge">.rlib</code> to be loaded and validated, and the rest
of the metadata will be stored in a <code class="language-plaintext highlighter-rouge">.rmeta</code> file.</p>

<h2 id="cargo-integration">Cargo integration</h2>

<p>Normally, when a new unstable compiler flag is added, people can experiment with it using <code class="language-plaintext highlighter-rouge">RUSTFLAGS</code>.
However, the way this flag works means that you kind of need to combine it with <code class="language-plaintext highlighter-rouge">--emit=metadata</code>,
otherwise there will be <em>no</em> metadata generated at all, which would not be good. Furthermore, you
will now also need to pass the paths to the <code class="language-plaintext highlighter-rouge">.rmeta</code> files to the final <code class="language-plaintext highlighter-rouge">rustc</code> invocation that links
the top-level binary/library, otherwise the metadata will not be found. This essentially means that
this feature also needs some changes within Cargo, otherwise it wouldn’t be really usable with it.
So I went and implemented <a href="https://github.com/rust-lang/cargo/pull/15378">support</a> for this <code class="language-plaintext highlighter-rouge">rustc</code> flag in Cargo, and exposed it via a new
unstable Cargo flag called <code class="language-plaintext highlighter-rouge">-Zno-embed-metadata</code>.</p>

<p>Here are results of building <code class="language-plaintext highlighter-rouge">hyperqueue</code> using <code class="language-plaintext highlighter-rouge">-Zno-embed-metadata</code><sup id="fnref:build-cmd"><a href="#fn:build-cmd" class="footnote" rel="footnote" role="doc-noteref">3</a></sup>:</p>

<table>
  <thead>
    <tr>
      <th>Optimizations</th>
      <th>Incremental</th>
      <th>Debuginfo</th>
      <th style="text-align: right">Before [MiB]</th>
      <th style="text-align: right">After [MiB]</th>
      <th style="text-align: right">Reduction</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>No (<code class="language-plaintext highlighter-rouge">dev</code>)</td>
      <td>No</td>
      <td>No</td>
      <td style="text-align: right">462</td>
      <td style="text-align: right">336</td>
      <td style="text-align: right">-27.3%</td>
    </tr>
    <tr>
      <td>No (<code class="language-plaintext highlighter-rouge">dev</code>)</td>
      <td>Yes</td>
      <td>No</td>
      <td style="text-align: right">683</td>
      <td style="text-align: right">558</td>
      <td style="text-align: right">-18.3%</td>
    </tr>
    <tr>
      <td>No (<code class="language-plaintext highlighter-rouge">dev</code>)</td>
      <td>No</td>
      <td>Yes</td>
      <td style="text-align: right">874</td>
      <td style="text-align: right">748</td>
      <td style="text-align: right">-14.4%</td>
    </tr>
    <tr>
      <td>No (<code class="language-plaintext highlighter-rouge">dev</code>)</td>
      <td>Yes</td>
      <td>Yes</td>
      <td style="text-align: right">1325</td>
      <td style="text-align: right">1199</td>
      <td style="text-align: right">-9.5%</td>
    </tr>
    <tr>
      <td>Yes (<code class="language-plaintext highlighter-rouge">release</code>)</td>
      <td>No</td>
      <td>No</td>
      <td style="text-align: right">397</td>
      <td style="text-align: right">253</td>
      <td style="text-align: right">-36.3%</td>
    </tr>
  </tbody>
</table>

<p>As you can see, the benefits are most notable when building in <code class="language-plaintext highlighter-rouge">release</code> mode, or in general without
debuginfo or incremental compilation, as their size will usually dwarf the duplicated metadata contents.
Nevertheless, the disk space savings are quite nice, in my opinion.</p>

<p>Originally, I was also hoping that this could speed up compile times a little bit, as less data has
to be written to disk, but from my experiments it seems that the effect is rather miniscule, at least
on a Linux system with an <abbr title="Solid-state drive">SSD</abbr> disk.</p>

<p>One of the reasons why I wanted this flag to exist was to slightly reduce the size of the distributed
Rust compiler toolchain, particularly the standard library <code class="language-plaintext highlighter-rouge">.so</code> file. I didn’t get a chance to do
a full benchmark yet, as it’s a bit annoying to dogfood a Cargo feature, because I have to wait
until the Cargo change reaches the <code class="language-plaintext highlighter-rouge">beta</code> channel, so that I could use it for compiling the compiler
itself (the first stage of <code class="language-plaintext highlighter-rouge">rustc</code> is compiled using the <code class="language-plaintext highlighter-rouge">beta</code> compiler/Cargo), which will take
several weeks.</p>

<p>However, I did some initial local tests, and it seems that using this flag reduces the size of the <code class="language-plaintext highlighter-rouge">x86_64-unknown-linux-gnu</code>
standard library <code class="language-plaintext highlighter-rouge">.so</code> file from ~13 MiB to ~3 MiB, which seems nice.</p>

<h2 id="stabilization-plan">Stabilization plan</h2>

<p>I think that unless we find some major issues, we should make the <code class="language-plaintext highlighter-rouge">-Zno-embed-metadata</code> behavior
the default in Cargo, to reduce the disk space usage of the <code class="language-plaintext highlighter-rouge">target</code> directory for everyone.
Currently, it seems like it might be considered to be a backwards compatibility break though, as the Cargo
team is unsure if some people weren’t relying on the metadata being present in the <code class="language-plaintext highlighter-rouge">.rlib</code>
files. In general, it’s quite tricky to determine whether something is a breaking change in Cargo or
not, if it hasn’t been previously documented (<a href="https://www.hyrumslaw.com/">Hyrum’s Law</a> is ever-present).</p>

<p>In terms of how to technically perform the migration, I think that we could use the new behavior by default
on the <code class="language-plaintext highlighter-rouge">nightly</code> toolchain for some time<sup id="fnref:lld-nightly"><a href="#fn:lld-nightly" class="footnote" rel="footnote" role="doc-noteref">4</a></sup> to find potential issues in the wild,
and flip the Cargo flag to allow opting out of (instead of opting in) the new behavior
(so it would essentially become <code class="language-plaintext highlighter-rouge">-Zembed-metadata</code>).</p>

<p>If you are interested in the future of this feature, you can observe its status in
the <a href="https://github.com/rust-lang/rust/issues/139165">rustc</a> and <a href="https://github.com/rust-lang/cargo/issues/15495">Cargo</a> tracking issues.</p>

<h2 id="conclusion">Conclusion</h2>

<p>I would be interested in how this flag fares in real-world scenarios, whether it causes any issues,
and how much disk space it can save. If you would like to try it out, use a recent <code class="language-plaintext highlighter-rouge">rustc</code> and Cargo
and build your project using e.g. <code class="language-plaintext highlighter-rouge">cargo +nightly build -Zno-embed-metadata</code>. You can use e.g.
<a href="https://gist.github.com/Kobzol/72d9c6cbade6499206859e09e06760f1">this script</a> as an inspiration for
benchmarking the effect of the flag.</p>

<p>If you try it out, please let me know about your results on <a href="https://www.reddit.com/r/rust/comments/1l1gnoj/reducing_cargo_target_directory_size_with">Reddit</a>!</p>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:disk-vs-compilation">
      <p>Funnily enough, making compilation faster can sometimes increase disk usage; for example, the incremental system of the Rust compiler stores a lot of data on disk, which helps it to make subsequent recompilations faster. <a href="#fnref:disk-vs-compilation" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:jemalloc">
      <p>I compiled it with <code class="language-plaintext highlighter-rouge">--no-default-features</code> to avoid compiling the <em>C</em> <code class="language-plaintext highlighter-rouge">jemalloc</code> dependency, as that takes <a href="https://github.com/tikv/jemallocator/pull/119">a lot of space on disk</a>. <a href="#fnref:jemalloc" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:build-cmd">
      <p>The full command was <code class="language-plaintext highlighter-rouge">cargo +nightly build --no-default-features -Zno-embed-metadata</code>. <a href="#fnref:build-cmd" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:lld-nightly">
      <p>Same as we did with the <a href="https://blog.rust-lang.org/2024/05/17/enabling-rust-lld-on-linux/">lld</a> linker. <a href="#fnref:lld-nightly" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="rust" /><category term="rustc" /><summary type="html"><![CDATA[Disk usage of the target directory is a commonly cited annoyance with Rust (and Cargo) – in the last year’s Annual Survey, it was the third most pressing issue of Rust users, right after slow compilation1 and subpar debugging experience. Given the “build everything from source” compilation model of Rust, and both debuginfo and incremental compilation being enabled by default in the dev Cargo profile, it is unlikely that the target directory will ever become lean and small. However, there are still ways of how we could reduce the target directory size by a non-trivial amount. I will describe a brand-new method of achieving that in this blog post. Funnily enough, making compilation faster can sometimes increase disk usage; for example, the incremental system of the Rust compiler stores a lot of data on disk, which helps it to make subsequent recompilations faster. &#8617;]]></summary></entry></feed>