<?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-05-30T12:36:47+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">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><entry><title type="html">Disable debuginfo to improve Rust compile times</title><link href="https://kobzol.github.io/rust/rustc/2025/05/20/disable-debuginfo-to-improve-rust-compile-times.html" rel="alternate" type="text/html" title="Disable debuginfo to improve Rust compile times" /><published>2025-05-20T13:00:00+00:00</published><updated>2025-05-20T13:00:00+00:00</updated><id>https://kobzol.github.io/rust/rustc/2025/05/20/disable-debuginfo-to-improve-rust-compile-times</id><content type="html" xml:base="https://kobzol.github.io/rust/rustc/2025/05/20/disable-debuginfo-to-improve-rust-compile-times.html"><![CDATA[<blockquote>
  <p>This blog post is essentially a <abbr title="Public service announcement">PSA</abbr> on how you can easily improve <code class="language-plaintext highlighter-rouge">dev</code> incremental rebuild performance.</p>
</blockquote>

<p>At <a href="https://rustweek.org/">RustWeek</a>, we led many discussions on compiler performance, and what we can do to improve it. One of the discussion points in this area was the generation of debug information (debuginfo), which happens by default in the Cargo <code class="language-plaintext highlighter-rouge">dev</code> (unoptimized) profile. Now, I suppose that it’s not surprising that generating debuginfo is not <em>free</em> and costs some compilation time. But I’m not sure if people actually realize <em>just how expensive</em> it currently is, especially for incremental (re)builds.</p>

<p><a href="https://perf.rust-lang.org/compare.html?start=a8e4c68dcb4dc1e48a0db294c5323cab0227fcb9&amp;end=824ee216c0852d0622837402ed8aaf3a74ab4ac4&amp;stat=instructions:u">Here</a> are results of a run of the <a href="https://github.com/rust-lang/rustc-perf">compiler benchmark suite</a> where I forcefully disabled debuginfo generation. In some situations, especially for incremental rebuilds, disabling debuginfo can make your compilation 30-40% faster! And this benchmark was already performed with the fast <code class="language-plaintext highlighter-rouge">lld</code> linker, which is not used by default on the x64 Linux target (<a href="https://github.com/rust-lang/rust/pull/140525">yet</a>); I expect that with the default Linux linker (BFD), the improvements would be actually <em>way</em> higher, because linking debuginfo can be quite slow. You won’t get such a large improvement in all situations, of course, but it can definitely be worth it to consider disabling debuginfo.</p>

<p>There are essentially two ways how we can improve this situation. We could make debuginfo generation faster, as I suspect that it is currently quite suboptimal, particularly in incremental builds, or we can avoid generating it by default. But until either of that happens, you can speed up your (unoptimized) incremental rebuilds <em>today</em> by disabling debuginfo in your <code class="language-plaintext highlighter-rouge">Cargo.toml</code> file:</p>

<div class="language-toml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[profile.dev]</span>
<span class="py">debug</span> <span class="p">=</span> <span class="kc">false</span>
</code></pre></div></div>

<p>Note that this will also remove source code lines from stack traces, making them much less useful. If you want to keep that information, you can use <code class="language-plaintext highlighter-rouge">debug = "line-tables-only"</code> instead, which is a compromise between full and no debuginfo. It still provides a <a href="https://perf.rust-lang.org/compare.html?start=f8e9e7636aabcbc29345d9614432d15b3c0c4ec7&amp;end=280a1abf776b68a28cbb5cd830d3123723354b0f&amp;stat=instructions:u">pretty nice speedup</a> vs the default (full) debuginfo.</p>

<p>I personally think that generating debuginfo by default is a bit wasteful. Based on various polls and survey results that I saw, many Rust programmers simply don’t use a debugger at all. But even if you do use it, I would estimate that you don’t end up actually running a debugger on a large fraction of your builds, even though you do pay the debuginfo generation cost for every build (by default).</p>

<p>I’m planning to work on removing debuginfo (or changing it to <code class="language-plaintext highlighter-rouge">line-table-only</code>) from the default <code class="language-plaintext highlighter-rouge">dev</code> profile in Cargo, but it will take some design work and time (and it’s unclear if it will actually happen). We should probably also make debuginfo generation faster (and smarter for incremental builds) in the compiler.</p>

<blockquote>
  <p>Removing the debuginfo generation is actually the only optimization that I do for the stable compiler in the <a href="https://github.com/Kobzol/cargo-wizard">cargo-wizard</a> tool; other approaches designed to speed up compilation (such as the parallel frontend, the Cranelift backend or using a different linker) are either unstable or cannot be easily configured directly through <code class="language-plaintext highlighter-rouge">Cargo.toml</code>.</p>
</blockquote>

<p>If you have any thoughts on this, let me know on <a href="https://www.reddit.com/r/rust/comments/1kr7ri4/psa_you_can_disable_debuginfo_to_improve_rust/">Reddit</a>!</p>]]></content><author><name></name></author><category term="rust" /><category term="rustc" /><summary type="html"><![CDATA[This blog post is essentially a PSA on how you can easily improve dev incremental rebuild performance.]]></summary></entry><entry><title type="html">Evolution of Rust compiler errors</title><link href="https://kobzol.github.io/rust/rustc/2025/05/16/evolution-of-rustc-errors.html" rel="alternate" type="text/html" title="Evolution of Rust compiler errors" /><published>2025-05-16T11:00:00+00:00</published><updated>2025-05-16T11:00:00+00:00</updated><id>https://kobzol.github.io/rust/rustc/2025/05/16/evolution-of-rustc-errors</id><content type="html" xml:base="https://kobzol.github.io/rust/rustc/2025/05/16/evolution-of-rustc-errors.html"><![CDATA[<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/default.min.css" />

<link rel="stylesheet" href="/assets/posts/rustc-error-evolution/style.css" />

<script type="text/javascript" src="/assets/posts/rustc-error-evolution/ansi_up.js" defer=""></script>

<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@latest/build/highlight.min.js" defer=""></script>

<script type="text/javascript" src="/assets/posts/rustc-error-evolution/script.js" defer=""></script>

<p>I recently attended <a href="https://rustweek.org/">RustWeek</a> (which was totally awesome) and the talks by Alex Crichton (on the history of Rust) and Pietro Albini (on the importance of error messages) inspired me to do a little archaeology into the way Rust compiler messages have evolved over time.</p>

<p>I wrote a script that downloaded all stable Rust releases all the way back to 1.0<sup id="fnref:rustup"><a href="#fn:rustup" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>, executed each stable version of the compiler on a set of small programs containing an error and gathered the compiler standard (error) output.</p>

<p>The widget below visualizes how the error messages evolved over time. You can use the select box to examine different Rust programs to see their error:</p>

<p><select id="programs">
    <option value="moved_var">Moved variable</option>
    <option value="wrong_field">Wrong field</option>
    <option value="missing_impl">Missing implementation</option>
    <option value="swapped_args">Swapped arguments</option>
    <option value="wrong_type_arg">Wrong argument type</option>
    <option value="borrowck">Borrow check error</option>
    <option value="missing_where_bound">Missing where bound</option>
    <option value="unused_var">Unused variable</option>
</select></p>
<div id="error-widget"></div>

<p>There are a couple of interesting things to note:</p>

<ul>
  <li>First and foremost, the error messages are simply <em>great</em>. If you have used Rust previously, this probably isn’t too surprising. Even Rust <code class="language-plaintext highlighter-rouge">1.0.0</code> contained pretty solid error reporting, and it got much better over time.</li>
  <li>Rust <code class="language-plaintext highlighter-rouge">1.2.0</code> introduced numerical <a href="https://doc.rust-lang.org/error_codes/error-index.html">error codes</a>. Edit: they were actually added in <code class="language-plaintext highlighter-rouge">1.0.0</code>, just that this specific error did not have a code until <code class="language-plaintext highlighter-rouge">1.2.0</code>, as was pointed out to me by <a href="https://www.reddit.com/r/rust/comments/1knzzqa/comment/msnlx2r">Steve Klabnik</a>.</li>
  <li>Rust <code class="language-plaintext highlighter-rouge">1.26.0</code> introduced colorful error messages. It sounds like a small change, but you can see what an improvement it makes! It also added the <code class="language-plaintext highlighter-rouge">rustc --explain &lt;error-code&gt;</code> hint.</li>
  <li>The error messages sometimes went a bit back and forth in different Rust versions, which is a bit funny. For example, the <code class="language-plaintext highlighter-rouge">error: aborting due to 2 previous errors</code> has switched to <code class="language-plaintext highlighter-rouge">...previous error(s)</code> in <code class="language-plaintext highlighter-rouge">1.19.0</code> and then back to <code class="language-plaintext highlighter-rouge">...2 previous errors</code> in <code class="language-plaintext highlighter-rouge">1.20.0</code>, which seems like an unintended change in <code class="language-plaintext highlighter-rouge">1.19.0</code>. Sometimes the difference is only in a single space, which is not even visible in the visualization above.</li>
  <li>The error spans are also continuously being improved between rustc versions. My favourite example is the <code class="language-plaintext highlighter-rouge">Wrong field</code> program change in <code class="language-plaintext highlighter-rouge">1.87.0</code>.</li>
</ul>

<p>But I think that ultimately, the most interesting thing about this is the evolution process of these messages itself, which demonstrates that a lot of effort has to be put into the messages to make them <em>really good</em>. To someone, it might seem like these messages are somehow automatically derived from the compilation process, and we get them “for free”, but that couldn’t be further from the truth. It is the result of a continuous design, implementation, review and testing effort that has been performed by hundreds of individual contributors over the span of more than ten years. Thank you to everyone who has worked on the Rust compiler and contributed to these awesome error messages!</p>

<p>If you’d like to test this out on more programs than the ones I have shown here,
you can check out my script <a href="https://github.com/Kobzol/rustc-error-evolution">here</a><sup id="fnref:interactive"><a href="#fn:interactive" class="footnote" rel="footnote" role="doc-noteref">2</a></sup>.</p>

<p>If you have your own favourite example of a Rust compiler error message, share it with others on <a href="https://www.reddit.com/r/rust/comments/1knzzqa/evolution_of_rust_compiler_errors/">Reddit</a>!</p>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:rustup">
      <p>This is actually possible to do just with <code class="language-plaintext highlighter-rouge">rustup</code> (at least on x64 Linux), even though some of the oldest releases actually predate rustup. How cool is that? <a href="#fnref:rustup" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:interactive">
      <p>For a moment I thought about making the widget interactive, so that you could write any Rust code and see the evolution of the compiler errors for it directly in the browser, but that would essentially mean that I would have to reimplement the <a href="https://play.rust-lang.org/">Rust Playground</a> for almost a hundred different compiler versions, which seemed… too much for a blog post :) <a href="#fnref:interactive" 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[]]></summary></entry><entry><title type="html">git stash driven refactoring</title><link href="https://kobzol.github.io/programming/2025/05/06/git-stash-driven-refactoring.html" rel="alternate" type="text/html" title="git stash driven refactoring" /><published>2025-05-06T11:00:00+00:00</published><updated>2025-05-06T11:00:00+00:00</updated><id>https://kobzol.github.io/programming/2025/05/06/git-stash-driven-refactoring</id><content type="html" xml:base="https://kobzol.github.io/programming/2025/05/06/git-stash-driven-refactoring.html"><![CDATA[<p>Does the following sound familiar to you?</p>

<ol>
  <li>You want to implement a new feature. You start going through the code to figure out where the places that you need to modify are.</li>
  <li>You notice that some of the old code was suboptimal. That code can either be making it harder to implement the new feature, or it can be completely unrelated code that you just happened to read in detail after a long time.</li>
  <li>You decide to first fix/refactor the old code, to make the new feature easier to implement.</li>
  <li><code class="language-plaintext highlighter-rouge">GOTO 1.</code>, with the fix/refactor becoming the “new feature” that you’re now implementing.</li>
</ol>

<p>This happens to me all the time. After a few iterations of this cycle, where I repeatedly keep starting new and new refactorings, I usually end up with a git workspace that contains a ton of unrelated changes. And since I usually code in Rust these days, it’s also very likely that my project does not even compile, because I started (but did not finish) a lot of changes. At this point I sometimes used to just give up, <code class="language-plaintext highlighter-rouge">git checkout .</code> the whole thing and start from scratch, to avoid having to dig myself out of the mess and cleanly separate the unrelated changes into individual commits.</p>

<p>If you also sometimes fall into these endless refactoring cycles, I found a pretty simple workflow that makes it easier to untangle them (at least for me). Everytime you notice something suboptimal in the codebase that is not directly a part of what you’re currently implementing and that you want to “just slightly refactor”, use <code class="language-plaintext highlighter-rouge">git stash</code> to stash all your current changes away, and start working on the refactoring that you just thought of. If you encounter another thing that should be refactored or fixed during that, apply the workflow recursively - <code class="language-plaintext highlighter-rouge">git stash</code> your changes away and start working on the latest thing that you have in mind. After you finally get to a change that you can finish from start to end, commit it, and then restore the previous state with <code class="language-plaintext highlighter-rouge">git stash pop</code> and continue onwards. With this approach, the changes are effectively applied “inside-out”.</p>

<p>It’s nothing ground-breaking, of course, but I feel that this workflow really helps me to focus on a single thing at a time. I don’t have to consider the uncommitted changes I made to something unrelated previously. I know that I can always purge all workspace changes without worrying that I will remove work on the previous feature that spawned this refactoring. And most importantly, I do not have to think about the previous in-progress work. This is similar to how using <code class="language-plaintext highlighter-rouge">assert</code>s in code help me to avoid thinking about certain possibilities and code paths, as I know that they cannot happen<sup id="fnref:assert"><a href="#fn:assert" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>.</p>

<p>Of course, if the individual refactorings are too unrelated, you might want to merge them in separate PRs, so sometimes it makes sense to throw in a bunch of cherry-picks or interactive rebases to move the work to a different branch and merge it separately. But the main thing is to avoid working on multiple things at once, to avoid getting distracted, which is what <code class="language-plaintext highlighter-rouge">git stash</code> is really useful for.</p>

<p>By the way, here are two small tips related to <code class="language-plaintext highlighter-rouge">git stash</code>:</p>
<ul>
  <li>You can stash only seleted paths using <code class="language-plaintext highlighter-rouge">git stash -- &lt;path&gt;</code> or <code class="language-plaintext highlighter-rouge">git stash push &lt;path&gt;</code></li>
  <li>You can name stashes using <code class="language-plaintext highlighter-rouge">-m</code>, to make it easier to recall what you stashed away a week ago :)</li>
</ul>

<p>If you have any comments, let me know on <a href="https://www.reddit.com/r/programming/comments/1kg37vm/git_stash_driven_refactoring">Reddit</a>.</p>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:assert">
      <p>Without triggering an assert anyway. <a href="#fnref:assert" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="programming" /><summary type="html"><![CDATA[Does the following sound familiar to you?]]></summary></entry></feed>