Congratulations!

[Valid Atom 1.0] This is a valid Atom 1.0 feed.

Recommendations

This feed is valid, but interoperability with the widest range of feed readers could be improved by implementing the following recommendations.

Source: http://blog.golang.org/feed.atom

  1. <feed xmlns="http://www.w3.org/2005/Atom"><title>The Go Blog</title><id>tag:blog.golang.org,2013:blog.golang.org</id><link rel="self" href="https://go.dev/blog/feed.atom"></link><updated>2025-04-02T00:00:00+00:00</updated><entry><title>More predictable benchmarking with testing.B.Loop</title><id>tag:blog.golang.org,2013:blog.golang.org/testing-b-loop</id><link rel="alternate" href="https://go.dev/blog/testing-b-loop"></link><published>2025-04-02T00:00:00+00:00</published><updated>2025-04-02T00:00:00+00:00</updated><author><name>Junyang Shao</name></author><summary type="html">Better benchmark looping in Go 1.24.</summary><content type="html">&#xA;&lt;div id=&#34;blog&#34;&gt;&lt;div id=&#34;content&#34;&gt;&#xA;  &lt;div id=&#34;content&#34;&gt;&#xA;&#xA;    &lt;div class=&#34;Article&#34; data-slug=&#34;/blog/testing-b-loop&#34;&gt;&#xA;    &#xA;    &lt;h1 class=&#34;small&#34;&gt;&lt;a href=&#34;/blog/&#34;&gt;The Go Blog&lt;/a&gt;&lt;/h1&gt;&#xA;    &#xA;&#xA;    &lt;h1&gt;More predictable benchmarking with testing.B.Loop&lt;/h1&gt;&#xA;      &#xA;      &lt;p class=&#34;author&#34;&gt;&#xA;      Junyang Shao&lt;br&gt;&#xA;      2 April 2025&#xA;      &lt;/p&gt;&#xA;      &#xA;      &lt;p&gt;Go developers who have written benchmarks using the&#xA;&lt;a href=&#34;https://pkg.go.dev/testing&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;&lt;code&gt;testing&lt;/code&gt;&lt;/a&gt; package might have encountered some of&#xA;its various pitfalls. Go 1.24 introduces a new way to write benchmarks that&amp;rsquo;s just&#xA;as easy to use, but at the same time far more robust:&#xA;&lt;a href=&#34;https://pkg.go.dev/testing#B.Loop&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;&lt;code&gt;testing.B.Loop&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Traditionally, Go benchmarks are written using a loop from 0 to &lt;code&gt;b.N&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;func Benchmark(b *testing.B) {&#xA;  for range b.N {&#xA;    ... code to measure ...&#xA;  }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Using &lt;code&gt;b.Loop&lt;/code&gt; instead is a trivial change:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;func Benchmark(b *testing.B) {&#xA;  for b.Loop() {&#xA;    ... code to measure ...&#xA;  }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;code&gt;testing.B.Loop&lt;/code&gt; has many benefits:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;It prevents unwanted compiler optimizations within the benchmark loop.&lt;/li&gt;&#xA;&lt;li&gt;It automatically excludes setup and cleanup code from benchmark timing.&lt;/li&gt;&#xA;&lt;li&gt;Code can&amp;rsquo;t accidentally depend on the total number of iterations or the current&#xA;iteration.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;These were all easy mistakes to make with &lt;code&gt;b.N&lt;/code&gt;-style benchmarks that would&#xA;silently result in bogus benchmark results. As an added bonus, &lt;code&gt;b.Loop&lt;/code&gt;-style&#xA;benchmarks even complete in less time!&lt;/p&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s explore the advantages of &lt;code&gt;testing.B.Loop&lt;/code&gt; and how to effectively utilize it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;old-benchmark-loop-problems&#34;&gt;Old benchmark loop problems&lt;/h2&gt;&#xA;&lt;p&gt;Before Go 1.24, while the basic structure of a benchmark was simple, more sophisticated&#xA;benchmarks required more care:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;func Benchmark(b *testing.B) {&#xA;  ... setup ...&#xA;  b.ResetTimer() // if setup may be expensive&#xA;  for range b.N {&#xA;    ... code to measure ...&#xA;    ... use sinks or accumulation to prevent dead-code elimination ...&#xA;  }&#xA;  b.StopTimer() // if cleanup or reporting may be expensive&#xA;  ... cleanup ...&#xA;  ... report ...&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;If setup or cleanup are non-trivial, the developer needs to surround the benchmark loop&#xA;with &lt;code&gt;ResetTimer&lt;/code&gt; and/or &lt;code&gt;StopTimer&lt;/code&gt; calls. These are easy to forget, and even if the&#xA;developer remembers they may be necessary, it can be difficult to judge whether setup or&#xA;cleanup are &amp;ldquo;expensive enough&amp;rdquo; to require them.&lt;/p&gt;&#xA;&lt;p&gt;Without these, the &lt;code&gt;testing&lt;/code&gt; package can only time the entire benchmark function. If a&#xA;benchmark function omits them, the setup and cleanup code will be included in the overall&#xA;time measurement, silently skewing the final benchmark result.&lt;/p&gt;&#xA;&lt;p&gt;There is another, more subtle pitfall that requires deeper understanding:&#xA;(&lt;a href=&#34;https://eli.thegreenplace.net/2023/common-pitfalls-in-go-benchmarking/&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;Example source&lt;/a&gt;)&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;func isCond(b byte) bool {&#xA;  if b%3 == 1 &amp;amp;&amp;amp; b%7 == 2 &amp;amp;&amp;amp; b%17 == 11 &amp;amp;&amp;amp; b%31 == 9 {&#xA;    return true&#xA;  }&#xA;  return false&#xA;}&#xA;&#xA;func BenchmarkIsCondWrong(b *testing.B) {&#xA;  for range b.N {&#xA;    isCond(201)&#xA;  }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;In this example, the user might observe &lt;code&gt;isCond&lt;/code&gt; executing in sub-nanosecond&#xA;time. CPUs are fast, but not that fast! This seemingly anomalous result stems&#xA;from the fact that &lt;code&gt;isCond&lt;/code&gt; is inlined, and since its result is never used, the&#xA;compiler eliminates it as dead code. As a result, this benchmark doesn&amp;rsquo;t measure &lt;code&gt;isCond&lt;/code&gt;&#xA;at all; it measures how long it takes to do nothing. In this case, the sub-nanosecond&#xA;result is a clear red flag, but in more complex benchmarks, partial dead-code elimination&#xA;can lead to results that look reasonable but still aren&amp;rsquo;t measuring what was intended.&lt;/p&gt;&#xA;&lt;h2 id=&#34;how-testingbloop-helps&#34;&gt;How &lt;code&gt;testing.B.Loop&lt;/code&gt; helps&lt;/h2&gt;&#xA;&lt;p&gt;Unlike a &lt;code&gt;b.N&lt;/code&gt;-style benchmark, &lt;code&gt;testing.B.Loop&lt;/code&gt; is able to track when it is first called&#xA;in a benchmark when the final iteration ends. The &lt;code&gt;b.ResetTimer&lt;/code&gt; at the loop&amp;rsquo;s start&#xA;and &lt;code&gt;b.StopTimer&lt;/code&gt; at its end are integrated into &lt;code&gt;testing.B.Loop&lt;/code&gt;, eliminating the need&#xA;to manually manage the benchmark timer for setup and cleanup code.&lt;/p&gt;&#xA;&lt;p&gt;Furthermore, the Go compiler now detects loops where the condition is just a call to&#xA;&lt;code&gt;testing.B.Loop&lt;/code&gt; and prevents dead code elimination within the loop. In Go 1.24, this is&#xA;implemented by disallowing inlining into the body of such a loop, but we plan to&#xA;&lt;a href=&#34;/issue/73137&#34;&gt;improve&lt;/a&gt; this in the future.&lt;/p&gt;&#xA;&lt;p&gt;Another nice feature of &lt;code&gt;testing.B.Loop&lt;/code&gt; is its one-shot ramp-up approach. With a &lt;code&gt;b.N&lt;/code&gt;-style&#xA;benchmark, the testing package must call the benchmark function several times with different&#xA;values of &lt;code&gt;b.N&lt;/code&gt;, ramping up until the measured time reached a threshold. In contrast, &lt;code&gt;b.Loop&lt;/code&gt;&#xA;can simply run the benchmark loop until it reaches the time threshold, and only needs to call&#xA;the benchmark function once. Internally, &lt;code&gt;b.Loop&lt;/code&gt; still uses a ramp-up process to amortize&#xA;measurement overhead, but this is hidden from the caller and can be more efficient.&lt;/p&gt;&#xA;&lt;p&gt;Certain constraints of the &lt;code&gt;b.N&lt;/code&gt;-style loop still apply to the &lt;code&gt;b.Loop&lt;/code&gt;-style&#xA;loop. It remains the user&amp;rsquo;s responsibility to manage the timer within the benchmark loop,&#xA;when necessary:&#xA;(&lt;a href=&#34;https://eli.thegreenplace.net/2023/common-pitfalls-in-go-benchmarking/&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;Example source&lt;/a&gt;)&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;func BenchmarkSortInts(b *testing.B) {&#xA;  ints := make([]int, N)&#xA;  for b.Loop() {&#xA;    b.StopTimer()&#xA;    fillRandomInts(ints)&#xA;    b.StartTimer()&#xA;    slices.Sort(ints)&#xA;  }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;In this example, to benchmark the in-place sorting performance of &lt;code&gt;slices.Sort&lt;/code&gt;,a&#xA;randomly initialized array is required for each iteration. The user must still&#xA;manually manage the timer in such cases.&lt;/p&gt;&#xA;&lt;p&gt;Furthermore, there still needs to be exactly one such loop in the benchmark function body&#xA;(a &lt;code&gt;b.N&lt;/code&gt;-style loop cannot coexist with a &lt;code&gt;b.Loop&lt;/code&gt;-style loop), and every iteration of the&#xA;loop should do the same thing.&lt;/p&gt;&#xA;&lt;h2 id=&#34;when-to-use&#34;&gt;When to use&lt;/h2&gt;&#xA;&lt;p&gt;The &lt;code&gt;testing.B.Loop&lt;/code&gt; method is now the preferred way to write benchmarks:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;func Benchmark(b *testing.B) {&#xA;  ... setup ...&#xA;  for b.Loop() {&#xA;    // optional timer control for in-loop setup/cleanup&#xA;    ... code to measure ...&#xA;  }&#xA;  ... cleanup ...&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;code&gt;testing.B.Loop&lt;/code&gt; offers faster, more accurate, and&#xA;more intuitive benchmarking.&lt;/p&gt;&#xA;&lt;h2 id=&#34;acknowledgements&#34;&gt;Acknowledgements&lt;/h2&gt;&#xA;&lt;p&gt;A huge thank you to everyone in the community who provided feedback on the proposal&#xA;issue and reported bugs as this feature was released! I&amp;rsquo;m also grateful to Eli&#xA;Bendersky for his helpful blog summaries. And finally a big thank you to Austin Clements,&#xA;Cherry Mui and Michael Pratt for their review, thoughtful work on the design options and&#xA;documentation improvements. Thank you all for your contributions!&lt;/p&gt;&#xA;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;Article prevnext&#34;&gt;&#xA;    &#xA;    &#xA;      &#xA;        &lt;p&gt;&#xA;        &#xA;        &#xA;          &#xA;            &lt;b&gt;Previous article: &lt;/b&gt;&lt;a href=&#34;/blog/coretypes&#34;&gt;Goodbye core types - Hello Go as we know and love it!&lt;/a&gt;&lt;br&gt;&#xA;          &#xA;        &#xA;        &lt;b&gt;&lt;a href=&#34;/blog/all&#34;&gt;Blog Index&lt;/a&gt;&lt;/b&gt;&#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;    &lt;/div&gt;&#xA;    &#xA;&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;script src=&#34;/js/play.js&#34;&gt;&lt;/script&gt;&#xA;&#xA;</content></entry><entry><title>Goodbye core types - Hello Go as we know and love it!</title><id>tag:blog.golang.org,2013:blog.golang.org/coretypes</id><link rel="alternate" href="https://go.dev/blog/coretypes"></link><published>2025-03-26T00:00:00+00:00</published><updated>2025-03-26T00:00:00+00:00</updated><author><name>Robert Griesemer</name></author><summary type="html">Go 1.25 simplifies the language spec by removing the notion of core types</summary><content type="html">&#xA;&lt;div id=&#34;blog&#34;&gt;&lt;div id=&#34;content&#34;&gt;&#xA;  &lt;div id=&#34;content&#34;&gt;&#xA;&#xA;    &lt;div class=&#34;Article&#34; data-slug=&#34;/blog/coretypes&#34;&gt;&#xA;    &#xA;    &lt;h1 class=&#34;small&#34;&gt;&lt;a href=&#34;/blog/&#34;&gt;The Go Blog&lt;/a&gt;&lt;/h1&gt;&#xA;    &#xA;&#xA;    &lt;h1&gt;Goodbye core types - Hello Go as we know and love it!&lt;/h1&gt;&#xA;      &#xA;      &lt;p class=&#34;author&#34;&gt;&#xA;      Robert Griesemer&lt;br&gt;&#xA;      26 March 2025&#xA;      &lt;/p&gt;&#xA;      &#xA;      &lt;p&gt;The Go 1.18 release introduced generics and with that a number of new features, including type parameters, type constraints, and new concepts such as type sets.&#xA;It also introduced the notion of a &lt;em&gt;core type&lt;/em&gt;.&#xA;While the former provide concrete new functionality, a core type is an abstract construct that was introduced&#xA;for expediency and to simplify dealing with generic operands (operands whose types are type parameters).&#xA;In the Go compiler, code that in the past relied on the &lt;a href=&#34;/ref/spec/#Underlying_types&#34;&gt;underlying type&lt;/a&gt; of an operand,&#xA;now instead had to call a function computing the operand&amp;rsquo;s core type.&#xA;In the language spec, in many places we just needed to replace &amp;ldquo;underlying type&amp;rdquo; with &amp;ldquo;core type&amp;rdquo;.&#xA;What&amp;rsquo;s not to like?&lt;/p&gt;&#xA;&lt;p&gt;Quite a few things, as it turns out!&#xA;To understand how we got here, it&amp;rsquo;s useful to briefly revisit how type parameters and type constraints work.&lt;/p&gt;&#xA;&lt;h2 id=&#34;type-parameters-and-type-constraints&#34;&gt;Type parameters and type constraints&lt;/h2&gt;&#xA;&lt;p&gt;A type parameter is a placeholder for a future type argument;&#xA;it acts like a &lt;em&gt;type variable&lt;/em&gt; whose value is known at compile time,&#xA;similar to how a named constant stands for a number, string, or bool whose value is known at compile time.&#xA;Like ordinary variables, type parameters have a type.&#xA;That type is described by their &lt;em&gt;type constraint&lt;/em&gt; which determines&#xA;what operations are permitted on operands whose type is the respective type parameter.&lt;/p&gt;&#xA;&lt;p&gt;Any concrete type that instantiates a type parameter must satisfy the type parameter&amp;rsquo;s constraint.&#xA;This ensures that an operand whose type is a type parameter possesses all of the respective type constraint&amp;rsquo;s properties,&#xA;no matter what concrete type is used to instantiate the type parameter.&lt;/p&gt;&#xA;&lt;p&gt;In Go, type constraints are described through a mixture of method and type requirements which together&#xA;define a &lt;em&gt;type set&lt;/em&gt;: this is the set of all the types that satisfy all the requirements. Go uses a&#xA;generalized form of interfaces for this purpose. An interface enumerates a set of methods and types,&#xA;and the type set described by such an interface consists of all the types that implement those methods&#xA;and that are included in the enumerated types.&lt;/p&gt;&#xA;&lt;p&gt;For instance, the type set described by the interface&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-Go&#34;&gt;type Constraint interface {&#xA;    ~[]byte | ~string&#xA;    Hash() uint64&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;consists of all the types whose representation is &lt;code&gt;[]byte&lt;/code&gt; or &lt;code&gt;string&lt;/code&gt; and whose method set includes the &lt;code&gt;Hash&lt;/code&gt; method.&lt;/p&gt;&#xA;&lt;p&gt;With this we can now write down the rules that govern operations on generic operands.&#xA;For instance, the &lt;a href=&#34;/ref/spec#Index_expressions&#34;&gt;rules for index expressions&lt;/a&gt; state that (among other things)&#xA;for an operand &lt;code&gt;a&lt;/code&gt; of type parameter type &lt;code&gt;P&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;The index expression &lt;code&gt;a[x]&lt;/code&gt; must be valid for values of all types in &lt;code&gt;P&lt;/code&gt;&amp;rsquo;s type set.&#xA;The element types of all types in &lt;code&gt;P&lt;/code&gt;&amp;rsquo;s type set must be identical.&#xA;(In this context, the element type of a string type is &lt;code&gt;byte&lt;/code&gt;.)&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;These rules make it possible to index the generic variable &lt;code&gt;s&lt;/code&gt; below (&lt;a href=&#34;/play/p/M1LYKm3x3IB&#34;&gt;playground&lt;/a&gt;):&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-Go&#34;&gt;func at[bytestring Constraint](s bytestring, i int) byte {&#xA;    return s[i]&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The indexing operation &lt;code&gt;s[i]&lt;/code&gt; is permitted because the type of &lt;code&gt;s&lt;/code&gt; is &lt;code&gt;bytestring&lt;/code&gt;, and the type constraint (type set) of&#xA;&lt;code&gt;bytestring&lt;/code&gt; contains &lt;code&gt;[]byte&lt;/code&gt; and &lt;code&gt;string&lt;/code&gt; types for which indexing with &lt;code&gt;i&lt;/code&gt; is valid.&lt;/p&gt;&#xA;&lt;h2 id=&#34;core-types&#34;&gt;Core types&lt;/h2&gt;&#xA;&lt;p&gt;This type set-based approach is very flexible and in line with the intentions of the&#xA;&lt;a href=&#34;https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;original generics proposal&lt;/a&gt;:&#xA;an operation involving operands of generic type should be valid if it is valid for any type permitted by the respective&#xA;type constraint.&#xA;To simplify matters with respect to the implementation, knowing that we would be able to relax rules later,&#xA;this approach was &lt;em&gt;not&lt;/em&gt; chosen universally.&#xA;Instead, for instance, for &lt;a href=&#34;/ref/spec#Send_statements&#34;&gt;Send statements&lt;/a&gt;, the spec states that&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;The channel expression&amp;rsquo;s &lt;em&gt;core type&lt;/em&gt; must be a channel, the channel direction must permit send operations,&#xA;and the type of the value to be sent must be assignable to the channel&amp;rsquo;s element type.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;These rules are based on the notion of a core type which is defined roughly as follows:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;If a type is not a type parameter, its core type is just its &lt;a href=&#34;/ref/spec#Underlying_types&#34;&gt;underlying type&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;li&gt;If the type is a type parameter, the core type is the single underlying type of all the types in the type parameter&amp;rsquo;s type set.&#xA;If the type set has &lt;em&gt;different&lt;/em&gt; underlying types, the core type doesn&amp;rsquo;t exist.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;For instance, &lt;code&gt;interface{ ~[]int }&lt;/code&gt; has a core type (&lt;code&gt;[]int&lt;/code&gt;), but the &lt;code&gt;Constraint&lt;/code&gt; interface above does not have a core type.&#xA;To make things more complicated, when it comes to channel operations and certain built-in calls (&lt;code&gt;append&lt;/code&gt;, &lt;code&gt;copy&lt;/code&gt;) the above definition&#xA;of core types is too restrictive.&#xA;The actual rules have adjustments that allow for differing channel directions and type sets containing both &lt;code&gt;[]byte&lt;/code&gt; and &lt;code&gt;string&lt;/code&gt; types.&lt;/p&gt;&#xA;&lt;p&gt;There are various problems with this approach:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Because the definition of core type must lead to sound type rules for different language features,&#xA;it is overly restrictive for specific operations.&#xA;For instance, the Go 1.24 rules for &lt;a href=&#34;/ref/spec#Slice_expressions&#34;&gt;slice expressions&lt;/a&gt; do rely on core types,&#xA;and as a consequence slicing an operand of type &lt;code&gt;S&lt;/code&gt; constrained by &lt;code&gt;Constraint&lt;/code&gt; is not permitted, even though&#xA;it could be valid.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;When trying to understand a specific language feature, one may have to learn the intricacies of&#xA;core types even when considering non-generic code.&#xA;Again, for slice expressions, the language spec talks about the core type of the sliced operand,&#xA;rather than just stating that the operand must be an array, slice, or string.&#xA;The latter is more direct, simpler, and clearer, and doesn&amp;rsquo;t require knowing another concept that may be&#xA;irrelevant in the concrete case.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Because the notion of core types exists, the rules for index expressions, and &lt;code&gt;len&lt;/code&gt; and &lt;code&gt;cap&lt;/code&gt; (and others),&#xA;which all eschew core types, appear as exceptions in the language rather than the norm.&#xA;In turn, core types cause proposals such as &lt;a href=&#34;/issue/48522&#34;&gt;issue #48522&lt;/a&gt; which would permit a selector&#xA;&lt;code&gt;x.f&lt;/code&gt; to access a field &lt;code&gt;f&lt;/code&gt; shared by all elements of &lt;code&gt;x&lt;/code&gt;&amp;rsquo;s type set, to appear to add more exceptions to the&#xA;language.&#xA;Without core types, that feature becomes a natural and useful consequence of the ordinary rules for non-generic&#xA;field access.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;go-125&#34;&gt;Go 1.25&lt;/h2&gt;&#xA;&lt;p&gt;For the upcoming Go 1.25 release (August 2025) we decided to remove the notion of core types from the&#xA;language spec in favor of explicit (and equivalent!) prose where needed.&#xA;This has multiple benefits:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The Go spec presents fewer concepts, making it easier to learn the language.&lt;/li&gt;&#xA;&lt;li&gt;The behavior of non-generic code can be understood without reference to generics concepts.&lt;/li&gt;&#xA;&lt;li&gt;The individualized approach (specific rules for specific operations) opens the door for more flexible rules.&#xA;We already mentioned &lt;a href=&#34;/issue/48522&#34;&gt;issue #48522&lt;/a&gt;, but there are also ideas for more powerful&#xA;slice operations, and &lt;a href=&#34;/issue/69153&#34;&gt;improved type inference&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The respective &lt;a href=&#34;/issue/70128&#34;&gt;proposal issue #70128&lt;/a&gt; was recently approved and the relevant changes&#xA;are already implemented.&#xA;Concretely this means that a lot of prose in the language spec was reverted to its original,&#xA;pre-generics form, and new paragraphs were added where needed to explain the rules as they&#xA;pertain to generic operands. Importantly, no behavior was changed.&#xA;The entire section on core types was removed.&#xA;The compiler&amp;rsquo;s error messages were updated to not mention &amp;ldquo;core type&amp;rdquo; anymore, and in many&#xA;cases error messages are now more specific by pointing out exactly which type in a type set&#xA;is causing a problem.&lt;/p&gt;&#xA;&lt;p&gt;Here is a sample of the changes made. For the built-in function &lt;code&gt;close&lt;/code&gt;,&#xA;starting with Go 1.18 the spec began as follows:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;For an argument &lt;code&gt;ch&lt;/code&gt; with core type that is a channel,&#xA;the built-in function &lt;code&gt;close&lt;/code&gt; records that no more values will be sent on the channel.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;A reader who simply wanted to know how &lt;code&gt;close&lt;/code&gt; works, had to first learn about core types.&#xA;Starting with Go 1.25, this section will again begin the same way it began before Go 1.18:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;For a channel &lt;code&gt;ch&lt;/code&gt;, the built-in function &lt;code&gt;close(ch)&lt;/code&gt;&#xA;records that no more values will be sent on the channel.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;This is shorter and easier to understand.&#xA;Only when the reader is dealing with a generic operand will they have to contemplate&#xA;the newly added paragraph:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;If the type of the argument to &lt;code&gt;close&lt;/code&gt; is a type parameter&#xA;all types in its type set must be channels with the same element type.&#xA;It is an error if any of those channels is a receive-only channel.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;We made similar changes to each place that mentioned core types.&#xA;In summary, although this spec update does not affect any current Go program, it opens the&#xA;door to future language improvements while making the language as it is today easier to&#xA;learn and its spec simpler.&lt;/p&gt;&#xA;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;Article prevnext&#34;&gt;&#xA;    &#xA;    &#xA;      &#xA;    &#xA;      &#xA;        &lt;p&gt;&#xA;        &#xA;          &#xA;            &lt;b&gt;Next article: &lt;/b&gt;&lt;a href=&#34;/blog/testing-b-loop&#34;&gt;More predictable benchmarking with testing.B.Loop&lt;/a&gt;&lt;br&gt;&#xA;          &#xA;        &#xA;        &#xA;          &#xA;            &lt;b&gt;Previous article: &lt;/b&gt;&lt;a href=&#34;/blog/osroot&#34;&gt;Traversal-resistant file APIs&lt;/a&gt;&lt;br&gt;&#xA;          &#xA;        &#xA;        &lt;b&gt;&lt;a href=&#34;/blog/all&#34;&gt;Blog Index&lt;/a&gt;&lt;/b&gt;&#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;    &lt;/div&gt;&#xA;    &#xA;&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;script src=&#34;/js/play.js&#34;&gt;&lt;/script&gt;&#xA;&#xA;</content></entry><entry><title>Traversal-resistant file APIs</title><id>tag:blog.golang.org,2013:blog.golang.org/osroot</id><link rel="alternate" href="https://go.dev/blog/osroot"></link><published>2025-03-12T00:00:00+00:00</published><updated>2025-03-12T00:00:00+00:00</updated><author><name>Damien Neil</name></author><summary type="html">New file access APIs in Go 1.24.</summary><content type="html">&#xA;&lt;div id=&#34;blog&#34;&gt;&lt;div id=&#34;content&#34;&gt;&#xA;  &lt;div id=&#34;content&#34;&gt;&#xA;&#xA;    &lt;div class=&#34;Article&#34; data-slug=&#34;/blog/osroot&#34;&gt;&#xA;    &#xA;    &lt;h1 class=&#34;small&#34;&gt;&lt;a href=&#34;/blog/&#34;&gt;The Go Blog&lt;/a&gt;&lt;/h1&gt;&#xA;    &#xA;&#xA;    &lt;h1&gt;Traversal-resistant file APIs&lt;/h1&gt;&#xA;      &#xA;      &lt;p class=&#34;author&#34;&gt;&#xA;      Damien Neil&lt;br&gt;&#xA;      12 March 2025&#xA;      &lt;/p&gt;&#xA;      &#xA;      &lt;p&gt;A &lt;em&gt;path traversal vulnerability&lt;/em&gt; arises when an attacker can trick a program&#xA;into opening a file other than the one it intended.&#xA;This post explains this class of vulnerability,&#xA;some existing defenses against it, and describes how the new&#xA;&lt;a href=&#34;/pkg/os#Root&#34;&gt;&lt;code&gt;os.Root&lt;/code&gt;&lt;/a&gt; API added in Go 1.24 provides&#xA;a simple and robust defense against unintentional path traversal.&lt;/p&gt;&#xA;&lt;h2 id=&#34;path-traversal-attacks&#34;&gt;Path traversal attacks&lt;/h2&gt;&#xA;&lt;p&gt;&amp;ldquo;Path traversal&amp;rdquo; covers a number of related attacks following a common pattern:&#xA;A program attempts to open a file in some known location, but an attacker causes&#xA;it to open a file in a different location.&lt;/p&gt;&#xA;&lt;p&gt;If the attacker controls part of the filename, they may be able to use relative&#xA;directory components (&amp;quot;..&amp;quot;) to escape the intended location:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;f, err := os.Open(filepath.Join(trustedLocation, &amp;quot;../../../../etc/passwd&amp;quot;))&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;On Windows systems, some names have special meaning:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;// f will print to the console.&#xA;f, err := os.Create(filepath.Join(trustedLocation, &amp;quot;CONOUT$&amp;quot;))&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;If the attacker controls part of the local filesystem, they may be able to use&#xA;symbolic links to cause a program to access the wrong file:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;// Attacker links /home/user/.config to /home/otheruser/.config:&#xA;err := os.WriteFile(&amp;quot;/home/user/.config/foo&amp;quot;, config, 0o666)&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;If the program defends against symlink traversal by first verifying that the intended file&#xA;does not contain any symlinks, it may still be vulnerable to&#xA;&lt;a href=&#34;https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;time-of-check/time-of-use (TOCTOU) races&lt;/a&gt;,&#xA;where the attacker creates a symlink after the program&amp;rsquo;s check:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;// Validate the path before use.&#xA;cleaned, err := filepath.EvalSymlinks(unsafePath)&#xA;if err != nil {&#xA;  return err&#xA;}&#xA;if !filepath.IsLocal(cleaned) {&#xA;  return errors.New(&amp;quot;unsafe path&amp;quot;)&#xA;}&#xA;&#xA;// Attacker replaces part of the path with a symlink.&#xA;// The Open call follows the symlink:&#xA;f, err := os.Open(cleaned)&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Another variety of TOCTOU race involves moving a directory that forms part of a path&#xA;mid-traversal. For example, the attacker provides a path such as &amp;ldquo;a/b/c/../../etc/passwd&amp;rdquo;,&#xA;and renames &amp;ldquo;a/b/c&amp;rdquo; to &amp;ldquo;a/b&amp;rdquo; while the open operation is in progress.&lt;/p&gt;&#xA;&lt;h2 id=&#34;path-sanitization&#34;&gt;Path sanitization&lt;/h2&gt;&#xA;&lt;p&gt;Before we tackle path traversal attacks in general, let&amp;rsquo;s start with path sanitization.&#xA;When a program&amp;rsquo;s threat model does not include attackers with access to the local file system,&#xA;it can be sufficient to validate untrusted input paths before use.&lt;/p&gt;&#xA;&lt;p&gt;Unfortunately, sanitizing paths can be surprisingly tricky,&#xA;especially for portable programs that must handle both Unix and Windows paths.&#xA;For example, on Windows &lt;code&gt;filepath.IsAbs(`\foo`)&lt;/code&gt; reports &lt;code&gt;false&lt;/code&gt;,&#xA;because the path &amp;ldquo;\foo&amp;rdquo; is relative to the current drive.&lt;/p&gt;&#xA;&lt;p&gt;In Go 1.20, we added the &lt;a href=&#34;/pkg/path/filepath#IsLocal&#34;&gt;&lt;code&gt;path/filepath.IsLocal&lt;/code&gt;&lt;/a&gt;&#xA;function, which reports whether a path is &amp;ldquo;local&amp;rdquo;. A &amp;ldquo;local&amp;rdquo; path is one which:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;does not escape the directory in which it is evaluated (&amp;quot;../etc/passwd&amp;quot; is not allowed);&lt;/li&gt;&#xA;&lt;li&gt;is not an absolute path (&amp;quot;/etc/passwd&amp;quot; is not allowed);&lt;/li&gt;&#xA;&lt;li&gt;is not empty (&amp;quot;&amp;quot; is not allowed);&lt;/li&gt;&#xA;&lt;li&gt;on Windows, is not a reserved name (&amp;ldquo;COM1&amp;rdquo; is not allowed).&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;In Go 1.23, we added the &lt;a href=&#34;/pkg/path/filepath#Localize&#34;&gt;&lt;code&gt;path/filepath.Localize&lt;/code&gt;&lt;/a&gt;&#xA;function, which converts a /-separated path into a local operating system path.&lt;/p&gt;&#xA;&lt;p&gt;Programs that accept and operate on potentially attacker-controlled paths should almost&#xA;always use &lt;code&gt;filepath.IsLocal&lt;/code&gt; or &lt;code&gt;filepath.Localize&lt;/code&gt; to validate or sanitize those paths.&lt;/p&gt;&#xA;&lt;h2 id=&#34;beyond-sanitization&#34;&gt;Beyond sanitization&lt;/h2&gt;&#xA;&lt;p&gt;Path sanitization is not sufficient when attackers may have access to part of&#xA;the local filesystem.&lt;/p&gt;&#xA;&lt;p&gt;Multi-user systems are uncommon these days, but attacker access to the filesystem&#xA;can still occur in a variety of ways.&#xA;An unarchiving utility that extracts a tar or zip file may be induced&#xA;to extract a symbolic link and then extract a file name that traverses that link.&#xA;A container runtime may give untrusted code access to a portion of the local filesystem.&lt;/p&gt;&#xA;&lt;p&gt;Programs may defend against unintended symlink traversal by using the&#xA;&lt;a href=&#34;/pkg/path/filepath#EvalSymlinks&#34;&gt;&lt;code&gt;path/filepath.EvalSymlinks&lt;/code&gt;&lt;/a&gt;&#xA;function to resolve links in untrusted names before validation, but as described&#xA;above this two-step process is vulnerable to TOCTOU races.&lt;/p&gt;&#xA;&lt;p&gt;Before Go 1.24, the safer option was to use a package such as&#xA;&lt;a href=&#34;/pkg/github.com/google/safeopen&#34;&gt;github.com/google/safeopen&lt;/a&gt;,&#xA;that provides path traversal-resistant functions for opening a potentially-untrusted&#xA;filename within a specific directory.&lt;/p&gt;&#xA;&lt;h2 id=&#34;introducing-osroot&#34;&gt;Introducing &lt;code&gt;os.Root&lt;/code&gt;&lt;/h2&gt;&#xA;&lt;p&gt;In Go 1.24, we are introducing new APIs in the &lt;code&gt;os&lt;/code&gt; package to safely open&#xA;a file in a location in a traversal-resistant fashion.&lt;/p&gt;&#xA;&lt;p&gt;The new &lt;a href=&#34;/pkg/os#Root&#34;&gt;&lt;code&gt;os.Root&lt;/code&gt;&lt;/a&gt; type represents a directory somewhere&#xA;in the local filesystem. Open a root with the &lt;a href=&#34;/pkg/os#OpenRoot&#34;&gt;&lt;code&gt;os.OpenRoot&lt;/code&gt;&lt;/a&gt;&#xA;function:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;root, err := os.OpenRoot(&amp;quot;/some/root/directory&amp;quot;)&#xA;if err != nil {&#xA;  return err&#xA;}&#xA;defer root.Close()&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;code&gt;Root&lt;/code&gt; provides methods to operate on files within the root.&#xA;These methods all accept filenames relative to the root,&#xA;and disallow any operations that would escape from the root either&#xA;using relative path components (&amp;quot;..&amp;quot;) or symlinks.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;f, err := root.Open(&amp;quot;path/to/file&amp;quot;)&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;code&gt;Root&lt;/code&gt; permits relative path components and symlinks that do not escape the root.&#xA;For example, &lt;code&gt;root.Open(&amp;quot;a/../b&amp;quot;)&lt;/code&gt; is permitted. Filenames are resolved using the&#xA;semantics of the local platform: On Unix systems, this will follow&#xA;any symlink in &amp;ldquo;a&amp;rdquo; (so long as that link does not escape the root);&#xA;while on Windows systems this will open &amp;ldquo;b&amp;rdquo; (even if &amp;ldquo;a&amp;rdquo; does not exist).&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;Root&lt;/code&gt; currently provides the following set of operations:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;func (*Root) Create(string) (*File, error)&#xA;func (*Root) Lstat(string) (fs.FileInfo, error)&#xA;func (*Root) Mkdir(string, fs.FileMode) error&#xA;func (*Root) Open(string) (*File, error)&#xA;func (*Root) OpenFile(string, int, fs.FileMode) (*File, error)&#xA;func (*Root) OpenRoot(string) (*Root, error)&#xA;func (*Root) Remove(string) error&#xA;func (*Root) Stat(string) (fs.FileInfo, error)&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;In addition to the &lt;code&gt;Root&lt;/code&gt; type, the new&#xA;&lt;a href=&#34;/pkg/os#OpenInRoot&#34;&gt;&lt;code&gt;os.OpenInRoot&lt;/code&gt;&lt;/a&gt; function&#xA;provides a simple way to open a potentially-untrusted filename within a&#xA;specific directory:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;f, err := os.OpenInRoot(&amp;quot;/some/root/directory&amp;quot;, untrustedFilename)&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The &lt;code&gt;Root&lt;/code&gt; type provides a simple, safe, portable API for operating with untrusted filenames.&lt;/p&gt;&#xA;&lt;h2 id=&#34;caveats-and-considerations&#34;&gt;Caveats and considerations&lt;/h2&gt;&#xA;&lt;h3 id=&#34;unix&#34;&gt;Unix&lt;/h3&gt;&#xA;&lt;p&gt;On Unix systems, &lt;code&gt;Root&lt;/code&gt; is implemented using the &lt;code&gt;openat&lt;/code&gt; family of system calls.&#xA;A &lt;code&gt;Root&lt;/code&gt; contains a file descriptor referencing its root directory and will track that&#xA;directory across renames or deletion.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;Root&lt;/code&gt; defends against symlink traversal but does not limit traversal&#xA;of mount points. For example, &lt;code&gt;Root&lt;/code&gt; does not prevent traversal of&#xA;Linux bind mounts. Our threat model is that &lt;code&gt;Root&lt;/code&gt; defends against&#xA;filesystem constructs that may be created by ordinary users (such&#xA;as symlinks), but does not handle ones that require root privileges&#xA;to create (such as bind mounts).&lt;/p&gt;&#xA;&lt;h3 id=&#34;windows&#34;&gt;Windows&lt;/h3&gt;&#xA;&lt;p&gt;On Windows, &lt;code&gt;Root&lt;/code&gt; opens a handle referencing its root directory.&#xA;The open handle prevents that directory from being renamed or deleted until the &lt;code&gt;Root&lt;/code&gt; is closed.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;Root&lt;/code&gt; prevents access to reserved Windows device names such as &lt;code&gt;NUL&lt;/code&gt; and &lt;code&gt;COM1&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;wasi&#34;&gt;WASI&lt;/h3&gt;&#xA;&lt;p&gt;On WASI, the &lt;code&gt;os&lt;/code&gt; package uses the WASI preview 1 filesystem API,&#xA;which are intended to provide traversal-resistant filesystem access.&#xA;Not all WASI implementations fully support filesystem sandboxing,&#xA;however, and &lt;code&gt;Root&lt;/code&gt;&amp;rsquo;s defense against traversal is limited to that provided&#xA;by the WASI implementation.&lt;/p&gt;&#xA;&lt;h3 id=&#34;goosjs&#34;&gt;GOOS=js&lt;/h3&gt;&#xA;&lt;p&gt;When GOOS=js, the &lt;code&gt;os&lt;/code&gt; package uses the Node.js file system API.&#xA;This API does not include the openat family of functions,&#xA;and so &lt;code&gt;os.Root&lt;/code&gt; is vulnerable to TOCTOU (time-of-check-time-of-use) races in symlink&#xA;validation on this platform.&lt;/p&gt;&#xA;&lt;p&gt;When GOOS=js, a &lt;code&gt;Root&lt;/code&gt; references a directory name rather than a file descriptor,&#xA;and does not track directories across renames.&lt;/p&gt;&#xA;&lt;h3 id=&#34;plan-9&#34;&gt;Plan 9&lt;/h3&gt;&#xA;&lt;p&gt;Plan 9 does not have symlinks.&#xA;On Plan 9, a &lt;code&gt;Root&lt;/code&gt; references a directory name and performs lexical sanitization of&#xA;filenames.&lt;/p&gt;&#xA;&lt;h3 id=&#34;performance&#34;&gt;Performance&lt;/h3&gt;&#xA;&lt;p&gt;&lt;code&gt;Root&lt;/code&gt; operations on filenames containing many directory components can be much more expensive&#xA;than the equivalent non-&lt;code&gt;Root&lt;/code&gt; operation. Resolving &amp;ldquo;..&amp;rdquo; components can also be expensive.&#xA;Programs that want to limit the cost of filesystem operations can use &lt;code&gt;filepath.Clean&lt;/code&gt; to&#xA;remove &amp;ldquo;..&amp;rdquo; components from input filenames, and may want to limit the number of&#xA;directory components.&lt;/p&gt;&#xA;&lt;h2 id=&#34;who-should-use-osroot&#34;&gt;Who should use os.Root?&lt;/h2&gt;&#xA;&lt;p&gt;You should use &lt;code&gt;os.Root&lt;/code&gt; or &lt;code&gt;os.OpenInRoot&lt;/code&gt; if:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;you are opening a file in a directory; AND&lt;/li&gt;&#xA;&lt;li&gt;the operation should not access a file outside that directory.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;For example, an archive extractor writing files to an output directory should use&#xA;&lt;code&gt;os.Root&lt;/code&gt;, because the filenames are potentially untrusted and it would be incorrect&#xA;to write a file outside the output directory.&lt;/p&gt;&#xA;&lt;p&gt;However, a command-line program that writes output to a user-specified location&#xA;should not use &lt;code&gt;os.Root&lt;/code&gt;, because the filename is not untrusted and may&#xA;refer to anywhere on the filesystem.&lt;/p&gt;&#xA;&lt;p&gt;As a good rule of thumb, code which calls &lt;code&gt;filepath.Join&lt;/code&gt; to combine a fixed directory&#xA;and an externally-provided filename should probably use &lt;code&gt;os.Root&lt;/code&gt; instead.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;// This might open a file not located in baseDirectory.&#xA;f, err := os.Open(filepath.Join(baseDirectory, filename))&#xA;&#xA;// This will only open files under baseDirectory.&#xA;f, err := os.OpenInRoot(baseDirectory, filename)&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h2 id=&#34;future-work&#34;&gt;Future work&lt;/h2&gt;&#xA;&lt;p&gt;The &lt;code&gt;os.Root&lt;/code&gt; API is new in Go 1.24.&#xA;We expect to make additions and refinements to it in future releases.&lt;/p&gt;&#xA;&lt;p&gt;The current implementation prioritizes correctness and safety over performance.&#xA;Future versions will take advantage of platform-specific APIs, such as&#xA;Linux&amp;rsquo;s &lt;code&gt;openat2&lt;/code&gt;, to improve performance where possible.&lt;/p&gt;&#xA;&lt;p&gt;There are a number of filesystem operations which &lt;code&gt;Root&lt;/code&gt; does not support yet, such as&#xA;creating symbolic links and renaming files. Where possible, we will add support for these&#xA;operations. A list of additional functions in progress is in&#xA;&lt;a href=&#34;/issue/67002&#34;&gt;go.dev/issue/67002&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;Article prevnext&#34;&gt;&#xA;    &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;        &lt;p&gt;&#xA;        &#xA;          &#xA;            &lt;b&gt;Next article: &lt;/b&gt;&lt;a href=&#34;/blog/coretypes&#34;&gt;Goodbye core types - Hello Go as we know and love it!&lt;/a&gt;&lt;br&gt;&#xA;          &#xA;        &#xA;        &#xA;          &#xA;            &lt;b&gt;Previous article: &lt;/b&gt;&lt;a href=&#34;/blog/cleanups-and-weak&#34;&gt;From unique to cleanups and weak: new low-level tools for efficiency&lt;/a&gt;&lt;br&gt;&#xA;          &#xA;        &#xA;        &lt;b&gt;&lt;a href=&#34;/blog/all&#34;&gt;Blog Index&lt;/a&gt;&lt;/b&gt;&#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;    &lt;/div&gt;&#xA;    &#xA;&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;script src=&#34;/js/play.js&#34;&gt;&lt;/script&gt;&#xA;&#xA;</content></entry><entry><title>From unique to cleanups and weak: new low-level tools for efficiency</title><id>tag:blog.golang.org,2013:blog.golang.org/cleanups-and-weak</id><link rel="alternate" href="https://go.dev/blog/cleanups-and-weak"></link><published>2025-03-06T00:00:00+00:00</published><updated>2025-03-06T00:00:00+00:00</updated><author><name>Michael Knyszek</name></author><summary type="html">Weak pointers and better finalization in Go 1.24.</summary><content type="html">&#xA;&lt;div id=&#34;blog&#34;&gt;&lt;div id=&#34;content&#34;&gt;&#xA;  &lt;div id=&#34;content&#34;&gt;&#xA;&#xA;    &lt;div class=&#34;Article&#34; data-slug=&#34;/blog/cleanups-and-weak&#34;&gt;&#xA;    &#xA;    &lt;h1 class=&#34;small&#34;&gt;&lt;a href=&#34;/blog/&#34;&gt;The Go Blog&lt;/a&gt;&lt;/h1&gt;&#xA;    &#xA;&#xA;    &lt;h1&gt;From unique to cleanups and weak: new low-level tools for efficiency&lt;/h1&gt;&#xA;      &#xA;      &lt;p class=&#34;author&#34;&gt;&#xA;      Michael Knyszek&lt;br&gt;&#xA;      6 March 2025&#xA;      &lt;/p&gt;&#xA;      &#xA;      &lt;p&gt;In &lt;a href=&#34;/blog/unique&#34;&gt;last year&amp;rsquo;s blog post&lt;/a&gt; about the &lt;code&gt;unique&lt;/code&gt; package, we alluded&#xA;to some new features then in proposal review, and we&amp;rsquo;re excited to share that as&#xA;of Go 1.24 they are now available to all Go developers.&#xA;These new features are &lt;a href=&#34;https://pkg.go.dev/runtime#AddCleanup&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;the &lt;code&gt;runtime.AddCleanup&lt;/code&gt;&#xA;function&lt;/a&gt;, which queues up a function to&#xA;run when an object is no longer reachable, and &lt;a href=&#34;https://pkg.go.dev/weak#Pointer&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;the &lt;code&gt;weak.Pointer&lt;/code&gt;&#xA;type&lt;/a&gt;, which safely points to an object without&#xA;preventing it from being garbage collected.&#xA;Together, these two features are powerful enough to build your own &lt;code&gt;unique&lt;/code&gt;&#xA;package!&#xA;Let&amp;rsquo;s dig into what makes these features useful, and when to use them.&lt;/p&gt;&#xA;&lt;p&gt;Note: these new features are advanced features of the garbage collector.&#xA;If you&amp;rsquo;re not already familiar with basic garbage collection concepts, we&#xA;strongly recommend reading the introduction of our &lt;a href=&#34;/doc/gc-guide#Introduction&#34;&gt;garbage collector&#xA;guide&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;cleanups&#34;&gt;Cleanups&lt;/h2&gt;&#xA;&lt;p&gt;If you&amp;rsquo;ve ever used a finalizer, then the concept of a cleanup will be&#xA;familiar.&#xA;A finalizer is a function, associated with an allocated object by &lt;a href=&#34;https://pkg.go.dev/runtime#SetFinalizer&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;calling&#xA;&lt;code&gt;runtime.SetFinalizer&lt;/code&gt;&lt;/a&gt;, that is later&#xA;called by the garbage collector some time after the object becomes unreachable.&#xA;At a high level, cleanups work the same way.&lt;/p&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s consider an application that makes use of a memory-mapped file, and see&#xA;how cleanups can help.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;//go:build unix&#xA;&#xA;type MemoryMappedFile struct {&#xA;    data []byte&#xA;}&#xA;&#xA;func NewMemoryMappedFile(filename string) (*MemoryMappedFile, error) {&#xA;    f, err := os.Open(filename)&#xA;    if err != nil {&#xA;        return nil, err&#xA;    }&#xA;    defer f.Close()&#xA;&#xA;    // Get the file&#39;s info; we need its size.&#xA;    fi, err := f.Stat()&#xA;    if err != nil {&#xA;        return nil, err&#xA;    }&#xA;&#xA;    // Extract the file descriptor.&#xA;    conn, err := f.SyscallConn()&#xA;    if err != nil {&#xA;        return nil, err&#xA;    }&#xA;    var data []byte&#xA;    connErr := conn.Control(func(fd uintptr) {&#xA;        // Create a memory mapping backed by this file.&#xA;        data, err = syscall.Mmap(int(fd), 0, int(fi.Size()), syscall.PROT_READ, syscall.MAP_SHARED)&#xA;    })&#xA;    if connErr != nil {&#xA;        return nil, connErr&#xA;    }&#xA;    if err != nil {&#xA;        return nil, err&#xA;    }&#xA;    mf := &amp;amp;MemoryMappedFile{data: data}&#xA;    cleanup := func(data []byte) {&#xA;        syscall.Munmap(data) // ignore error&#xA;    }&#xA;    runtime.AddCleanup(mf, cleanup, data)&#xA;    return mf, nil&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;A memory-mapped file has its contents mapped to memory, in this case, the&#xA;underlying data of a byte slice.&#xA;Thanks to operating-system magic, reads and writes to the byte slice directly&#xA;access the contents of the file.&#xA;With this code, we can pass around a &lt;code&gt;*MemoryMappedFile&lt;/code&gt;, and when it&amp;rsquo;s&#xA;no longer referenced, the memory mapping we created will get cleaned up.&lt;/p&gt;&#xA;&lt;p&gt;Notice that &lt;code&gt;runtime.AddCleanup&lt;/code&gt; takes three arguments: the address of a&#xA;variable to attach the cleanup to, the cleanup function itself, and an argument&#xA;to the cleanup function.&#xA;A key difference between this function and &lt;code&gt;runtime.SetFinalizer&lt;/code&gt; is that the&#xA;cleanup function takes a different argument than the object we&amp;rsquo;re attaching the&#xA;cleanup to.&#xA;This change fixes some problems with finalizers.&lt;/p&gt;&#xA;&lt;p&gt;It&amp;rsquo;s no secret that &lt;a href=&#34;/doc/gc-guide#Common_finalizer_issues&#34;&gt;finalizers&#xA;are difficult to use correctly&lt;/a&gt;.&#xA;For example, objects to which finalizers are attached must not be involved&#xA;in any reference cycles (even a pointer to itself is too much!), otherwise the&#xA;object will never be reclaimed and the finalizer will never run, causing a&#xA;leak.&#xA;Finalizers also significantly delay reclamation of memory.&#xA;It takes at a minimum two full garbage collection cycles to reclaim the&#xA;memory for a finalized object: one to determine that it&amp;rsquo;s unreachable, and&#xA;another to determine that it&amp;rsquo;s still unreachable after the finalizer&#xA;executes.&lt;/p&gt;&#xA;&lt;p&gt;The problem is that finalizers &lt;a href=&#34;https://en.wikipedia.org/wiki/Object_resurrection&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;resurrect the objects they&amp;rsquo;re attached&#xA;to&lt;/a&gt;.&#xA;The finalizer doesn&amp;rsquo;t run until the object is unreachable, at which point it is&#xA;considered &amp;ldquo;dead.&amp;rdquo;&#xA;But since the finalizer is called with a pointer to the object, the garbage&#xA;collector must prevent the reclamation of that object&amp;rsquo;s memory, and instead must&#xA;generate a new reference for the finalizer, making it reachable, or &amp;ldquo;live,&amp;rdquo; once&#xA;more.&#xA;That reference may even remain after the finalizer returns, for example if the&#xA;finalizer writes it to a global variable or sends it across a channel.&#xA;Object resurrection is problematic because it means the object, and everything&#xA;it points to, and everything those objects point to, and so on, is reachable,&#xA;even if it would otherwise have been collected as garbage.&lt;/p&gt;&#xA;&lt;p&gt;We solve both of these problems by not passing the original object to the&#xA;cleanup function.&#xA;First, the values the object refers to don&amp;rsquo;t need to be kept specially&#xA;reachable by the garbage collector, so the object can still be reclaimed even&#xA;if it&amp;rsquo;s involved in a cycle.&#xA;Second, since the object is not needed for the cleanup, its memory can be&#xA;reclaimed immediately.&lt;/p&gt;&#xA;&lt;h2 id=&#34;weak-pointers&#34;&gt;Weak pointers&lt;/h2&gt;&#xA;&lt;p&gt;Returning to our memory-mapped file example, suppose we notice that our program&#xA;frequently maps the same files over and over, from different goroutines that are&#xA;unaware of each other.&#xA;This is fine from a memory perspective, since all these mappings will share&#xA;physical memory, but it results in lots of unnecessary system calls to map and&#xA;unmap the file.&#xA;This is especially bad if each goroutine reads only a small section of each&#xA;file.&lt;/p&gt;&#xA;&lt;p&gt;So, let&amp;rsquo;s deduplicate the mappings by filename.&#xA;(Let&amp;rsquo;s assume that our program only reads from the mappings, and the files&#xA;themselves are never modified or renamed once created.&#xA;Such assumptions are reasonable for system font files, for example.)&lt;/p&gt;&#xA;&lt;p&gt;We could maintain a map from filename to memory mapping, but then it becomes&#xA;unclear when it&amp;rsquo;s safe to remove entries from that map.&#xA;We could &lt;em&gt;almost&lt;/em&gt; use a cleanup, if it weren&amp;rsquo;t for the fact that the map entry&#xA;itself will keep the memory-mapped file object alive.&lt;/p&gt;&#xA;&lt;p&gt;Weak pointers solve this problem.&#xA;A weak pointer is a special kind of pointer that the garbage collector ignores&#xA;when deciding whether an object is reachable.&#xA;Go 1.24&amp;rsquo;s &lt;a href=&#34;https://pkg.go.dev/weak#Pointer&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;new weak pointer type,&#xA;&lt;code&gt;weak.Pointer&lt;/code&gt;&lt;/a&gt;, has a &lt;code&gt;Value&lt;/code&gt; method that&#xA;returns either a real pointer if the object is still reachable, or &lt;code&gt;nil&lt;/code&gt; if it&#xA;is not.&lt;/p&gt;&#xA;&lt;p&gt;If we instead maintain a map that only &lt;em&gt;weakly&lt;/em&gt; points to the memory-mapped&#xA;file, we can clean up the map entry when nobody&amp;rsquo;s using it anymore!&#xA;Let&amp;rsquo;s see what this looks like.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;var cache sync.Map // map[string]weak.Pointer[MemoryMappedFile]&#xA;&#xA;func NewCachedMemoryMappedFile(filename string) (*MemoryMappedFile, error) {&#xA;    var newFile *MemoryMappedFile&#xA;    for {&#xA;        // Try to load an existing value out of the cache.&#xA;        value, ok := cache.Load(filename)&#xA;        if !ok {&#xA;            // No value found. Create a new mapped file if needed.&#xA;            if newFile == nil {&#xA;                var err error&#xA;                newFile, err = NewMemoryMappedFile(filename)&#xA;                if err != nil {&#xA;                    return nil, err&#xA;                }&#xA;            }&#xA;&#xA;            // Try to install the new mapped file.&#xA;            wp := weak.Make(newFile)&#xA;            var loaded bool&#xA;            value, loaded = cache.LoadOrStore(filename, wp)&#xA;            if !loaded {&#xA;                runtime.AddCleanup(newFile, func(filename string) {&#xA;                    // Only delete if the weak pointer is equal. If it&#39;s not, someone&#xA;                    // else already deleted the entry and installed a new mapped file.&#xA;                    cache.CompareAndDelete(filename, wp)&#xA;                }, filename)&#xA;                return newFile, nil&#xA;            }&#xA;            // Someone got to installing the file before us.&#xA;            //&#xA;            // If it&#39;s still there when we check in a moment, we&#39;ll discard newFile&#xA;            // and it&#39;ll get cleaned up by garbage collector.&#xA;        }&#xA;&#xA;        // See if our cache entry is valid.&#xA;        if mf := value.(weak.Pointer[MemoryMappedFile]).Value(); mf != nil {&#xA;            return mf, nil&#xA;        }&#xA;&#xA;        // Discovered a nil entry awaiting cleanup. Eagerly delete it.&#xA;        cache.CompareAndDelete(filename, value)&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;This example is a little complicated, but the gist is simple.&#xA;We start with a global concurrent map of all the mapped files we made.&#xA;&lt;code&gt;NewCachedMemoryMappedFile&lt;/code&gt; consults this map for an existing mapped&#xA;file, and if that fails, creates and tries to insert a new mapped file.&#xA;This could of course fail as well since we&amp;rsquo;re racing with other insertions, so&#xA;we need to be careful about that too, and retry.&#xA;(This design has a flaw in that we might wastefully map the same file multiple&#xA;times in a race, and we&amp;rsquo;ll have to throw it away via the cleanup added by&#xA;&lt;code&gt;NewMemoryMappedFile&lt;/code&gt;.&#xA;This is probably not a big deal most of the time.&#xA;Fixing it is left as an exercise for the reader.)&lt;/p&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s look at some useful properties of weak pointers and cleanups exploited by&#xA;this code.&lt;/p&gt;&#xA;&lt;p&gt;Firstly, notice that weak pointers are comparable.&#xA;Not only that, weak pointers have a stable and independent identity, which&#xA;remains even after the objects they point to are long gone.&#xA;This is why it is safe for the cleanup function to call &lt;code&gt;sync.Map&lt;/code&gt;&amp;rsquo;s&#xA;&lt;code&gt;CompareAndDelete&lt;/code&gt;, which compares the &lt;code&gt;weak.Pointer&lt;/code&gt;, and a crucial reason&#xA;this code works at all.&lt;/p&gt;&#xA;&lt;p&gt;Secondly, observe that we can add multiple independent cleanups to a single&#xA;&lt;code&gt;MemoryMappedFile&lt;/code&gt; object.&#xA;This allows us to use cleanups in a composable way and use them to build&#xA;generic data structures.&#xA;In this particular example, it might be more efficient to combine&#xA;&lt;code&gt;NewCachedMemoryMappedFile&lt;/code&gt; with &lt;code&gt;NewMemoryMappedFile&lt;/code&gt; and&#xA;have them share a cleanup.&#xA;However, the advantage of the code we wrote above is that it can be rewritten&#xA;in a generic way!&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;type Cache[K comparable, V any] struct {&#xA;    create func(K) (*V, error)&#xA;    m     sync.Map&#xA;}&#xA;&#xA;func NewCache[K comparable, V any](create func(K) (*V, error)) *Cache[K, V] {&#xA;    return &amp;amp;Cache[K, V]{create: create}&#xA;}&#xA;&#xA;func (c *Cache[K, V]) Get(key K) (*V, error) {&#xA;    var newValue *V&#xA;    for {&#xA;        // Try to load an existing value out of the cache.&#xA;        value, ok := cache.Load(key)&#xA;        if !ok {&#xA;            // No value found. Create a new mapped file if needed.&#xA;            if newValue == nil {&#xA;                var err error&#xA;                newValue, err = c.create(key)&#xA;                if err != nil {&#xA;                    return nil, err&#xA;                }&#xA;            }&#xA;&#xA;            // Try to install the new mapped file.&#xA;            wp := weak.Make(newValue)&#xA;            var loaded bool&#xA;            value, loaded = cache.LoadOrStore(key, wp)&#xA;            if !loaded {&#xA;                runtime.AddCleanup(newValue, func(key K) {&#xA;                    // Only delete if the weak pointer is equal. If it&#39;s not, someone&#xA;                    // else already deleted the entry and installed a new mapped file.&#xA;                    cache.CompareAndDelete(key, wp)&#xA;                }, key)&#xA;                return newValue, nil&#xA;            }&#xA;        }&#xA;&#xA;        // See if our cache entry is valid.&#xA;        if mf := value.(weak.Pointer[V]).Value(); mf != nil {&#xA;            return mf, nil&#xA;        }&#xA;&#xA;        // Discovered a nil entry awaiting cleanup. Eagerly delete it.&#xA;        cache.CompareAndDelete(key, value)&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h2 id=&#34;caveats-and-future-work&#34;&gt;Caveats and future work&lt;/h2&gt;&#xA;&lt;p&gt;Despite our best efforts, cleanups and weak pointers can still be error-prone.&#xA;To guide those considering using finalizers, cleanups, and weak pointers, we&#xA;recently updated the &lt;a href=&#34;/doc/gc-guide#Finalizers_cleanups_and_weak_pointers&#34;&gt;guide to the garbage&#xA;collector&lt;/a&gt; with some advice&#xA;about using these features.&#xA;Take a look next time you reach for them, but also carefully consider whether&#xA;you need to use them at all.&#xA;These are advanced tools with subtle semantics and, as the guide says, most&#xA;Go code benefits from these features indirectly, not from using them directly.&#xA;Stick to the use-cases where these features shine, and you&amp;rsquo;ll be alright.&lt;/p&gt;&#xA;&lt;p&gt;For now, we&amp;rsquo;ll call out some of the issues that you are more likely to run into.&lt;/p&gt;&#xA;&lt;p&gt;First, the object the cleanup is attached to must be reachable from neither&#xA;the cleanup function (as a captured variable) nor the argument to the cleanup&#xA;function.&#xA;Both of these situations result in the cleanup never executing.&#xA;(In the special case of the cleanup argument being exactly the pointer passed&#xA;to &lt;code&gt;runtime.AddCleanup&lt;/code&gt;, &lt;code&gt;runtime.AddCleanup&lt;/code&gt; will panic, as a signal to the&#xA;caller that they should not use cleanups the same way as finalizers.)&lt;/p&gt;&#xA;&lt;p&gt;Second, when weak pointers are used as map keys, the weakly referenced object&#xA;must not be reachable from the corresponding map value, otherwise the object&#xA;will continue to remain live.&#xA;This may seem obvious when deep inside of a blog post about weak pointers, but&#xA;it&amp;rsquo;s an easy subtlety to miss.&#xA;This problem inspired the entire concept of an&#xA;&lt;a href=&#34;https://en.wikipedia.org/wiki/Ephemeron&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;ephemeron&lt;/a&gt; to resolve it, which is a&#xA;potential future direction.&lt;/p&gt;&#xA;&lt;p&gt;Thirdly, a common pattern with cleanups is that a wrapper object is needed, like&#xA;we see here with our &lt;code&gt;MemoryMappedFile&lt;/code&gt; example.&#xA;In this particular case, you could imagine the garbage collector directly&#xA;tracking the mapped memory region and passing around the inner &lt;code&gt;[]byte&lt;/code&gt;.&#xA;Such functionality is possible future work, and an API for it has been recently&#xA;&lt;a href=&#34;/issue/70224&#34;&gt;proposed&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Lastly, both weak pointers and cleanups are inherently non-deterministic, their&#xA;behavior depending intimately on the design and dynamics of the garbage&#xA;collector.&#xA;The documentation for cleanups even permits the garbage collector never to run&#xA;cleanups at all.&#xA;Effectively testing code that uses them can be tricky, but &lt;a href=&#34;/doc/gc-guide#Testing_object_death&#34;&gt;it is&#xA;possible&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-now&#34;&gt;Why now?&lt;/h2&gt;&#xA;&lt;p&gt;Weak pointers have been brought up as a feature for Go since nearly the&#xA;beginning, but for years were not prioritized by the Go team.&#xA;One reason for that is that they are subtle, and the design space of weak&#xA;pointers is a minefield of decisions that can make them even harder to use.&#xA;Another is that weak pointers are a niche tool, while simultaneously adding&#xA;complexity to the language.&#xA;We already had experience with how painful &lt;code&gt;SetFinalizer&lt;/code&gt; could be to use.&#xA;But there are some useful programs that are not expressible without them, and&#xA;the &lt;code&gt;unique&lt;/code&gt; package and the reasons for its existence really emphasized that.&lt;/p&gt;&#xA;&lt;p&gt;With generics, the hindsight of finalizers, and insights from all the great work&#xA;since done by teams in other languages like C# and Java, the designs for weak&#xA;pointers and cleanups came together quickly.&#xA;The desire to use weak pointers with finalizers raised additional questions,&#xA;and so the design for &lt;code&gt;runtime.AddCleanup&lt;/code&gt; quickly came together as well.&lt;/p&gt;&#xA;&lt;h2 id=&#34;acknowledgements&#34;&gt;Acknowledgements&lt;/h2&gt;&#xA;&lt;p&gt;I want to thank everyone in the community who contributed feedback on the&#xA;proposal issues and filed bugs when the features became available.&#xA;I also want to thank David Chase for thoroughly thinking through weak&#xA;pointer semantics with me, and I want to thank him, Russ Cox, and Austin&#xA;Clements for their help with the design of &lt;code&gt;runtime.AddCleanup&lt;/code&gt;.&#xA;I want to thank Carlos Amedee for his work on getting &lt;code&gt;runtime.AddCleanup&lt;/code&gt;&#xA;implemented, polished, landed for Go 1.24.&#xA;And finally I want to thank Carlos Amedee and Ian Lance Taylor for their work&#xA;replacing &lt;code&gt;runtime.SetFinalizer&lt;/code&gt; with &lt;code&gt;runtime.AddCleanup&lt;/code&gt; throughout the&#xA;standard library for Go 1.25.&lt;/p&gt;&#xA;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;Article prevnext&#34;&gt;&#xA;    &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;        &lt;p&gt;&#xA;        &#xA;          &#xA;            &lt;b&gt;Next article: &lt;/b&gt;&lt;a href=&#34;/blog/osroot&#34;&gt;Traversal-resistant file APIs&lt;/a&gt;&lt;br&gt;&#xA;          &#xA;        &#xA;        &#xA;          &#xA;            &lt;b&gt;Previous article: &lt;/b&gt;&lt;a href=&#34;/blog/swisstable&#34;&gt;Faster Go maps with Swiss Tables&lt;/a&gt;&lt;br&gt;&#xA;          &#xA;        &#xA;        &lt;b&gt;&lt;a href=&#34;/blog/all&#34;&gt;Blog Index&lt;/a&gt;&lt;/b&gt;&#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;    &lt;/div&gt;&#xA;    &#xA;&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;script src=&#34;/js/play.js&#34;&gt;&lt;/script&gt;&#xA;&#xA;</content></entry><entry><title>Faster Go maps with Swiss Tables</title><id>tag:blog.golang.org,2013:blog.golang.org/swisstable</id><link rel="alternate" href="https://go.dev/blog/swisstable"></link><published>2025-02-26T00:00:00+00:00</published><updated>2025-02-26T00:00:00+00:00</updated><author><name>Michael Pratt</name></author><summary type="html">Go 1.24 improves map performance with a brand new map implementation</summary><content type="html">&#xA;&lt;div id=&#34;blog&#34;&gt;&lt;div id=&#34;content&#34;&gt;&#xA;  &lt;div id=&#34;content&#34;&gt;&#xA;&#xA;    &lt;div class=&#34;Article&#34; data-slug=&#34;/blog/swisstable&#34;&gt;&#xA;    &#xA;    &lt;h1 class=&#34;small&#34;&gt;&lt;a href=&#34;/blog/&#34;&gt;The Go Blog&lt;/a&gt;&lt;/h1&gt;&#xA;    &#xA;&#xA;    &lt;h1&gt;Faster Go maps with Swiss Tables&lt;/h1&gt;&#xA;      &#xA;      &lt;p class=&#34;author&#34;&gt;&#xA;      Michael Pratt&lt;br&gt;&#xA;      26 February 2025&#xA;      &lt;/p&gt;&#xA;      &#xA;      &lt;p&gt;The hash table is a central data structure in computer science, and it provides the implementation for the map type in many languages, including Go.&lt;/p&gt;&#xA;&lt;p&gt;The concept of a hash table was &lt;a href=&#34;https://spectrum.ieee.org/hans-peter-luhn-and-the-birth-of-the-hashing-algorithm&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;first described&lt;/a&gt; by Hans Peter Luhn in 1953 in an internal IBM memo that suggested speeding up search by placing items into &amp;ldquo;buckets&amp;rdquo; and using a linked list for overflow when buckets already contain an item.&#xA;Today we would call this a &lt;a href=&#34;https://en.wikipedia.org/wiki/Hash_table#Separate_chaining&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;hash table using chaining&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;In 1954, Gene M. Amdahl, Elaine M. McGraw, and Arthur L. Samuel first used an &amp;ldquo;open addressing&amp;rdquo; scheme when programming the IBM 701.&#xA;When a bucket already contains an item, the new item is placed in the next empty bucket.&#xA;This idea was formalized and published in 1957 by W. Wesley Peterson in &lt;a href=&#34;https://ieeexplore.ieee.org/document/5392733&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;&amp;ldquo;Addressing for Random-Access Storage&amp;rdquo;&lt;/a&gt;.&#xA;Today we would call this a &lt;a href=&#34;https://en.wikipedia.org/wiki/Hash_table#Open_addressing&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;hash table using open addressing with linear probing&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;With data structures that have been around this long, it&amp;rsquo;s easy to think that they must be &amp;ldquo;done&amp;rdquo;; that we know everything there is to know about them and they can&amp;rsquo;t be improved anymore.&#xA;That&amp;rsquo;s not true!&#xA;Computer science research continues to make advancements in fundamental algorithms, both in terms of algorithmic complexity and taking advantage of modern CPU hardware.&#xA;For example, Go 1.19 &lt;a href=&#34;/doc/go1.19#sortpkgsort&#34;&gt;switched the &lt;code&gt;sort&lt;/code&gt; package&lt;/a&gt; from a traditional quicksort, to &lt;a href=&#34;https://arxiv.org/pdf/2106.05123.pdf&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;pattern-defeating quicksort&lt;/a&gt;, a novel sorting algorithm from Orson R. L. Peters, first described in 2015.&lt;/p&gt;&#xA;&lt;p&gt;Like sorting algorithms, hash table data structures continue to see improvements.&#xA;In 2017, Sam Benzaquen, Alkis Evlogimenos, Matt Kulukundis, and Roman Perepelitsa at Google presented &lt;a href=&#34;https://www.youtube.com/watch?v=ncHmEUmJZf4&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;a new C++ hash table design&lt;/a&gt;, dubbed &amp;ldquo;Swiss Tables&amp;rdquo;.&#xA;In 2018, their implementation was &lt;a href=&#34;https://abseil.io/blog/20180927-swisstables&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;open sourced in the Abseil C++ library&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Go 1.24 includes a completely new implementation of the built-in map type, based on the Swiss Table design.&#xA;In this blog post we&amp;rsquo;ll look at how Swiss Tables improve upon traditional hash tables, and at some of the unique challenges in bringing the Swiss Table design to Go&amp;rsquo;s maps.&lt;/p&gt;&#xA;&lt;h2 id=&#34;open-addressed-hash-table&#34;&gt;Open-addressed hash table&lt;/h2&gt;&#xA;&lt;p&gt;Swiss Tables are a form of open-addressed hash table, so let&amp;rsquo;s do a quick overview of how a basic open-addressed hash table works.&lt;/p&gt;&#xA;&lt;p&gt;In an open-addressed hash table, all items are stored in a single backing array.&#xA;We&amp;rsquo;ll call each location in the array a &lt;em&gt;slot&lt;/em&gt;.&#xA;The slot to which a key belongs is primarily determined by the &lt;em&gt;hash function&lt;/em&gt;, &lt;code&gt;hash(key)&lt;/code&gt;.&#xA;The hash function maps each key to an integer, where the same key always maps to the same integer, and different keys ideally follow a uniform random distribution of integers.&#xA;The defining feature of open-addressed hash tables is that they resolve collisions by storing the key elsewhere in the backing array.&#xA;So, if the slot is already full (a &lt;em&gt;collision&lt;/em&gt;), then a &lt;em&gt;probe sequence&lt;/em&gt; is used to consider other slots until an empty slot is found.&#xA;Let&amp;rsquo;s take a look at a sample hash table to see how this works.&lt;/p&gt;&#xA;&lt;h3 id=&#34;example&#34;&gt;Example&lt;/h3&gt;&#xA;&lt;p&gt;Below you can see a 16-slot backing array for a hash table, and the key (if any) stored in each slot.&#xA;The values are not shown, as they are not relevant to this example.&lt;/p&gt;&#xA;&lt;style&gt;&#xA; &#xA;@media screen and (max-width: 55em) {&#xA;    .swisstable-table-container {&#xA;         &#xA;        overflow: scroll;&#xA;    }&#xA;}&#xA;&#xA;.swisstable-table {&#xA;     &#xA;    border-collapse: collapse;&#xA;     &#xA;    table-layout: fixed;&#xA;     &#xA;    margin: 0 auto;&#xA;}&#xA;&#xA;.swisstable-table-cell {&#xA;     &#xA;    border: 1px solid;&#xA;     &#xA;    padding: 0.5em 1em 0.5em 1em;&#xA;     &#xA;    text-align: center;&#xA;}&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;swisstable-table-container&#34;&gt;&#xA;    &lt;table class=&#34;swisstable-table&#34;&gt;&#xA;        &lt;thead&gt;&#xA;            &lt;tr&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;Slot&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;0&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;1&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;2&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;3&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;4&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;5&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;6&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;7&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;8&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;9&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;10&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;11&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;12&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;13&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;14&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;15&lt;/th&gt;&#xA;            &lt;/tr&gt;&#xA;        &lt;/thead&gt;&#xA;        &lt;tbody&gt;&#xA;            &lt;tr&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;Key&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;56&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;32&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;21&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;78&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;            &lt;/tr&gt;&#xA;        &lt;/tbody&gt;&#xA;    &lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;To insert a new key, we use the hash function to select a slot.&#xA;Since there are only 16 slots, we need to restrict to this range, so we&amp;rsquo;ll use &lt;code&gt;hash(key) % 16&lt;/code&gt; as the target slot.&#xA;Suppose we want to insert key &lt;code&gt;98&lt;/code&gt; and &lt;code&gt;hash(98) % 16 = 7&lt;/code&gt;.&#xA;Slot 7 is empty, so we simply insert 98 there.&#xA;On the other hand, suppose we want to insert key &lt;code&gt;25&lt;/code&gt; and &lt;code&gt;hash(25) % 16 = 3&lt;/code&gt;.&#xA;Slot 3 is a collision because it already contains key 56.&#xA;Thus we cannot insert here.&lt;/p&gt;&#xA;&lt;p&gt;We use a probe sequence to find another slot.&#xA;There are a variety of well-known probe sequences.&#xA;The original and most straightforward probe sequence is &lt;em&gt;linear probing&lt;/em&gt;, which simply tries successive slots in order.&lt;/p&gt;&#xA;&lt;p&gt;So, in the &lt;code&gt;hash(25) % 16 = 3&lt;/code&gt; example, since slot 3 is in use, we would consider slot 4 next, which is also in use.&#xA;So too is slot 5.&#xA;Finally, we&amp;rsquo;d get to empty slot 6, where we&amp;rsquo;d store key 25.&lt;/p&gt;&#xA;&lt;p&gt;Lookup follows the same approach.&#xA;A lookup of key 25 would start at slot 3, check whether it contains key 25 (it does not), and then continue linear probing until it finds key 25 in slot 6.&lt;/p&gt;&#xA;&lt;p&gt;This example uses a backing array with 16 slots.&#xA;What happens if we insert more than 16 elements?&#xA;If the hash table runs out of space, it will grow, usually by doubling the size of the backing array.&#xA;All existing entries are reinserted into the new backing array.&lt;/p&gt;&#xA;&lt;p&gt;Open-addressed hash tables don&amp;rsquo;t actually wait until the backing array is completely full to grow because as the array gets more full, the average length of each probe sequence increases.&#xA;In the example above using key 25, we must probe 4 different slots to find an empty slot.&#xA;If the array had only one empty slot, the worst case probe length would be O(n).&#xA;That is, you may need to scan the entire array.&#xA;The proportion of used slots is called the &lt;em&gt;load factor&lt;/em&gt;, and most hash tables define a &lt;em&gt;maximum load factor&lt;/em&gt; (typically 70-90%) at which point they will grow to avoid the extremely long probe sequences of very full hash tables.&lt;/p&gt;&#xA;&lt;h2 id=&#34;swiss-table&#34;&gt;Swiss Table&lt;/h2&gt;&#xA;&lt;p&gt;The Swiss Table &lt;a href=&#34;https://abseil.io/about/design/swisstables&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;design&lt;/a&gt; is also a form of open-addressed hash table.&#xA;Let&amp;rsquo;s see how it improves over a traditional open-addressed hash table.&#xA;We still have a single backing array for storage, but we will break the array into logical &lt;em&gt;groups&lt;/em&gt; of 8 slots each.&#xA;(Larger group sizes are possible as well. More on that below.)&lt;/p&gt;&#xA;&lt;p&gt;In addition, each group has a 64-bit &lt;em&gt;control word&lt;/em&gt; for metadata.&#xA;Each of the 8 bytes in the control word corresponds to one of the slots in the group.&#xA;The value of each byte denotes whether that slot is empty, deleted, or in use.&#xA;If it is in use, the byte contains the lower 7 bits of the hash for that slot&amp;rsquo;s key (called &lt;code&gt;h2&lt;/code&gt;).&lt;/p&gt;&#xA;&lt;div class=&#34;swisstable-table-container&#34;&gt;&#xA;    &lt;table class=&#34;swisstable-table&#34;&gt;&#xA;        &lt;thead&gt;&#xA;            &lt;tr&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34; colspan=&#34;8&#34;&gt;Group 0&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34; colspan=&#34;8&#34;&gt;Group 1&lt;/th&gt;&#xA;            &lt;/tr&gt;&#xA;            &lt;tr&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;Slot&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;0&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;1&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;2&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;3&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;4&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;5&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;6&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;7&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;0&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;1&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;2&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;3&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;4&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;5&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;6&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;7&lt;/th&gt;&#xA;            &lt;/tr&gt;&#xA;        &lt;/thead&gt;&#xA;        &lt;tbody&gt;&#xA;            &lt;tr&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;Key&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;56&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;32&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;21&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;78&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;            &lt;/tr&gt;&#xA;        &lt;/tbody&gt;&#xA;    &lt;/table&gt;&#xA;    &lt;br/&gt; &#xA;    &lt;table class=&#34;swisstable-table&#34;&gt;&#xA;        &lt;thead&gt;&#xA;            &lt;tr&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34; colspan=&#34;8&#34;&gt;64-bit control word 0&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34; colspan=&#34;8&#34;&gt;64-bit control word 1&lt;/th&gt;&#xA;            &lt;/tr&gt;&#xA;            &lt;tr&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;Slot&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;0&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;1&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;2&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;3&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;4&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;5&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;6&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;7&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;0&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;1&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;2&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;3&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;4&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;5&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;6&lt;/th&gt;&#xA;                &lt;th class=&#34;swisstable-table-cell&#34;&gt;7&lt;/th&gt;&#xA;            &lt;/tr&gt;&#xA;        &lt;/thead&gt;&#xA;        &lt;tbody&gt;&#xA;            &lt;tr&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;h2&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;23&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;89&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;50&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;47&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;/td&gt;&#xA;            &lt;/tr&gt;&#xA;        &lt;/tbody&gt;&#xA;    &lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;Insertion works as follows:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Compute &lt;code&gt;hash(key)&lt;/code&gt; and break the hash into two parts: the upper 57-bits (called &lt;code&gt;h1&lt;/code&gt;) and the lower 7 bits (called &lt;code&gt;h2&lt;/code&gt;).&lt;/li&gt;&#xA;&lt;li&gt;The upper bits (&lt;code&gt;h1&lt;/code&gt;) are used to select the first group to consider: &lt;code&gt;h1 % 2&lt;/code&gt; in this case, as there are only 2 groups.&lt;/li&gt;&#xA;&lt;li&gt;Within a group, all slots are equally eligible to hold the key. We must first determine whether any slot already contains this key, in which case this is an update rather than a new insertion.&lt;/li&gt;&#xA;&lt;li&gt;If no slot contains the key, then we look for an empty slot to place this key.&lt;/li&gt;&#xA;&lt;li&gt;If no slot is empty, then we continue the probe sequence by searching the next group.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Lookup follows the same basic process.&#xA;If we find an empty slot in step 4, then we know an insertion would have used this slot and can stop the search.&lt;/p&gt;&#xA;&lt;p&gt;Step 3 is where the Swiss Table magic happens.&#xA;We need to check whether any slot in a group contains the desired key.&#xA;Naively, we could just do a linear scan and compare all 8 keys.&#xA;However, the control word lets us do this more efficiently.&#xA;Each byte contains the lower 7 bits of the hash (&lt;code&gt;h2&lt;/code&gt;) for that slot.&#xA;If we determine which bytes of the control word contain the &lt;code&gt;h2&lt;/code&gt; we are looking for, we&amp;rsquo;ll have a set of candidate matches.&lt;/p&gt;&#xA;&lt;p&gt;In other words, we want to do a byte-by-byte equality comparison within the control word.&#xA;For example, if we are looking for key 32, where &lt;code&gt;h2 = 89&lt;/code&gt;, the operation we want looks like so.&lt;/p&gt;&#xA;&lt;div class=&#34;swisstable-table-container&#34;&gt;&#xA;    &lt;table class=&#34;swisstable-table&#34;&gt;&#xA;        &lt;tbody&gt;&#xA;            &lt;tr&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;strong&gt;Test word&lt;/strong&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;89&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;89&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;89&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;89&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;89&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;89&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;89&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;89&lt;/td&gt;&#xA;            &lt;/tr&gt;&#xA;            &lt;tr&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;strong&gt;Comparison&lt;/strong&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;==&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;==&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;==&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;==&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;==&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;==&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;==&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;==&lt;/td&gt;&#xA;            &lt;/tr&gt;&#xA;            &lt;tr&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;strong&gt;Control word&lt;/strong&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;23&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;89&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;50&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;-&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;-&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;-&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;-&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;-&lt;/td&gt;&#xA;            &lt;/tr&gt;&#xA;            &lt;tr&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;&lt;strong&gt;Result&lt;/strong&gt;&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;0&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;1&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;0&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;0&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;0&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;0&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;0&lt;/td&gt;&#xA;                &lt;td class=&#34;swisstable-table-cell&#34;&gt;0&lt;/td&gt;&#xA;            &lt;/tr&gt;&#xA;        &lt;/tbody&gt;&#xA;    &lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;This is an operation supported by &lt;a href=&#34;https://en.wikipedia.org/wiki/Single_instruction,_multiple_data&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;SIMD&lt;/a&gt; hardware, where a single instruction performs parallel operations on independent values within a larger value (&lt;em&gt;vector&lt;/em&gt;). In this case, we &lt;a href=&#34;https://cs.opensource.google/go/go/+/master:src/internal/runtime/maps/group.go;drc=a08984bc8f2acacebeeadf7445ecfb67b7e7d7b1;l=155?ss=go&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;can implement this operation&lt;/a&gt; using a set of standard arithmetic and bitwise operations when special SIMD hardware is not available.&lt;/p&gt;&#xA;&lt;p&gt;The result is a set of candidate slots.&#xA;Slots where &lt;code&gt;h2&lt;/code&gt; does not match do not have a matching key, so they can be skipped.&#xA;Slots where &lt;code&gt;h2&lt;/code&gt; does match are potential matches, but we must still check the entire key, as there is potential for collisions (1/128 probability of collision with a 7-bit hash, so still quite low).&lt;/p&gt;&#xA;&lt;p&gt;This operation is very powerful, as we have effectively performed 8 steps of a probe sequence at once, in parallel.&#xA;This speeds up lookup and insertion by reducing the average number of comparisons we need to perform.&#xA;This improvement to probing behavior allowed both the Abseil and Go implementations to increase the maximum load factor of Swiss Table maps compared to prior maps, which lowers the average memory footprint.&lt;/p&gt;&#xA;&lt;h2 id=&#34;go-challenges&#34;&gt;Go challenges&lt;/h2&gt;&#xA;&lt;p&gt;Go&amp;rsquo;s built-in map type has some unusual properties that pose additional challenges to adopting a new map design.&#xA;Two were particularly tricky to deal with.&lt;/p&gt;&#xA;&lt;h3 id=&#34;incremental-growth&#34;&gt;Incremental growth&lt;/h3&gt;&#xA;&lt;p&gt;When a hash table reaches its maximum load factor, it needs to grow the backing array.&#xA;Typically this means the next insertion doubles the size of the array, and copies all entries to the new array.&#xA;Imagine inserting into a map with 1GB of entries.&#xA;Most insertions are very fast, but the one insertion that needs to grow the map from 1GB to 2GB will need to copy 1GB of entries, which will take a long time.&lt;/p&gt;&#xA;&lt;p&gt;Go is frequently used for latency-sensitive servers, so we don&amp;rsquo;t want operations on built-in types that can have arbitrarily large impact on tail latency.&#xA;Instead, Go maps grow incrementally, so that each insertion has an upper bound on the amount of growth work it must do.&#xA;This bounds the latency impact of a single map insertion.&lt;/p&gt;&#xA;&lt;p&gt;Unfortunately, the Abseil (C++) Swiss Table design assumes all at once growth, and the probe sequence depends on the total group count, making it difficult to break up.&lt;/p&gt;&#xA;&lt;p&gt;Go&amp;rsquo;s built-in map addresses this with another layer of indirection by splitting each map into multiple Swiss Tables.&#xA;Rather than a single Swiss Table implementing the entire map, each map consists of one or more independent tables that cover a subset of the key space.&#xA;An individual table stores a maximum of 1024 entries.&#xA;A variable number of upper bits in the hash are used to select which table a key belongs to.&#xA;This is a form of &lt;a href=&#34;https://en.wikipedia.org/wiki/Extendible_hashing&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;&lt;em&gt;extendible hashing&lt;/em&gt;&lt;/a&gt;, where the number of bits used increases as needed to differentiate the total number of tables.&lt;/p&gt;&#xA;&lt;p&gt;During insertion, if an individual table needs to grow, it will do so all at once, but other tables are unaffected.&#xA;Thus the upper bound for a single insertion is the latency of growing a 1024-entry table into two 1024-entry tables, copying 1024 entries.&lt;/p&gt;&#xA;&lt;h3 id=&#34;modification-during-iteration&#34;&gt;Modification during iteration&lt;/h3&gt;&#xA;&lt;p&gt;Many hash table designs, including Abseil&amp;rsquo;s Swiss Tables, forbid modifying the map during iteration.&#xA;The Go language spec &lt;a href=&#34;/ref/spec#For_statements:~:text=The%20iteration%20order,iterations%20is%200.&#34;&gt;explicitly allows&lt;/a&gt; modifications during iteration, with the following semantics:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;If an entry is deleted before it is reached, it will not be produced.&lt;/li&gt;&#xA;&lt;li&gt;If an entry is updated before it is reached, the updated value will be produced.&lt;/li&gt;&#xA;&lt;li&gt;If a new entry is added, it may or may not be produced.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;A typical approach to hash table iteration is to simply walk through the backing array and produce values in the order they are laid out in memory.&#xA;This approach runs afoul of the above semantics, most notably because insertions may grow the map, which would shuffle the memory layout.&lt;/p&gt;&#xA;&lt;p&gt;We can avoid the impact of shuffle during growth by having the iterator keep a reference to the table it is currently iterating over.&#xA;If that table grows during iteration, we keep using the old version of the table and thus continue to deliver keys in the order of the old memory layout.&lt;/p&gt;&#xA;&lt;p&gt;Does this work with the above semantics?&#xA;New entries added after growth will be missed entirely, as they are only added to the grown table, not the old table.&#xA;That&amp;rsquo;s fine, as the semantics allow new entries not to be produced.&#xA;Updates and deletions are a problem, though: using the old table could produce stale or deleted entries.&lt;/p&gt;&#xA;&lt;p&gt;This edge case is addressed by using the old table only to determine the iteration order.&#xA;Before actually returning the entry, we consult the grown table to determine whether the entry still exists, and to retrieve the latest value.&lt;/p&gt;&#xA;&lt;p&gt;This covers all the core semantics, though there are even more small edge cases not covered here.&#xA;Ultimately, the permissiveness of Go maps with iteration results in iteration being the most complex part of Go&amp;rsquo;s map implementation.&lt;/p&gt;&#xA;&lt;h2 id=&#34;future-work&#34;&gt;Future work&lt;/h2&gt;&#xA;&lt;p&gt;In &lt;a href=&#34;/issue/54766#issuecomment-2542444404&#34;&gt;microbenchmarks&lt;/a&gt;, map operations are up to 60% faster than in Go 1.23.&#xA;Exact performance improvement varies quite a bit due to the wide variety of operations and uses of maps, and some edge cases do regress compared to Go 1.23.&#xA;Overall, in full application benchmarks, we found a geometric mean CPU time improvement of around 1.5%.&lt;/p&gt;&#xA;&lt;p&gt;There are more map improvements we want to investigate for future Go releases.&#xA;For example, we may be able to &lt;a href=&#34;/issue/70835&#34;&gt;increase the locality of&lt;/a&gt; operations on maps that are not in the CPU cache.&lt;/p&gt;&#xA;&lt;p&gt;We could also further improve the control word comparisons.&#xA;As described above, we have a portable implementation using standard arithmetic and bitwise operations.&#xA;However, some architectures have SIMD instructions that perform this sort of comparison directly.&#xA;Go 1.24 already uses 8-byte SIMD instructions for amd64, but we could extend support to other architectures.&#xA;More importantly, while standard instructions operate on up to 8-byte words, SIMD instructions nearly always support at least 16-byte words.&#xA;This means we could increase the group size to 16 slots, and perform 16 hash comparisons in parallel instead of 8.&#xA;This would further decrease the average number of probes required for lookups.&lt;/p&gt;&#xA;&lt;h2 id=&#34;acknowledgements&#34;&gt;Acknowledgements&lt;/h2&gt;&#xA;&lt;p&gt;A Swiss Table-based Go map implementation has been a long time coming, and involved many contributors.&#xA;I want to thank YunHao Zhang (&lt;a href=&#34;https://github.com/zhangyunhao116&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;@zhangyunhao116&lt;/a&gt;), PJ Malloy (&lt;a href=&#34;https://github.com/thepudds&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;@thepudds&lt;/a&gt;), and &lt;a href=&#34;https://github.com/andy-wm-arthur&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;@andy-wm-arthur&lt;/a&gt; for building initial versions of a Go Swiss Table implementation.&#xA;Peter Mattis (&lt;a href=&#34;https://github.com/petermattis&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;@petermattis&lt;/a&gt;) combined these ideas with solutions to the Go challenges above to build &lt;a href=&#34;https://pkg.go.dev/github.com/cockroachdb/swiss&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;&lt;code&gt;github.com/cockroachdb/swiss&lt;/code&gt;&lt;/a&gt;, a Go-spec compliant Swiss Table implementation.&#xA;The Go 1.24 built-in map implementation is heavily based on Peter&amp;rsquo;s work.&#xA;Thank you to everyone in the community that contributed!&lt;/p&gt;&#xA;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;Article prevnext&#34;&gt;&#xA;    &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;        &lt;p&gt;&#xA;        &#xA;          &#xA;            &lt;b&gt;Next article: &lt;/b&gt;&lt;a href=&#34;/blog/cleanups-and-weak&#34;&gt;From unique to cleanups and weak: new low-level tools for efficiency&lt;/a&gt;&lt;br&gt;&#xA;          &#xA;        &#xA;        &#xA;          &#xA;            &lt;b&gt;Previous article: &lt;/b&gt;&lt;a href=&#34;/blog/synctest&#34;&gt;Testing concurrent code with testing/synctest&lt;/a&gt;&lt;br&gt;&#xA;          &#xA;        &#xA;        &lt;b&gt;&lt;a href=&#34;/blog/all&#34;&gt;Blog Index&lt;/a&gt;&lt;/b&gt;&#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;    &lt;/div&gt;&#xA;    &#xA;&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;script src=&#34;/js/play.js&#34;&gt;&lt;/script&gt;&#xA;&#xA;</content></entry><entry><title>Testing concurrent code with testing/synctest</title><id>tag:blog.golang.org,2013:blog.golang.org/synctest</id><link rel="alternate" href="https://go.dev/blog/synctest"></link><published>2025-02-19T00:00:00+00:00</published><updated>2025-02-19T00:00:00+00:00</updated><author><name>Damien Neil</name></author><summary type="html">Go 1.24 contains an experimental package to aid in testing concurrent code.</summary><content type="html">&#xA;&lt;div id=&#34;blog&#34;&gt;&lt;div id=&#34;content&#34;&gt;&#xA;  &lt;div id=&#34;content&#34;&gt;&#xA;&#xA;    &lt;div class=&#34;Article&#34; data-slug=&#34;/blog/synctest&#34;&gt;&#xA;    &#xA;    &lt;h1 class=&#34;small&#34;&gt;&lt;a href=&#34;/blog/&#34;&gt;The Go Blog&lt;/a&gt;&lt;/h1&gt;&#xA;    &#xA;&#xA;    &lt;h1&gt;Testing concurrent code with testing/synctest&lt;/h1&gt;&#xA;      &#xA;      &lt;p class=&#34;author&#34;&gt;&#xA;      Damien Neil&lt;br&gt;&#xA;      19 February 2025&#xA;      &lt;/p&gt;&#xA;      &#xA;      &lt;p&gt;One of Go&amp;rsquo;s signature features is built-in support for concurrency.&#xA;Goroutines and channels are simple and effective primitives for&#xA;writing concurrent programs.&lt;/p&gt;&#xA;&lt;p&gt;However, testing concurrent programs can be difficult and error prone.&lt;/p&gt;&#xA;&lt;p&gt;In Go 1.24, we are introducing a new, experimental&#xA;&lt;a href=&#34;/pkg/testing/synctest&#34;&gt;&lt;code&gt;testing/synctest&lt;/code&gt;&lt;/a&gt; package&#xA;to support testing concurrent code. This post will explain the motivation behind&#xA;this experiment, demonstrate how to use the synctest package, and discuss its potential future.&lt;/p&gt;&#xA;&lt;p&gt;In Go 1.24, the &lt;code&gt;testing/synctest&lt;/code&gt; package is experimental and&#xA;not subject to the Go compatibility promise.&#xA;It is not visible by default.&#xA;To use it, compile your code with &lt;code&gt;GOEXPERIMENT=synctest&lt;/code&gt; set in your environment.&lt;/p&gt;&#xA;&lt;h2 id=&#34;testing-concurrent-programs-is-difficult&#34;&gt;Testing concurrent programs is difficult&lt;/h2&gt;&#xA;&lt;p&gt;To begin with, let us consider a simple example.&lt;/p&gt;&#xA;&lt;p&gt;The &lt;a href=&#34;/pkg/context#AfterFunc&#34;&gt;&lt;code&gt;context.AfterFunc&lt;/code&gt;&lt;/a&gt; function&#xA;arranges for a function to be called in its own goroutine after a context is canceled.&#xA;Here is a possible test for &lt;code&gt;AfterFunc&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;func TestAfterFunc(t *testing.T) {&#xA;    ctx, cancel := context.WithCancel(context.Background())&#xA;&#xA;    calledCh := make(chan struct{}) // closed when AfterFunc is called&#xA;    context.AfterFunc(ctx, func() {&#xA;        close(calledCh)&#xA;    })&#xA;&#xA;    // TODO: Assert that the AfterFunc has not been called.&#xA;&#xA;    cancel()&#xA;&#xA;    // TODO: Assert that the AfterFunc has been called.&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;We want to check two conditions in this test:&#xA;The function is not called before the context is canceled,&#xA;and the function &lt;em&gt;is&lt;/em&gt; called after the context is canceled.&lt;/p&gt;&#xA;&lt;p&gt;Checking a negative in a concurrent system is difficult.&#xA;We can easily test that the function has not been called &lt;em&gt;yet&lt;/em&gt;,&#xA;but how do we check that it &lt;em&gt;will not&lt;/em&gt; be called?&lt;/p&gt;&#xA;&lt;p&gt;A common approach is to wait for some amount of time before&#xA;concluding that an event will not happen.&#xA;Let&amp;rsquo;s try introducing a helper function to our test which does this.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;// funcCalled reports whether the function was called.&#xA;funcCalled := func() bool {&#xA;    select {&#xA;    case &amp;lt;-calledCh:&#xA;        return true&#xA;    case &amp;lt;-time.After(10 * time.Millisecond):&#xA;        return false&#xA;    }&#xA;}&#xA;&#xA;if funcCalled() {&#xA;    t.Fatalf(&amp;quot;AfterFunc function called before context is canceled&amp;quot;)&#xA;}&#xA;&#xA;cancel()&#xA;&#xA;if !funcCalled() {&#xA;    t.Fatalf(&amp;quot;AfterFunc function not called after context is canceled&amp;quot;)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;This test is slow:&#xA;10 milliseconds isn&amp;rsquo;t a lot of time, but it adds up over many tests.&lt;/p&gt;&#xA;&lt;p&gt;This test is also flaky:&#xA;10 milliseconds is a long time on a fast computer,&#xA;but it isn&amp;rsquo;t unusual to see pauses lasting several seconds&#xA;on shared and overloaded&#xA;&lt;a href=&#34;https://en.wikipedia.org/wiki/Continuous_integration&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;CI&lt;/a&gt;&#xA;systems.&lt;/p&gt;&#xA;&lt;p&gt;We can make the test less flaky at the expense of making it slower,&#xA;and we can make it less slow at the expense of making it flakier,&#xA;but we can&amp;rsquo;t make it both fast and reliable.&lt;/p&gt;&#xA;&lt;h2 id=&#34;introducing-the-testingsynctest-package&#34;&gt;Introducing the testing/synctest package&lt;/h2&gt;&#xA;&lt;p&gt;The &lt;code&gt;testing/synctest&lt;/code&gt; package solves this problem.&#xA;It allows us to rewrite this test to be simple, fast, and reliable,&#xA;without any changes to the code being tested.&lt;/p&gt;&#xA;&lt;p&gt;The package contains only two functions: &lt;code&gt;Run&lt;/code&gt; and &lt;code&gt;Wait&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;Run&lt;/code&gt; calls a function in a new goroutine.&#xA;This goroutine and any goroutines started by it&#xA;exist in an isolated environment which we call a &lt;em&gt;bubble&lt;/em&gt;.&#xA;&lt;code&gt;Wait&lt;/code&gt; waits for every goroutine in the current goroutine&amp;rsquo;s bubble&#xA;to block on another goroutine in the bubble.&lt;/p&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s rewrite our test above using the &lt;code&gt;testing/synctest&lt;/code&gt; package.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;func TestAfterFunc(t *testing.T) {&#xA;    synctest.Run(func() {&#xA;        ctx, cancel := context.WithCancel(context.Background())&#xA;&#xA;        funcCalled := false&#xA;        context.AfterFunc(ctx, func() {&#xA;            funcCalled = true&#xA;        })&#xA;&#xA;        synctest.Wait()&#xA;        if funcCalled {&#xA;            t.Fatalf(&amp;quot;AfterFunc function called before context is canceled&amp;quot;)&#xA;        }&#xA;&#xA;        cancel()&#xA;&#xA;        synctest.Wait()&#xA;        if !funcCalled {&#xA;            t.Fatalf(&amp;quot;AfterFunc function not called after context is canceled&amp;quot;)&#xA;        }&#xA;    })&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;This is almost identical to our original test,&#xA;but we have wrapped the test in a &lt;code&gt;synctest.Run&lt;/code&gt; call&#xA;and we call &lt;code&gt;synctest.Wait&lt;/code&gt; before asserting that the function has been called or not.&lt;/p&gt;&#xA;&lt;p&gt;The &lt;code&gt;Wait&lt;/code&gt; function waits for every goroutine in the caller&amp;rsquo;s bubble to block.&#xA;When it returns, we know that the context package has either called the function,&#xA;or will not call it until we take some further action.&lt;/p&gt;&#xA;&lt;p&gt;This test is now both fast and reliable.&lt;/p&gt;&#xA;&lt;p&gt;The test is simpler, too:&#xA;we have replaced the &lt;code&gt;calledCh&lt;/code&gt; channel with a boolean.&#xA;Previously we needed to use a channel to avoid a data race between&#xA;the test goroutine and the &lt;code&gt;AfterFunc&lt;/code&gt; goroutine,&#xA;but the &lt;code&gt;Wait&lt;/code&gt; function now provides that synchronization.&lt;/p&gt;&#xA;&lt;p&gt;The race detector understands &lt;code&gt;Wait&lt;/code&gt; calls,&#xA;and this test passes when run with &lt;code&gt;-race&lt;/code&gt;.&#xA;If we remove the second &lt;code&gt;Wait&lt;/code&gt; call,&#xA;the race detector will correctly report a data race in the test.&lt;/p&gt;&#xA;&lt;h2 id=&#34;testing-time&#34;&gt;Testing time&lt;/h2&gt;&#xA;&lt;p&gt;Concurrent code often deals with time.&lt;/p&gt;&#xA;&lt;p&gt;Testing code that works with time can be difficult.&#xA;Using real time in tests causes slow and flaky tests,&#xA;as we have seen above.&#xA;Using fake time requires avoiding &lt;code&gt;time&lt;/code&gt; package functions,&#xA;and designing the code under test to work with&#xA;an optional fake clock.&lt;/p&gt;&#xA;&lt;p&gt;The &lt;code&gt;testing/synctest&lt;/code&gt; package makes it simpler to test code that uses time.&lt;/p&gt;&#xA;&lt;p&gt;Goroutines in the bubble started by &lt;code&gt;Run&lt;/code&gt; use a fake clock.&#xA;Within the bubble, functions in the &lt;code&gt;time&lt;/code&gt; package operate on the&#xA;fake clock. Time advances in the bubble when all goroutines are&#xA;blocked.&lt;/p&gt;&#xA;&lt;p&gt;To demonstrate, let&amp;rsquo;s write a test for the&#xA;&lt;a href=&#34;/pkg/context#WithTimeout&#34;&gt;&lt;code&gt;context.WithTimeout&lt;/code&gt;&lt;/a&gt; function.&#xA;&lt;code&gt;WithTimeout&lt;/code&gt; creates a child of a context,&#xA;which expires after a given timeout.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;func TestWithTimeout(t *testing.T) {&#xA;    synctest.Run(func() {&#xA;        const timeout = 5 * time.Second&#xA;        ctx, cancel := context.WithTimeout(context.Background(), timeout)&#xA;        defer cancel()&#xA;&#xA;        // Wait just less than the timeout.&#xA;        time.Sleep(timeout - time.Nanosecond)&#xA;        synctest.Wait()&#xA;        if err := ctx.Err(); err != nil {&#xA;            t.Fatalf(&amp;quot;before timeout, ctx.Err() = %v; want nil&amp;quot;, err)&#xA;        }&#xA;&#xA;        // Wait the rest of the way until the timeout.&#xA;        time.Sleep(time.Nanosecond)&#xA;        synctest.Wait()&#xA;        if err := ctx.Err(); err != context.DeadlineExceeded {&#xA;            t.Fatalf(&amp;quot;after timeout, ctx.Err() = %v; want DeadlineExceeded&amp;quot;, err)&#xA;        }&#xA;    })&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;We write this test just as if we were working with real time.&#xA;The only difference is that we wrap the test function in &lt;code&gt;synctest.Run&lt;/code&gt;,&#xA;and call &lt;code&gt;synctest.Wait&lt;/code&gt; after each &lt;code&gt;time.Sleep&lt;/code&gt; call to wait for the context&#xA;package&amp;rsquo;s timers to finish running.&lt;/p&gt;&#xA;&lt;h2 id=&#34;blocking-and-the-bubble&#34;&gt;Blocking and the bubble&lt;/h2&gt;&#xA;&lt;p&gt;A key concept in &lt;code&gt;testing/synctest&lt;/code&gt; is the bubble becoming &lt;em&gt;durably blocked&lt;/em&gt;.&#xA;This happens when every goroutine in the bubble is blocked,&#xA;and can only be unblocked by another goroutine in the bubble.&lt;/p&gt;&#xA;&lt;p&gt;When a bubble is durably blocked:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;If there is an outstanding &lt;code&gt;Wait&lt;/code&gt; call, it returns.&lt;/li&gt;&#xA;&lt;li&gt;Otherwise, time advances to the next time that could unblock a goroutine, if any.&lt;/li&gt;&#xA;&lt;li&gt;Otherwise, the bubble is deadlocked and &lt;code&gt;Run&lt;/code&gt; panics.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;A bubble is not durably blocked if any goroutine is blocked&#xA;but might be woken by some event from outside the bubble.&lt;/p&gt;&#xA;&lt;p&gt;The complete list of operations which durably block a goroutine is:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;a send or receive on a nil channel&lt;/li&gt;&#xA;&lt;li&gt;a send or receive blocked on a channel created within the same bubble&lt;/li&gt;&#xA;&lt;li&gt;a select statement where every case is durably blocking&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;time.Sleep&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;sync.Cond.Wait&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;sync.WaitGroup.Wait&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;mutexes&#34;&gt;Mutexes&lt;/h3&gt;&#xA;&lt;p&gt;Operations on a &lt;code&gt;sync.Mutex&lt;/code&gt; are not durably blocking.&lt;/p&gt;&#xA;&lt;p&gt;It is common for functions to acquire a global mutex.&#xA;For example, a number of functions in the reflect package&#xA;use a global cache guarded by a mutex.&#xA;If a goroutine in a synctest bubble blocks while acquiring&#xA;a mutex held by a goroutine outside the bubble,&#xA;it is not durably blocked—it is blocked, but will be unblocked&#xA;by a goroutine from outside its bubble.&lt;/p&gt;&#xA;&lt;p&gt;Since mutexes are usually not held for long periods of time,&#xA;we simply exclude them from &lt;code&gt;testing/synctest&lt;/code&gt;&amp;rsquo;s consideration.&lt;/p&gt;&#xA;&lt;h3 id=&#34;channels&#34;&gt;Channels&lt;/h3&gt;&#xA;&lt;p&gt;Channels created within a bubble behave differently from ones created outside.&lt;/p&gt;&#xA;&lt;p&gt;Channel operations are durably blocking only if the channel is bubbled&#xA;(created in the bubble).&#xA;Operating on a bubbled channel from outside the bubble panics.&lt;/p&gt;&#xA;&lt;p&gt;These rules ensure that a goroutine is durably blocked only when&#xA;communicating with goroutines within its bubble.&lt;/p&gt;&#xA;&lt;h3 id=&#34;io&#34;&gt;I/O&lt;/h3&gt;&#xA;&lt;p&gt;External I/O operations, such as reading from a network connection,&#xA;are not durably blocking.&lt;/p&gt;&#xA;&lt;p&gt;Network reads may be unblocked by writes from outside the bubble,&#xA;possibly even from other processes.&#xA;Even if the only writer to a network connection is also in the same bubble,&#xA;the runtime cannot distinguish between a connection waiting for more data to arrive&#xA;and one where the kernel has received data and is in the process of delivering it.&lt;/p&gt;&#xA;&lt;p&gt;Testing a network server or client with synctest will generally&#xA;require supplying a fake network implementation.&#xA;For example, the &lt;a href=&#34;/pkg/net#Pipe&#34;&gt;&lt;code&gt;net.Pipe&lt;/code&gt;&lt;/a&gt; function&#xA;creates a pair of &lt;code&gt;net.Conn&lt;/code&gt;s that use an in-memory network connection&#xA;and can be used in synctest tests.&lt;/p&gt;&#xA;&lt;h2 id=&#34;bubble-lifetime&#34;&gt;Bubble lifetime&lt;/h2&gt;&#xA;&lt;p&gt;The &lt;code&gt;Run&lt;/code&gt; function starts a goroutine in a new bubble.&#xA;It returns when every goroutine in the bubble has exited.&#xA;It panics if the bubble is durably blocked&#xA;and cannot be unblocked by advancing time.&lt;/p&gt;&#xA;&lt;p&gt;The requirement that every goroutine in the bubble exit before Run returns&#xA;means that tests must be careful to clean up any background goroutines&#xA;before completing.&lt;/p&gt;&#xA;&lt;h2 id=&#34;testing-networked-code&#34;&gt;Testing networked code&lt;/h2&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s look at another example, this time using the &lt;code&gt;testing/synctest&lt;/code&gt;&#xA;package to test a networked program.&#xA;For this example, we&amp;rsquo;ll test the &lt;code&gt;net/http&lt;/code&gt; package&amp;rsquo;s handling of&#xA;the 100 Continue response.&lt;/p&gt;&#xA;&lt;p&gt;An HTTP client sending a request can include an &amp;ldquo;Expect: 100-continue&amp;rdquo;&#xA;header to tell the server that the client has additional data to send.&#xA;The server may then respond with a 100 Continue informational response&#xA;to request the rest of the request,&#xA;or with some other status to tell the client that the content is not needed.&#xA;For example, a client uploading a large file might use this feature to&#xA;confirm that the server is willing to accept the file before sending it.&lt;/p&gt;&#xA;&lt;p&gt;Our test will confirm that when sending an &amp;ldquo;Expect: 100-continue&amp;rdquo; header&#xA;the HTTP client does not send a request&amp;rsquo;s content before the server&#xA;requests it, and that it does send the content after receiving a&#xA;100 Continue response.&lt;/p&gt;&#xA;&lt;p&gt;Often tests of a communicating client and server can use a&#xA;loopback network connection. When working with &lt;code&gt;testing/synctest&lt;/code&gt;,&#xA;however, we will usually want to use a fake network connection&#xA;to allow us to detect when all goroutines are blocked on the network.&#xA;We&amp;rsquo;ll start this test by creating an &lt;code&gt;http.Transport&lt;/code&gt; (an HTTP client) that uses&#xA;an in-memory network connection created by &lt;a href=&#34;/pkg/net#Pipe&#34;&gt;&lt;code&gt;net.Pipe&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;func Test(t *testing.T) {&#xA;    synctest.Run(func() {&#xA;        srvConn, cliConn := net.Pipe()&#xA;        defer srvConn.Close()&#xA;        defer cliConn.Close()&#xA;        tr := &amp;amp;http.Transport{&#xA;            DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {&#xA;                return cliConn, nil&#xA;            },&#xA;            // Setting a non-zero timeout enables &amp;quot;Expect: 100-continue&amp;quot; handling.&#xA;            // Since the following test does not sleep,&#xA;            // we will never encounter this timeout,&#xA;            // even if the test takes a long time to run on a slow machine.&#xA;            ExpectContinueTimeout: 5 * time.Second,&#xA;        }&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;We send a request on this transport with the &amp;ldquo;Expect: 100-continue&amp;rdquo; header set.&#xA;The request is sent in a new goroutine, since it won&amp;rsquo;t complete until the end of the test.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;        body := &amp;quot;request body&amp;quot;&#xA;        go func() {&#xA;            req, _ := http.NewRequest(&amp;quot;PUT&amp;quot;, &amp;quot;http://test.tld/&amp;quot;, strings.NewReader(body))&#xA;            req.Header.Set(&amp;quot;Expect&amp;quot;, &amp;quot;100-continue&amp;quot;)&#xA;            resp, err := tr.RoundTrip(req)&#xA;            if err != nil {&#xA;                t.Errorf(&amp;quot;RoundTrip: unexpected error %v&amp;quot;, err)&#xA;            } else {&#xA;                resp.Body.Close()&#xA;            }&#xA;        }()&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;We read the request headers sent by the client.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;        req, err := http.ReadRequest(bufio.NewReader(srvConn))&#xA;        if err != nil {&#xA;            t.Fatalf(&amp;quot;ReadRequest: %v&amp;quot;, err)&#xA;        }&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Now we come to the heart of the test.&#xA;We want to assert that the client will not send the request body yet.&lt;/p&gt;&#xA;&lt;p&gt;We start a new goroutine copying the body sent to the server into a &lt;code&gt;strings.Builder&lt;/code&gt;,&#xA;wait for all goroutines in the bubble to block, and verify that we haven&amp;rsquo;t read anything&#xA;from the body yet.&lt;/p&gt;&#xA;&lt;p&gt;If we forget the &lt;code&gt;synctest.Wait&lt;/code&gt; call, the race detector will correctly complain&#xA;about a data race, but with the &lt;code&gt;Wait&lt;/code&gt; this is safe.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;        var gotBody strings.Builder&#xA;        go io.Copy(&amp;amp;gotBody, req.Body)&#xA;        synctest.Wait()&#xA;        if got := gotBody.String(); got != &amp;quot;&amp;quot; {&#xA;            t.Fatalf(&amp;quot;before sending 100 Continue, unexpectedly read body: %q&amp;quot;, got)&#xA;        }&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;We write a &amp;ldquo;100 Continue&amp;rdquo; response to the client and verify that it now sends the&#xA;request body.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;        srvConn.Write([]byte(&amp;quot;HTTP/1.1 100 Continue\r\n\r\n&amp;quot;))&#xA;        synctest.Wait()&#xA;        if got := gotBody.String(); got != body {&#xA;            t.Fatalf(&amp;quot;after sending 100 Continue, read body %q, want %q&amp;quot;, got, body)&#xA;        }&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;And finally, we finish up by sending the &amp;ldquo;200 OK&amp;rdquo; response to conclude the request.&lt;/p&gt;&#xA;&lt;p&gt;We have started several goroutines during this test.&#xA;The &lt;code&gt;synctest.Run&lt;/code&gt; call will wait for all of them to exit before returning.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;        srvConn.Write([]byte(&amp;quot;HTTP/1.1 200 OK\r\n\r\n&amp;quot;))&#xA;    })&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;This test can be easily extended to test other behaviors,&#xA;such as verifying that the request body is not sent if the server does not ask for it,&#xA;or that it is sent if the server does not respond within a timeout.&lt;/p&gt;&#xA;&lt;h2 id=&#34;status-of-the-experiment&#34;&gt;Status of the experiment&lt;/h2&gt;&#xA;&lt;p&gt;We are introducing &lt;a href=&#34;/pkg/testing/synctest&#34;&gt;&lt;code&gt;testing/synctest&lt;/code&gt;&lt;/a&gt;&#xA;in Go 1.24 as an &lt;em&gt;experimental&lt;/em&gt; package.&#xA;Depending on feedback and experience&#xA;we may release it with or without amendments,&#xA;continue the experiment,&#xA;or remove it in a future version of Go.&lt;/p&gt;&#xA;&lt;p&gt;The package is not visible by default.&#xA;To use it, compile your code with &lt;code&gt;GOEXPERIMENT=synctest&lt;/code&gt; set in your environment.&lt;/p&gt;&#xA;&lt;p&gt;We want to hear your feedback!&#xA;If you try out &lt;code&gt;testing/synctest&lt;/code&gt;,&#xA;please report your experiences, positive or negative,&#xA;on &lt;a href=&#34;/issue/67434&#34;&gt;go.dev/issue/67434&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;Article prevnext&#34;&gt;&#xA;    &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;        &lt;p&gt;&#xA;        &#xA;          &#xA;            &lt;b&gt;Next article: &lt;/b&gt;&lt;a href=&#34;/blog/swisstable&#34;&gt;Faster Go maps with Swiss Tables&lt;/a&gt;&lt;br&gt;&#xA;          &#xA;        &#xA;        &#xA;          &#xA;            &lt;b&gt;Previous article: &lt;/b&gt;&lt;a href=&#34;/blog/wasmexport&#34;&gt;Extensible Wasm Applications with Go&lt;/a&gt;&lt;br&gt;&#xA;          &#xA;        &#xA;        &lt;b&gt;&lt;a href=&#34;/blog/all&#34;&gt;Blog Index&lt;/a&gt;&lt;/b&gt;&#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;    &lt;/div&gt;&#xA;    &#xA;&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;script src=&#34;/js/play.js&#34;&gt;&lt;/script&gt;&#xA;&#xA;</content></entry><entry><title>Extensible Wasm Applications with Go</title><id>tag:blog.golang.org,2013:blog.golang.org/wasmexport</id><link rel="alternate" href="https://go.dev/blog/wasmexport"></link><published>2025-02-13T00:00:00+00:00</published><updated>2025-02-13T00:00:00+00:00</updated><author><name>Cherry Mui</name></author><summary type="html">Go 1.24 enhances WebAssembly capabilities with function export and reactor mode</summary><content type="html">&#xA;&lt;div id=&#34;blog&#34;&gt;&lt;div id=&#34;content&#34;&gt;&#xA;  &lt;div id=&#34;content&#34;&gt;&#xA;&#xA;    &lt;div class=&#34;Article&#34; data-slug=&#34;/blog/wasmexport&#34;&gt;&#xA;    &#xA;    &lt;h1 class=&#34;small&#34;&gt;&lt;a href=&#34;/blog/&#34;&gt;The Go Blog&lt;/a&gt;&lt;/h1&gt;&#xA;    &#xA;&#xA;    &lt;h1&gt;Extensible Wasm Applications with Go&lt;/h1&gt;&#xA;      &#xA;      &lt;p class=&#34;author&#34;&gt;&#xA;      Cherry Mui&lt;br&gt;&#xA;      13 February 2025&#xA;      &lt;/p&gt;&#xA;      &#xA;      &lt;p&gt;Go 1.24 enhances its WebAssembly (Wasm) capabilities with the&#xA;addition of the &lt;code&gt;go:wasmexport&lt;/code&gt; directive and the ability to build a reactor&#xA;for WebAssembly System Interface (WASI).&#xA;These features enable Go developers to export Go functions to Wasm,&#xA;facilitating better integration with Wasm hosts and expanding the possibilities&#xA;for Go-based Wasm applications.&lt;/p&gt;&#xA;&lt;h2 id=&#34;webassembly-and-the-webassembly-system-interface&#34;&gt;WebAssembly and the WebAssembly System Interface&lt;/h2&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://webassembly.org/&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;WebAssembly (Wasm)&lt;/a&gt; is a binary instruction format&#xA;that was initially created for web browsers, providing the execution of&#xA;high-performance, low-level code at speeds approaching native performance.&#xA;Since then, Wasm&amp;rsquo;s utility has expanded, and it is now used in various&#xA;environments beyond the browser.&#xA;Notably, cloud providers offer services that directly execute Wasm&#xA;executables, taking advantage of the&#xA;&lt;a href=&#34;https://wasi.dev/&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;WebAssembly System Interface (WASI)&lt;/a&gt; system call API.&#xA;WASI allows these executables to interact with system resources.&lt;/p&gt;&#xA;&lt;p&gt;Go first added support for compiling to Wasm in the 1.11 release, through the&#xA;&lt;code&gt;js/wasm&lt;/code&gt; port.&#xA;Go 1.21 added a new port targeting the WASI preview 1 syscall API through the&#xA;new &lt;code&gt;GOOS=wasip1&lt;/code&gt; port.&lt;/p&gt;&#xA;&lt;h2 id=&#34;exporting-go-functions-to-wasm-with-gowasmexport&#34;&gt;Exporting Go Functions to Wasm with &lt;code&gt;go:wasmexport&lt;/code&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Go 1.24 introduces a new compiler directive, &lt;code&gt;go:wasmexport&lt;/code&gt;, which allows&#xA;developers to export Go functions to be called from outside of the&#xA;Wasm module, typically from a host application that runs the Wasm runtime.&#xA;This directive instructs the compiler to make the annotated function available&#xA;as a Wasm &lt;a href=&#34;https://webassembly.github.io/spec/core/valid/modules.html?highlight=export#exports&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;export&lt;/a&gt;&#xA;in the resulting Wasm binary.&lt;/p&gt;&#xA;&lt;p&gt;To use the &lt;code&gt;go:wasmexport&lt;/code&gt; directive, simply add it to a function definition:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;//go:wasmexport add&#xA;func add(a, b int32) int32 { return a + b }&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;With this, the Wasm module will have an exported function named &lt;code&gt;add&lt;/code&gt; that&#xA;can be called from the host.&lt;/p&gt;&#xA;&lt;p&gt;This is analogous to the &lt;a href=&#34;/cmd/cgo#hdr-C_references_to_Go&#34;&gt;cgo &lt;code&gt;export&lt;/code&gt; directive&lt;/a&gt;,&#xA;which makes the function available to be called from C,&#xA;though &lt;code&gt;go:wasmexport&lt;/code&gt; uses a different, simpler mechanism.&lt;/p&gt;&#xA;&lt;h2 id=&#34;building-a-wasi-reactor&#34;&gt;Building a WASI Reactor&lt;/h2&gt;&#xA;&lt;p&gt;A WASI reactor is a WebAssembly module that operates continuously, and&#xA;can be called upon multiple times to react on events or requests.&#xA;Unlike a &amp;ldquo;command&amp;rdquo; module, which terminates after its main function finishes,&#xA;a reactor instance remains live after initialization, and its exports remain&#xA;accessible.&lt;/p&gt;&#xA;&lt;p&gt;With Go 1.24, one can build a WASI reactor with the &lt;code&gt;-buildmode=c-shared&lt;/code&gt; build&#xA;flag.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;$ GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o reactor.wasm&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The build flag signals to the linker not to generate the &lt;code&gt;_start&lt;/code&gt; function&#xA;(the entry point for a command module), and instead generate an&#xA;&lt;code&gt;_initialize&lt;/code&gt; function, which performs runtime and package initialization,&#xA;along with any exported functions and their dependencies.&#xA;The &lt;code&gt;_initialize&lt;/code&gt; function must be called before any other exported functions.&#xA;The &lt;code&gt;main&lt;/code&gt; function will not be automatically invoked.&lt;/p&gt;&#xA;&lt;p&gt;To use a WASI reactor, the host application first initializes it by calling&#xA;&lt;code&gt;_initialize&lt;/code&gt;, then simply invoke the exported functions.&#xA;Here is an example using &lt;a href=&#34;https://wazero.io/&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;Wazero&lt;/a&gt;, a Go-based Wasm runtime&#xA;implementation:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;// Create a Wasm runtime, set up WASI.&#xA;r := wazero.NewRuntime(ctx)&#xA;defer r.Close(ctx)&#xA;wasi_snapshot_preview1.MustInstantiate(ctx, r)&#xA;&#xA;// Configure the module to initialize the reactor.&#xA;config := wazero.NewModuleConfig().WithStartFunctions(&amp;quot;_initialize&amp;quot;)&#xA;&#xA;// Instantiate the module.&#xA;wasmModule, _ := r.InstantiateWithConfig(ctx, wasmFile, config)&#xA;&#xA;// Call the exported function.&#xA;fn := wasmModule.ExportedFunction(&amp;quot;add&amp;quot;)&#xA;var a, b int32 = 1, 2&#xA;res, _ := fn.Call(ctx, api.EncodeI32(a), api.EncodeI32(b))&#xA;c := api.DecodeI32(res[0])&#xA;fmt.Printf(&amp;quot;add(%d, %d) = %d\n&amp;quot;, a, b, c)&#xA;&#xA;// The instance is still alive. We can call the function again.&#xA;res, _ = fn.Call(ctx, api.EncodeI32(b), api.EncodeI32(c))&#xA;fmt.Printf(&amp;quot;add(%d, %d) = %d\n&amp;quot;, b, c, api.DecodeI32(res[0]))&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The &lt;code&gt;go:wasmexport&lt;/code&gt; directive and the reactor build mode allow applications to&#xA;be extended by calling into Go-based Wasm code.&#xA;This is particularly valuable for applications that have adopted Wasm as a&#xA;plugin or extension mechanism with well-defined interfaces.&#xA;By exporting Go functions, applications can leverage the Go Wasm modules to&#xA;provide functionality without needing to recompile the entire application.&#xA;Furthermore, building as a reactor ensures that the exported functions can be&#xA;called multiple times without requiring reinitialization, making it suitable&#xA;for long-running applications or services.&lt;/p&gt;&#xA;&lt;h2 id=&#34;supporting-rich-types-between-the-host-and-the-client&#34;&gt;Supporting rich types between the host and the client&lt;/h2&gt;&#xA;&lt;p&gt;Go 1.24 also relaxes the constraints on types that can be used as input and&#xA;result parameters with &lt;code&gt;go:wasmimport&lt;/code&gt; functions.&#xA;For example, one can pass a bool, a string, a pointer to an &lt;code&gt;int32&lt;/code&gt;, or a&#xA;pointer to a struct which embeds &lt;code&gt;structs.HostLayout&lt;/code&gt; and contains supported&#xA;field types&#xA;(see the &lt;a href=&#34;/cmd/compile#hdr-WebAssembly_Directives&#34;&gt;documentation&lt;/a&gt; for detail).&#xA;This allows Go Wasm applications to be written in a more natural and ergonomic&#xA;way, and removes some unnecessary type conversions.&lt;/p&gt;&#xA;&lt;h2 id=&#34;limitations&#34;&gt;Limitations&lt;/h2&gt;&#xA;&lt;p&gt;While Go 1.24 has made significant enhancements to its Wasm capabilities,&#xA;there are still some notable limitations.&lt;/p&gt;&#xA;&lt;p&gt;Wasm is a single-threaded architecture with no parallelism.&#xA;A &lt;code&gt;go:wasmexport&lt;/code&gt; function can spawn new goroutines.&#xA;But if a function creates a background goroutine, it will not continue&#xA;executing when the &lt;code&gt;go:wasmexport&lt;/code&gt; function returns, until calling back into&#xA;the Go-based Wasm module.&lt;/p&gt;&#xA;&lt;p&gt;While some type restrictions have been relaxed in Go 1.24, there are still&#xA;limitations on the types that can be used with &lt;code&gt;go:wasmimport&lt;/code&gt; and&#xA;&lt;code&gt;go:wasmexport&lt;/code&gt; functions.&#xA;Due to the unfortunate mismatch between the 64-bit architecture of the client&#xA;and the 32-bit architecture of the host, it is not possible to pass pointers in&#xA;memory.&#xA;For example, a &lt;code&gt;go:wasmimport&lt;/code&gt; function cannot take a pointer to a struct that&#xA;contains a pointer-typed field.&lt;/p&gt;&#xA;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;&#xA;&lt;p&gt;The addition of the ability to build a WASI reactor and export Go functions to&#xA;Wasm in Go 1.24 represent a significant step forward for Go&amp;rsquo;s WebAssembly&#xA;capabilities.&#xA;These features empower developers to create more versatile and powerful Go-based&#xA;Wasm applications, opening up new possibilities for Go in the Wasm ecosystem.&lt;/p&gt;&#xA;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;Article prevnext&#34;&gt;&#xA;    &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;        &lt;p&gt;&#xA;        &#xA;          &#xA;            &lt;b&gt;Next article: &lt;/b&gt;&lt;a href=&#34;/blog/synctest&#34;&gt;Testing concurrent code with testing/synctest&lt;/a&gt;&lt;br&gt;&#xA;          &#xA;        &#xA;        &#xA;          &#xA;            &lt;b&gt;Previous article: &lt;/b&gt;&lt;a href=&#34;/blog/go1.24&#34;&gt;Go 1.24 is released!&lt;/a&gt;&lt;br&gt;&#xA;          &#xA;        &#xA;        &lt;b&gt;&lt;a href=&#34;/blog/all&#34;&gt;Blog Index&lt;/a&gt;&lt;/b&gt;&#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;    &lt;/div&gt;&#xA;    &#xA;&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;script src=&#34;/js/play.js&#34;&gt;&lt;/script&gt;&#xA;&#xA;</content></entry><entry><title>Go 1.24 is released!</title><id>tag:blog.golang.org,2013:blog.golang.org/go1.24</id><link rel="alternate" href="https://go.dev/blog/go1.24"></link><published>2025-02-11T00:00:00+00:00</published><updated>2025-02-11T00:00:00+00:00</updated><author><name>Junyang Shao, on behalf of the Go team</name></author><summary type="html">Go 1.24 brings generic type aliases, map performance improvements, FIPS 140 compliance and more.</summary><content type="html">&#xA;&lt;div id=&#34;blog&#34;&gt;&lt;div id=&#34;content&#34;&gt;&#xA;  &lt;div id=&#34;content&#34;&gt;&#xA;&#xA;    &lt;div class=&#34;Article&#34; data-slug=&#34;/blog/go1.24&#34;&gt;&#xA;    &#xA;    &lt;h1 class=&#34;small&#34;&gt;&lt;a href=&#34;/blog/&#34;&gt;The Go Blog&lt;/a&gt;&lt;/h1&gt;&#xA;    &#xA;&#xA;    &lt;h1&gt;Go 1.24 is released!&lt;/h1&gt;&#xA;      &#xA;      &lt;p class=&#34;author&#34;&gt;&#xA;      Junyang Shao, on behalf of the Go team&lt;br&gt;&#xA;      11 February 2025&#xA;      &lt;/p&gt;&#xA;      &#xA;      &lt;p&gt;Today the Go team is excited to release Go 1.24,&#xA;which you can get by visiting the &lt;a href=&#34;/dl/&#34;&gt;download page&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Go 1.24 comes with many improvements over Go 1.23. Here are some of the notable&#xA;changes; for the full list, refer to the &lt;a href=&#34;/doc/go1.24&#34;&gt;release notes&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;language-changes&#34;&gt;Language changes&lt;/h2&gt;&#xA;&lt;p&gt;Go 1.24 now fully supports &lt;a href=&#34;/issue/46477&#34;&gt;generic type aliases&lt;/a&gt;: a type alias&#xA;may be parameterized like a defined type.&#xA;See the &lt;a href=&#34;/ref/spec#Alias_declarations&#34;&gt;language spec&lt;/a&gt; for details.&lt;/p&gt;&#xA;&lt;h2 id=&#34;performance-improvements&#34;&gt;Performance improvements&lt;/h2&gt;&#xA;&lt;p&gt;Several performance improvements in the runtime have decreased CPU overhead&#xA;by 2–3% on average across a suite of representative benchmarks. These&#xA;improvements include a new builtin &lt;code&gt;map&lt;/code&gt; implementation based on&#xA;&lt;a href=&#34;https://abseil.io/about/design/swisstables&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;Swiss Tables&lt;/a&gt;, more efficient&#xA;memory allocation of small objects, and a new runtime-internal mutex&#xA;implementation.&lt;/p&gt;&#xA;&lt;h2 id=&#34;tool-improvements&#34;&gt;Tool improvements&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The &lt;code&gt;go&lt;/code&gt; command now provides a mechanism for tracking tool dependencies for a&#xA;module. Use &lt;code&gt;go get -tool&lt;/code&gt; to add a &lt;code&gt;tool&lt;/code&gt; directive to the current module. Use&#xA;&lt;code&gt;go tool [tool name]&lt;/code&gt; to run the tools declared with the &lt;code&gt;tool&lt;/code&gt; directive.&#xA;Read more on the &lt;a href=&#34;/doc/go1.24#go-command&#34;&gt;go command&lt;/a&gt; in the release notes.&lt;/li&gt;&#xA;&lt;li&gt;The new &lt;code&gt;test&lt;/code&gt; analyzer in &lt;code&gt;go vet&lt;/code&gt; subcommand reports common mistakes in&#xA;declarations of tests, fuzzers, benchmarks, and examples in test packages.&#xA;Read more on &lt;a href=&#34;/doc/go1.24#vet&#34;&gt;vet&lt;/a&gt; in the release notes.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;standard-library-additions&#34;&gt;Standard library additions&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;The standard library now includes &lt;a href=&#34;/doc/security/fips140&#34;&gt;a new set of mechanisms to facilitate&#xA;FIPS 140-3 compliance&lt;/a&gt;. Applications require no source code&#xA;changes to use the new mechanisms for approved algorithms. Read more&#xA;on &lt;a href=&#34;/doc/go1.24#fips140&#34;&gt;FIPS 140-3 compliance&lt;/a&gt; in the release notes.&#xA;Apart from FIPS 140, several packages that were previously in the&#xA;&lt;a href=&#34;/pkg/golang.org/x/crypto&#34;&gt;x/crypto&lt;/a&gt; module are now available in the&#xA;&lt;a href=&#34;/doc/go1.24#crypto-mlkem&#34;&gt;standard library&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Benchmarks may now use the faster and less error-prone&#xA;&lt;a href=&#34;/pkg/testing#B.Loop&#34;&gt;&lt;code&gt;testing.B.Loop&lt;/code&gt;&lt;/a&gt; method to perform benchmark iterations&#xA;like &lt;code&gt;for b.Loop() { ... }&lt;/code&gt; in place of the typical loop structures involving&#xA;&lt;code&gt;b.N&lt;/code&gt; like &lt;code&gt;for range b.N&lt;/code&gt;. Read more on&#xA;&lt;a href=&#34;/doc/go1.24#new-benchmark-function&#34;&gt;the new benchmark function&lt;/a&gt; in the&#xA;release notes.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;The new &lt;a href=&#34;/pkg/os#Root&#34;&gt;&lt;code&gt;os.Root&lt;/code&gt;&lt;/a&gt; type provides the ability to perform&#xA;filesystem operations isolated under a specific directory. Read more on&#xA;&lt;a href=&#34;/doc/go1.24#directory-limited-filesystem-access&#34;&gt;filesystem access&lt;/a&gt; in the&#xA;release notes.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;The runtime provides a new finalization mechanism,&#xA;&lt;a href=&#34;/pkg/runtime#AddCleanup&#34;&gt;&lt;code&gt;runtime.AddCleanup&lt;/code&gt;&lt;/a&gt;, that is more flexible,&#xA;more efficient, and less error-prone than&#xA;&lt;a href=&#34;/pkg/runtime#SetFinalizer&#34;&gt;&lt;code&gt;runtime.SetFinalizer&lt;/code&gt;&lt;/a&gt;. Read more on&#xA;&lt;a href=&#34;/doc/go1.24#improved-finalizers&#34;&gt;cleanups&lt;/a&gt; in the release notes.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;improved-webassembly-support&#34;&gt;Improved WebAssembly support&lt;/h2&gt;&#xA;&lt;p&gt;Go 1.24 adds a new &lt;code&gt;go:wasmexport&lt;/code&gt; directive for Go programs to export&#xA;functions to the WebAssembly host, and supports building a Go program as a WASI&#xA;&lt;a href=&#34;https://github.com/WebAssembly/WASI/blob/63a46f61052a21bfab75a76558485cf097c0dbba/legacy/application-abi.md#current-unstable-abi&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;reactor/library&lt;/a&gt;.&#xA;Read more on &lt;a href=&#34;/doc/go1.24#wasm&#34;&gt;WebAssembly&lt;/a&gt; in the release notes.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;Please read the &lt;a href=&#34;/doc/go1.24&#34;&gt;Go 1.24 release notes&lt;/a&gt; for the complete and&#xA;detailed information. Don&amp;rsquo;t forget to watch for follow-up blog posts that&#xA;will go in more depth on some of the topics mentioned here!&lt;/p&gt;&#xA;&lt;p&gt;Thank you to everyone who contributed to this release by writing code and&#xA;documentation, reporting bugs, sharing feedback, and testing the release&#xA;candidates. Your efforts helped to ensure that Go 1.24 is as stable as possible.&#xA;As always, if you notice any problems, please &lt;a href=&#34;/issue/new&#34;&gt;file an issue&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Enjoy Go 1.24!&lt;/p&gt;&#xA;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;Article prevnext&#34;&gt;&#xA;    &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;        &lt;p&gt;&#xA;        &#xA;          &#xA;            &lt;b&gt;Next article: &lt;/b&gt;&lt;a href=&#34;/blog/wasmexport&#34;&gt;Extensible Wasm Applications with Go&lt;/a&gt;&lt;br&gt;&#xA;          &#xA;        &#xA;        &#xA;          &#xA;            &lt;b&gt;Previous article: &lt;/b&gt;&lt;a href=&#34;/blog/survey2024-h2-results&#34;&gt;Go Developer Survey 2024 H2 Results&lt;/a&gt;&lt;br&gt;&#xA;          &#xA;        &#xA;        &lt;b&gt;&lt;a href=&#34;/blog/all&#34;&gt;Blog Index&lt;/a&gt;&lt;/b&gt;&#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;    &lt;/div&gt;&#xA;    &#xA;&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;script src=&#34;/js/play.js&#34;&gt;&lt;/script&gt;&#xA;&#xA;</content></entry><entry><title>Go Developer Survey 2024 H2 Results</title><id>tag:blog.golang.org,2013:blog.golang.org/survey2024-h2-results</id><link rel="alternate" href="https://go.dev/blog/survey2024-h2-results"></link><published>2024-12-20T00:00:00+00:00</published><updated>2024-12-20T00:00:00+00:00</updated><author><name>Alice Merrick</name></author><summary type="html">What we learned from our 2024 H2 developer survey</summary><content type="html">&#xA;&lt;div id=&#34;blog&#34;&gt;&lt;div id=&#34;content&#34;&gt;&#xA;  &lt;div id=&#34;content&#34;&gt;&#xA;&#xA;    &lt;div class=&#34;Article&#34; data-slug=&#34;/blog/survey2024-h2-results&#34;&gt;&#xA;    &#xA;    &lt;h1 class=&#34;small&#34;&gt;&lt;a href=&#34;/blog/&#34;&gt;The Go Blog&lt;/a&gt;&lt;/h1&gt;&#xA;    &#xA;&#xA;    &lt;h1&gt;Go Developer Survey 2024 H2 Results&lt;/h1&gt;&#xA;      &#xA;      &lt;p class=&#34;author&#34;&gt;&#xA;      Alice Merrick&lt;br&gt;&#xA;      20 December 2024&#xA;      &lt;/p&gt;&#xA;      &#xA;      &lt;style type=&#34;text/css&#34; scoped&gt;&#xA;  .chart {&#xA;    margin-left: 1.5rem;&#xA;    margin-right: 1.5rem;&#xA;    width: 800px;&#xA;  }&#xA;  blockquote p {&#xA;    color: var(--color-text-subtle) !important;&#xA;  }&#xA;&#xA;  .quote_source {&#xA;    font-style: italic;&#xA;  }&#xA;&#xA;  @media (prefers-color-scheme: dark) {&#xA;    .chart {&#xA;      border-radius: 8px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;background&#34;&gt;Background&lt;/h2&gt;&#xA;&lt;p&gt;Go was designed with a focus on developer experience, and we deeply value the&#xA;feedback we receive through proposals, issues, and community interactions.&#xA;However, these channels often represent the voices of our most experienced or&#xA;engaged users, a small subset of the broader Go community. To ensure we&amp;rsquo;re&#xA;serving developers of all skill levels, including those who may not have strong&#xA;opinions on language design, we conduct this survey once or twice a year to&#xA;gather systematic feedback and quantitative evidence. This inclusive approach&#xA;allows us to hear from a wider range of Go developers, providing valuable&#xA;insights into how Go is used across different contexts and experience levels.&#xA;Your participation is critical in informing our decisions about language changes&#xA;and resource allocation, ultimately shaping the future of Go. Thank you to&#xA;everyone who contributed, and we strongly encourage your continued participation&#xA;in future surveys. Your experience matters to us.&lt;/p&gt;&#xA;&lt;p&gt;This post shares the results of our most recent Go Developer Survey, conducted&#xA;from September 9–23, 2024. We recruited participants from the Go blog and&#xA;through randomized prompts in the &lt;a href=&#34;https://code.visualstudio.com/&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;VS Code&lt;/a&gt; Go&#xA;plug-in and &lt;a href=&#34;https://www.jetbrains.com/go/&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;GoLand IDE&lt;/a&gt;, allowing us to recruit&#xA;a more representative sample of Go developers. We received a total of 4,156&#xA;responses. A huge thank you to all those who contributed to making this&#xA;possible.&lt;/p&gt;&#xA;&lt;p&gt;Along with capturing sentiments and challenges around using Go and Go tooling,&#xA;our primary focus areas for this survey were on uncovering sources of toil,&#xA;challenges to performing best practices, and how developers are using AI&#xA;assistance.&lt;/p&gt;&#xA;&lt;h2 id=&#34;highlights&#34;&gt;Highlights&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Developer sentiment towards Go remains extremely positive&lt;/strong&gt;, with 93% of&#xA;survey respondents saying they felt satisfied while working with Go during the&#xA;prior year.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Ease of deployment and an easy to use API/SDK&lt;/strong&gt; were respondents’ favorite&#xA;things about using Go on the top three cloud providers. First-class Go support&#xA;is critical to keeping up with developer expectations.&lt;/li&gt;&#xA;&lt;li&gt;70% of respondents were using AI assistants when developing with Go. The most&#xA;common uses were &lt;strong&gt;LLM-based code completion&lt;/strong&gt;, writing tests, generating Go&#xA;code from natural language descriptions, and brainstorming. There was a&#xA;significant discrepancy between what respondents said they wanted to use AI&#xA;for last year, and what they currently use AI for.&lt;/li&gt;&#xA;&lt;li&gt;The biggest challenge for teams using Go was &lt;strong&gt;maintaining consistent coding&#xA;standards&lt;/strong&gt; across their codebase. This was often due to team members having&#xA;different levels of Go experience and coming from different programming&#xA;backgrounds, leading to inconsistencies in coding style and adoption of&#xA;non-idiomatic patterns.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;contents&#34;&gt;Contents&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#sentiment&#34;&gt;Overall satisfaction&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#devenv&#34;&gt;Development environments and tools&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#cloud&#34;&gt;Go in the clouds&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#ai-assistance&#34;&gt;AI assistance&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#team-challenges&#34;&gt;Challenges for teams using Go&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#simd&#34;&gt;Single Instruction, Multiple Data (SIMD)&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#demographics&#34;&gt;Demographics&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#firmographics&#34;&gt;Firmographics&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#methodology&#34;&gt;Methodology&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#how-to-read-these-results&#34;&gt;How to read these results&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#closing&#34;&gt;Closing&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;sentiment&#34;&gt;Overall satisfaction&lt;/h3&gt;&#xA;&lt;p&gt;Overall satisfaction remains high in the survey with 93% of respondents saying&#xA;they were somewhat or very satisfied with Go during the last year. Although the&#xA;exact percentages fluctuate slightly from cycle to cycle, we do not see any&#xA;statistically significant differences from our &lt;a href=&#34;/blog/survey2023-h2-results&#34;&gt;2023&#xA;H2&lt;/a&gt; or &lt;a href=&#34;/blog/survey2024-h1-results&#34;&gt;2024 H1&#xA;&lt;/a&gt;Surveys when the satisfaction rate&#xA;was 90% and 93%, respectively.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/csat.svg&#34; alt=&#34;Chart of developer satisfaction with Go&#34;&#xA;class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;The open comments we received on the survey continue to highlight what&#xA;developers like most about using Go, for example, its simplicity, the Go&#xA;toolchain, and its promise of backwards compatibility:&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;“I am a programming languages enjoyer (C-like) and I always come back to Go for&#xA;its simplicity, fast compilation and robust toolchain. Keep it up!”&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;“Thank you for creating Go! It is my favorite language, because it is pretty&#xA;minimal, the development cycle has rapid build-test cycles, and when using a&#xA;random open source project written in Go, there is a good chance that it will&#xA;work, even 10 years after. I love the 1.0 compatibility guarantee.”&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;devenv&#34;&gt;Development environments and tools&lt;/h3&gt;&#xA;&lt;h4 id=&#34;developer-os&#34;&gt;Developer OS&lt;/h4&gt;&#xA;&lt;p&gt;Consistent with previous years, most survey respondents develop with Go on Linux&#xA;(61%) and macOS (59%) systems. Historically, the proportion of Linux and macOS&#xA;users has been very close, and we didn’t see any significant changes from the&#xA;last survey. The randomly sampled groups from JetBrains and VS Code were more&#xA;likely (33% and 36%, respectively) to develop on Windows than the self-selected&#xA;group (16%).&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/os_dev.svg&#34; alt=&#34;Chart of operating systems respondents&#xA;use when developing Go software&#34; class=&#34;chart&#34; /&gt; &lt;img&#xA;src=&#34;survey2024h2/os_dev_src.svg&#34; alt=&#34;Chart of operating systems respondents&#xA;use when developing Go software, split by difference sample sources&#34;&#xA;class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;h4 id=&#34;deployment-environments&#34;&gt;Deployment environments&lt;/h4&gt;&#xA;&lt;p&gt;Given the prevalence of Go for cloud development and containerized workloads,&#xA;it’s no surprise that Go developers primarily deploy to Linux environments&#xA;(96%).&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/os_deploy.svg&#34; alt=&#34;Chart of operating systems&#xA;respondents deploy to when developing Go software&#34; class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;We included several questions to understand what architectures respondents are&#xA;deploying to when deploying to Linux, Windows or WebAssembly. The x86-64 /&#xA;AMD64 architecture was by far the most popular choice for those deploying to&#xA;both Linux (92%) and Windows (97%). ARM64 was second at 49% for Linux and 21%&#xA;for Windows.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/arch_linux.svg&#34; alt=&#34;Linux architecture usage&#34;&#xA;class=&#34;chart&#34; /&gt; &lt;img src=&#34;survey2024h2/arch_win.svg&#34; alt=&#34;Windows architecture&#xA;usage&#34; class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;Not many respondents deployed to Web Assembly (only about 4% of overall&#xA;respondents), but 73% that do said they deploy to JS and 48% to WASI Preview 1.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/arch_wa.svg&#34; alt=&#34;Web assembly architecture usage&#34;&#xA;class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;h4 id=&#34;editor-awareness-and-preferences&#34;&gt;Editor awareness and preferences&lt;/h4&gt;&#xA;&lt;p&gt;We introduced a new question on this survey to assess awareness and usage of&#xA;popular editors for Go. When interpreting these results, keep in mind that 34%&#xA;of respondents came to the survey from VS Code and 9% of respondents came from&#xA;GoLand, so it is more likely for them to use those editors regularly.&lt;/p&gt;&#xA;&lt;p&gt;VS Code was the most widely used editor, with 66% of respondents using it&#xA;regularly, and GoLand was the second most used at 35%. Almost all respondents&#xA;had heard of both VS Code and GoLand, but respondents were much more likely to&#xA;have at least tried VS Code. Interestingly, 33% of respondents said they&#xA;regularly use 2 or more editors. They may use different editors for different&#xA;tasks or environments, such as using Emacs or Vim via SSH, where IDEs aren’t&#xA;available.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/editor_aware.svg&#34; alt=&#34;Level of familiarity with each&#xA;editor&#34; class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;We also asked a question about editor preference, the same as we have asked on&#xA;previous surveys. Because our randomly sampled populations were recruited from&#xA;within VS Code or GoLand, they are strongly biased towards preferring those&#xA;editors. To avoid skewing the results, we show the data for the most preferred&#xA;editor here from the self-selected group only. 38% preferred VS Code and 35%&#xA;preferred GoLand. This is a notable difference from the last survey in H1, when&#xA;43% preferred VS Code and 33% preferred GoLand. A possible explanation could be&#xA;in how respondents were recruited this year. This year the VS Code notification&#xA;began inviting developers to take the survey before the Go blog entry was&#xA;posted, so a larger proportion of respondents came from the VS Code prompt this&#xA;year who might have otherwise come from the blog post. Because we only show the&#xA;self-selected respondents in this chart, data from respondents from the VS Code&#xA;prompt data are not represented here. Another contributing factor could be the&#xA;slight increase in those who prefer &amp;ldquo;Other&amp;rdquo; (4%). The write-in responses suggest&#xA;there is increased interest in editors like &lt;a href=&#34;https://zed.dev/&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;Zed&lt;/a&gt;, which made&#xA;up 43% of the write-in responses.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/editor.svg&#34; alt=&#34;Code editors respondents most prefer to&#xA;use with Go&#34; class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;h4 id=&#34;code-analysis-tools&#34;&gt;Code analysis tools&lt;/h4&gt;&#xA;&lt;p&gt;The most popular code analysis tool was &lt;code&gt;gopls&lt;/code&gt;, which was knowingly used by 65%&#xA;of respondents. Because &lt;code&gt;gopls&lt;/code&gt; is used under-the-hood by default in VS Code,&#xA;this is likely an undercount. Following closely behind, &lt;code&gt;golangci-lint&lt;/code&gt; was used&#xA;by 57% of respondents, and &lt;code&gt;staticcheck&lt;/code&gt; was used by 34% of respondents. A much&#xA;smaller proportion used custom or other tools, which suggests that most&#xA;respondents prefer common established tools over custom solutions. Only 10% of&#xA;respondents indicated they don&amp;rsquo;t use any code analysis tools.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/code_analysis.svg&#34; alt=&#34;Code analysis tools respondents&#xA;use with Go&#34; class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;h4 id=&#34;cloud&#34;&gt;Go in the Clouds&lt;/h4&gt;&#xA;&lt;p&gt;Go is a popular language for modern cloud-based development, so we typically&#xA;include survey questions to help us understand which cloud platforms and&#xA;services Go developers are using. In this cycle, we sought to learn about&#xA;preferences and experiences of Go developers across cloud providers, with a&#xA;particular focus on the largest cloud providers: Amazon Web Services (AWS),&#xA;Microsoft Azure, and Google Cloud. We also included an additional option for&#xA;“Bare Metal Servers” for those who deploy to servers without virtualization.&lt;/p&gt;&#xA;&lt;p&gt;Similar to previous years, almost half of respondents (50%) deploy Go programs&#xA;to Amazon Web Services. AWS is followed by self-owned or company-owned servers&#xA;(37%), and Google Cloud (30%). Respondents who work at large organizations are a&#xA;little more likely to deploy to self-owned or company-owned servers (48%) than&#xA;those who work at small-to-medium organizations (34%). They‘re also a little&#xA;more likely to deploy to Microsoft Azure (25%) than small-to-medium&#xA;organizations (12%).&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/cloud_platform.svg&#34; alt=&#34;Cloud providers where&#xA;respondents deploy Go software&#34; class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;The most commonly used cloud services were AWS Elastic Kubernetes Service (41%),&#xA;AWS EC2 (39%), and Google Cloud GKE (29%). Although we’ve seen Kubernetes usage&#xA;increase over time, this is the first time we’ve seen EKS become more widely&#xA;used than EC2. Overall, Kubernetes offerings were the most popular services for&#xA;AWS, Google Cloud, and Azure, followed by VMs and then Serverless offerings.&#xA;Go&amp;rsquo;s strengths in containerization and microservices development naturally align&#xA;with the rising popularity of Kubernetes, as it provides an efficient and&#xA;scalable platform for deploying and managing these types of applications.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/cloud_service.svg&#34; alt=&#34;Cloud platforms where respondents&#xA;deploy Go software&#34; class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;We asked a followup question to respondents who deployed Go code to the top&#xA;three cloud providers, Amazon Web Services, Google Cloud, and Microsoft Azure on&#xA;what they like most about deploying Go code to each cloud. The most popular&#xA;response across different providers was actually Go&amp;rsquo;s performance and language&#xA;features rather than something about the cloud provider.&lt;/p&gt;&#xA;&lt;p&gt;Other common reasons were:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Familiarity with the given cloud provider compared to other clouds&lt;/li&gt;&#xA;&lt;li&gt;Ease of deployment of Go applications on the given cloud provider&lt;/li&gt;&#xA;&lt;li&gt;The cloud provider&amp;rsquo;s API/SDK for Go is easy to use&lt;/li&gt;&#xA;&lt;li&gt;The API/SDK is well documented&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Other than familiarity, the top favorite things highlight the importance of&#xA;having first class support for Go to keep up with developer expectations.&lt;/p&gt;&#xA;&lt;p&gt;It was also fairly common for respondents to say they don&amp;rsquo;t have a favorite&#xA;thing about their cloud provider. From a previous version of the survey that&#xA;involved write-in responses, this often meant that they did not interact&#xA;directly with the Cloud. In particular, respondents who use Microsoft Azure were&#xA;much more likely to say that “Nothing” was their favorite thing (51%) compared&#xA;to AWS (27%) or Google Cloud (30%).&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/cloud_fave_all.svg&#34; alt= &#34;What respondents liked most&#xA;about each of the top 3 Clouds&#34; class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;ai-assistance&#34;&gt;AI assistance&lt;/h3&gt;&#xA;&lt;p&gt;The Go team hypothesizes that AI assistance has the potential to alleviate&#xA;developers from tedious and repetitive tasks, allowing them to focus on more&#xA;creative and fulfilling aspects of their work. To gain insights into areas where&#xA;AI assistance could be most beneficial, we included a section in our survey to&#xA;identify common developer toil.&lt;/p&gt;&#xA;&lt;p&gt;The majority of respondents (70%) are using AI assistants when developing with&#xA;Go. The most common usage of AI assistants was in LLM-based code completion&#xA;(35%). Other common responses were writing tests (29%), generating Go code from&#xA;a natural language description (27%), and brainstorming ideas (25%). There was&#xA;also a sizable minority (30%) of respondents who had not used any LLM for&#xA;assistance in the last month.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/ai_assist_tasks.svg&#34; alt= &#34;Most common tasks used with AI&#xA;assistance&#34; class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;Some of these results stood out when compared to findings from our 2023 H2&#xA;survey, where we asked respondents for the top 5 use cases they would like to&#xA;see AI/ML support Go developers. Although a couple new responses were introduced&#xA;in the current survey, we can still do a rough comparison between what&#xA;respondents said they wanted AI support for, and what their actual usage was&#xA;like. In that previous survey, writing tests was the most desired use case&#xA;(49%). In our latest 2024 H2 survey, about 29% of respondents had used AI&#xA;assistants for this in the last month. This suggests that current offerings are&#xA;not meeting developer needs for writing tests. Similarly, in 2023, 47%&#xA;respondents said they would like suggestions for best practices while coding,&#xA;while only 14% a year later said they are using AI assistance for this use case.&#xA;46% said they wanted help catching common mistakes while coding, and only 13%&#xA;said they were using AI assistance for this. This could indicate that current AI&#xA;assistants are not well-equipped for these kinds of tasks, or they&amp;rsquo;re not well&#xA;integrated into developer workflows or tooling.&lt;/p&gt;&#xA;&lt;p&gt;It was also surprising to see such high usage of AI for generating Go code from&#xA;natural language and brainstorming, since the previous survey didn&amp;rsquo;t indicate&#xA;these as highly desired use cases. There could be a number of explanations for&#xA;these differences. While previous respondents might not have explicitly &lt;em&gt;wanted&lt;/em&gt;&#xA;AI for code generation or brainstorming initially, they might be gravitating&#xA;towards these uses because they align with the current strengths of generative&#xA;AI—natural language processing and creative text generation. We should also keep&#xA;in mind that people&lt;a href=&#34;https://www.nngroup.com/articles/first-rule-of-usability-dont-listen-to-users/&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt; are not necessarily the best predictors of their own&#xA;behavior&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/ai_assist_tasks_yoy.svg&#34; alt= &#34;Tasks used AI assistance&#xA;in 2024 compared to those wanted in 2023&#34; class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;We also saw some notable differences in how different groups responded to this&#xA;question. Respondents at small to medium sized organizations were a little more&#xA;likely to have used LLMs (75%) compared to those at large organizations (66%).&#xA;There could be a number of reasons why, for example, larger organizations may&#xA;have stricter security and compliance requirements and concerns about the&#xA;security of LLM coding assistants, the potential for data leakage, or compliance&#xA;with industry-specific regulations. They also may have already invested in&#xA;other developer tools and practices that already provide similar benefits to&#xA;developer productivity.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/ai_assist_tasks_org.svg&#34; alt= &#34;Most common tasks used&#xA;with AI assistance by org size&#34; class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;Go developers with less than 2 years of experience were more likely to use AI&#xA;assistants (75%) compared to Go developers with 5+ years of experience (67%).&#xA;Less experienced Go developers were also more likely to use them for more tasks,&#xA;on average 3.50. Although all experience levels tended to use LLM-based code&#xA;completion, less experienced Go developers were more likely to use Go for more&#xA;tasks related to learning and debugging, such as explaining what a piece of Go&#xA;code does, resolving compiler errors and debugging failures in their Go code.&#xA;This suggests that AI assistants are currently providing the greatest utility to&#xA;those who are less familiar with Go. We don&amp;rsquo;t know how AI assistants affect&#xA;learning or getting started on a new Go project, something we want to&#xA;investigate in the future. However, all experience levels had similar rates of&#xA;satisfaction with their AI assistants, around 73%, so new Go developers are not&#xA;more satisfied with AI assistants, despite using them more often.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/ai_assist_tasks_exp.svg&#34; alt= &#34;Most common tasks used&#xA;with AI assistance by experience with Go&#34; class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;To respondents who reported using AI assistance for at least one task related to&#xA;writing Go code, we asked some follow up questions to learn more about their AI&#xA;assistant usage. The most commonly used AI assistants were ChatGPT (68%) and&#xA;GitHub Copilot (50%). When asked which AI assistant they used &lt;em&gt;most&lt;/em&gt; in the last&#xA;month, ChatGPT and Copilot were about even at 36% each, so although more&#xA;respondents used ChatGPT, it wasn’t necessarily their primary assistant.&#xA;Participants were similarly satisfied with both tools (73% satisfied with&#xA;ChatGPT, vs. 78% with GitHub CoPilot). The highest satisfaction rate for any AI&#xA;assistant was Anthropic Claude, at 87%.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/ai_assistants_withother.svg&#34; alt= &#34;Most common AI&#xA;assistants used&#34; class=&#34;chart&#34; /&gt; &lt;img src=&#34;survey2024h2/ai_primary.svg&#34; alt=&#xA;&#34;Most common primary AI assistants used&#34; class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;team-challenges&#34;&gt;Challenges for teams using Go&lt;/h3&gt;&#xA;&lt;p&gt;In this section of the survey, we wanted to understand which best practices or&#xA;tools should be better integrated into developer workflows. Our approach was to&#xA;identify common problems for teams using Go. We then asked respondents which&#xA;challenges would bring them the most benefit if they were “magically” solved for&#xA;them. (This was so that respondents would not focus on particular solutions.)&#xA;Common problems that would provide the most benefit if they were solved would be&#xA;considered candidates for improvement.&lt;/p&gt;&#xA;&lt;p&gt;The most commonly reported challenges for teams were maintaining consistent coding&#xA;standards across our Go codebase (58%), identifying performance issues in a&#xA;running Go program (58%) and identifying resource usage inefficiencies in a&#xA;running Go program (57%).&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/dev_challenges.svg&#34; alt= &#34;Most common challenges for&#xA;teams&#34; class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;21% of respondents said their team would benefit most from maintaining&#xA;consistent coding standards across their Go codebase. This was the most common&#xA;response, making it a good candidate to address. In a follow-up question, we got&#xA;more details as to why specifically this was so challenging.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/dev_challenges_most_benefit.svg&#34; alt= &#34;Most benefit to&#xA;solve&#34; class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;According to the write-in responses, many teams face challenges maintaining&#xA;consistent coding standards because their members have varying levels of&#xA;experience with Go and come from different programming backgrounds. This led to&#xA;inconsistencies in coding style and the adoption of non-idiomatic patterns.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;“There&amp;rsquo;s lots of polyglot engineers where I work. So the Go written is not&#xA;consistent. I do consider myself a Gopher and spend time trying to convince my&#xA;teammates what is idiomatic in Go”—Go developer with 2–4 years of experience.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;“Most of the team members are learning Go from scratch. Coming from the&#xA;dynamically typed languages, it takes them a while to get used to the new&#xA;language. They seem to struggle maintaining the code consistency following the&#xA;Go guidelines.”—Go developer with 2–4 years of experience.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;This echoes some feedback we’ve heard before about teammates who write &amp;ldquo;Gava&amp;rdquo; or&#xA;&amp;ldquo;Guby&amp;rdquo; due to their previous language experiences. Although static analysis was&#xA;a class of tool we had in mind to address this issue when we came up with this&#xA;question, we are currently exploring different ways we might address this.&lt;/p&gt;&#xA;&lt;h3 id=&#34;simd&#34;&gt;Single Instruction, Multiple Data (SIMD)&lt;/h3&gt;&#xA;&lt;p&gt;SIMD, or Single Instruction, Multiple Data, is a type of parallel processing&#xA;that allows a single CPU instruction to operate on multiple data points&#xA;simultaneously. This facilitates tasks involving large datasets and repetitive&#xA;operations, and is often used to optimize performance in fields like game&#xA;development, data processing, and scientific computing. In this section of the&#xA;survey we wanted to assess respondents&amp;rsquo; needs for native SIMD support in Go.&lt;/p&gt;&#xA;&lt;p&gt;The majority of respondents (89%) say that work on projects where performance&#xA;optimizations are crucial at least some of the time. 40% said they work on such&#xA;projects at least half the time. This held true across different organization&#xA;sizes and experience levels, suggesting that performance is an important issue&#xA;for most developers.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/perf_freq.svg&#34; alt= &#34;How often respondents work on&#xA;performance critical software&#34; class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;About half of respondents (54%), said they are at least a little familiar with&#xA;the concept of SIMD. Working with SIMD often requires a deeper understanding of&#xA;computer architecture and low-level programming concepts, so unsurprisingly we&#xA;find that less experienced developers were less likely to be familiar with SIMD.&#xA;Respondents with more experience and who worked on performance-crucial&#xA;applications at least half the time were the most likely to be familiar with&#xA;SIMD.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/simd_fam.svg&#34; alt= &#34;Familiarity with SIMD&#34; class=&#34;chart&#34;&#xA;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;For those who were at least slightly familiar with SIMD, we asked some follow&#xA;-up questions to understand how respondents were affected by the absence of&#xA;native SIMD support in Go. Over a third, about 37%, said they had been impacted.&#xA;17% of respondents said they had been limited in the performance they could&#xA;achieve in their projects, 15% said they had to use another language instead of&#xA;Go to achieve their goals, and 13% said they had to use non-Go libraries when&#xA;they would have preferred to use Go libraries. Interestingly, respondents who&#xA;were negatively impacted by the absence of native SIMD support were a little&#xA;more likely to use Go for data processing and AI/ML. This suggests that adding&#xA;SIMD support could make Go a better option for these domains.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/simd_impact.svg&#34; alt= &#34;Impacts of lack of native Go support&#xA;for SIMD&#34; class=&#34;chart&#34; /&gt; &lt;img src=&#34;survey2024h2/what_simd_impact.svg&#34; alt=&#xA;&#34;What impacted respondents build with Go&#34; class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;demographics&#34;&gt;Demographics&lt;/h3&gt;&#xA;&lt;p&gt;We ask similar demographic questions during each cycle of this survey so we can&#xA;understand how comparable the year-over-year results may be. For example, if we&#xA;saw changes in who responded to the survey in terms of Go experience, it’d be&#xA;very likely that other differences in results from prior cycles were due to this&#xA;demographic shift. We also use these questions to provide comparisons between&#xA;groups, such as satisfaction according to how long respondents have been using&#xA;Go.&lt;/p&gt;&#xA;&lt;p&gt;We didn’t see any significant changes in levels of experience among respondents&#xA;during this cycle.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/go_exp.svg&#34; alt= &#34;Experience levels of respondents&#34;&#xA;class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;There are differences in the demographics of respondents according to whether&#xA;they came from The Go Blog, the VS Code extension, or GoLand. The population&#xA;who responded to survey notifications in VS Code skews toward less experience&#xA;with Go; we suspect this a reflection of VS Code’s popularity with new Go&#xA;developers, who may not be ready to invest in an IDE license while they’re still&#xA;learning. With respect to years of Go experience, the respondents randomly&#xA;selected from GoLand are more similar to our self-selected population who found&#xA;the survey through the Go Blog. Seeing consistencies between samples allows us&#xA;to more confidently generalize findings to the rest of the community.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/go_exp_src.svg&#34; alt= &#34;Experience with Go by survey&#xA;source&#34; class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;In addition to years of experience with Go, we also measured years of&#xA;professional coding experience. Our audience tends to be a pretty experienced&#xA;bunch, with 26% of respondents having 16 or more years of professional coding&#xA;experience.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/dev_exp.svg&#34; alt= &#34;Overall levels of professional&#xA;developer experience&#34; class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;The self-selected group was even more experienced than the randomly selected&#xA;groups, with 29% having 16 or more years of professional experience. This&#xA;suggests that our self-selected group is generally more experienced than our&#xA;randomly selected groups and can help explain some of the differences we see in&#xA;this group.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/dev_exp_src.svg&#34; alt= &#34;Levels of professional developer&#xA;experience by survey source&#34; class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;We found that 81% of respondents were fully employed. When we look at our&#xA;individual samples, we see a small but significant difference within our&#xA;respondents from VS Code, who are slightly more likely to be students. This&#xA;makes sense given that VS Code is free.&lt;/p&gt;&#xA;&lt;img src=&#34;survey2024h2/employment.svg&#34; alt= &#34;Employment status&#34; class=&#34;chart&#34; /&gt;&#xA;&lt;img src=&#34;survey2024h2/employment_src.svg&#34; alt= &#34;Employment status by survey&#xA;source&#34; class=&#34;chart&#34; /&gt;&#xA;&lt;p&gt;Similar to previous years, the most common use cases for Go were API/RPC&#xA;services (75%) and command line tools (62%). More experienced Go developers&#xA;reported building a wider variety of applications in Go. This trend was&#xA;consistent across every category of app or service. We did not find any notable&#xA;differences in what respondents are building based on their organization size.&#xA;Respondents from the random VS Code and GoLand samples did not display&#xA;significant differences either.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/what.svg&#34; alt= &#34;What respondents build with Go&#34;&#xA;class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;firmographics&#34;&gt;Firmographics&lt;/h3&gt;&#xA;&lt;p&gt;We heard from respondents at a variety of different organizations. About 29%&#xA;worked at large organizations with 1,001 or more employees, 25% were from&#xA;midsize organizations of 101–1,000 employees, and 43% worked at smaller&#xA;organizations with fewer than 100 employees. As in previous years, the most&#xA;common industry people work in was technology (43%) while the second most common&#xA;was financial services (13%).&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/org_size.svg&#34; alt= &#34;Organization sizes where respondents&#xA;work&#34; class=&#34;chart&#34; /&gt; &lt;img src=&#34;survey2024h2/industry.svg&#34; alt= &#34;Industries&#xA;respondents work in&#34; class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;As in previous surveys, the most common location for survey respondents was the&#xA;United States (19%). This year we saw a significant shift in the proportion of&#xA;respondents coming from Ukraine, from 1% to 6%, making it the third most common&#xA;location for survey respondents. Because we only saw this difference among our&#xA;self-selected respondents, and not in the randomly sampled groups, this suggests&#xA;that something affected who discovered the survey, rather than a widespread&#xA;increase in Go adoption across all developers in Ukraine. Perhaps there was&#xA;increased visibility or awareness of the survey or the Go Blog among developers&#xA;in Ukraine.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/location.svg&#34; alt= &#34;Where respondents are located&#34;&#xA;class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;methodology&#34;&gt;Methodology&lt;/h2&gt;&#xA;&lt;p&gt;We announce the survey primarily through the Go Blog, where it is often picked&#xA;up on various social channels like Reddit, or Hacker News. We also recruit&#xA;respondents by using the VS Code Go plugin to randomly select users to whom we show&#xA;a prompt asking if they’d like to participate in the survey. With some help from&#xA;our friends at JetBrains, we also have an additional random sample from&#xA;prompting a random subset of GoLand users to take the survey. This gave us two&#xA;sources we used to compare the self-selected respondents from our traditional&#xA;channels and help identify potential effects of &lt;a href=&#34;https://en.wikipedia.org/wiki/Self-selection_bias&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;self-selection&#xA;bias&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;57% of survey respondents “self-selected” to take the survey, meaning they found&#xA;it on the Go blog or other social Go channels. People who don’t follow these&#xA;channels are less likely to learn about the survey from them, and in some cases,&#xA;they respond differently than people who do closely follow them. For example,&#xA;they might be new to the Go community and not yet aware of the Go blog. About&#xA;43% of respondents were randomly sampled, meaning they responded to the survey&#xA;after seeing a prompt in VS Code (25%) or GoLand (11%). Over the period of&#xA;September 9–23, 2024, there was roughly a 10% chance users of the VS Code plugin&#xA;would have seen this prompt. The prompt in GoLand was similarly active between&#xA;September 9–20. By examining how the randomly sampled groups differ from the&#xA;self-selected responses, as well as from each other, we’re able to more&#xA;confidently generalize findings to the larger community of Go developers.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;survey2024h2/source.svg&#34; alt=&#34;Chart of different sources of survey&#xA;respondents&#34; class=&#34;chart&#34; /&gt;&lt;/p&gt;&#xA;&lt;h4 id=&#34;how-to-read-these-results&#34;&gt;How to read these results&lt;/h4&gt;&#xA;&lt;p&gt;Throughout this report we use charts of survey responses to provide supporting&#xA;evidence for our findings. All of these charts use a similar format. The title&#xA;is the exact question that survey respondents saw. Unless otherwise noted,&#xA;questions were multiple choice and participants could only select a single&#xA;response choice; each chart’s subtitle will tell the reader if the question&#xA;allowed multiple response choices or was an open-ended text box instead of a&#xA;multiple choice question. For charts of open-ended text responses, a Go team&#xA;member read and manually categorized all of the responses. Many open-ended&#xA;questions elicited a wide variety of responses; to keep the chart sizes&#xA;reasonable, we condensed them to a maximum of the top 10-12 themes, with&#xA;additional themes all grouped under “Other”. The percentage labels shown in&#xA;charts are rounded to the nearest integer (e.g., 1.4% and 0.8% will both be&#xA;displayed as 1%), but the length of each bar and row ordering are based on the&#xA;unrounded values.&lt;/p&gt;&#xA;&lt;p&gt;To help readers understand the weight of evidence underlying each finding, we&#xA;included error bars showing the 95% &lt;a href=&#34;https://en.wikipedia.org/wiki/Confidence_interval&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;confidence&#xA;interval&lt;/a&gt; for responses;&#xA;narrower bars indicate increased confidence. Sometimes two or more responses&#xA;have overlapping error bars, which means the relative order of those responses&#xA;is not statistically meaningful (i.e., the responses are effectively tied). The&#xA;lower right of each chart shows the number of people whose responses are&#xA;included in the chart, in the form “n = [number of respondents]”. In cases where&#xA;we found interesting differences in responses between groups, (e.g., years of&#xA;experience, organization size, or sample source) we showed a color-coded&#xA;breakdown of the differences.&lt;/p&gt;&#xA;&lt;h3 id=&#34;closing&#34;&gt;Closing&lt;/h3&gt;&#xA;&lt;p&gt;Thanks for reviewing our semi-annual Go Developer Survey! And many thanks to&#xA;everyone who shared their thoughts on Go and everyone who contributed to making&#xA;this survey happen. It means the world to us and truly helps us improve Go.&lt;/p&gt;&#xA;&lt;p&gt;&amp;mdash; Alice (on behalf of the Go team at Google)&lt;/p&gt;&#xA;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;Article prevnext&#34;&gt;&#xA;    &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;        &lt;p&gt;&#xA;        &#xA;          &#xA;            &lt;b&gt;Next article: &lt;/b&gt;&lt;a href=&#34;/blog/go1.24&#34;&gt;Go 1.24 is released!&lt;/a&gt;&lt;br&gt;&#xA;          &#xA;        &#xA;        &#xA;          &#xA;            &lt;b&gt;Previous article: &lt;/b&gt;&lt;a href=&#34;/blog/protobuf-opaque&#34;&gt;Go Protobuf: The new Opaque API&lt;/a&gt;&lt;br&gt;&#xA;          &#xA;        &#xA;        &lt;b&gt;&lt;a href=&#34;/blog/all&#34;&gt;Blog Index&lt;/a&gt;&lt;/b&gt;&#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;    &lt;/div&gt;&#xA;    &#xA;&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;script src=&#34;/js/play.js&#34;&gt;&lt;/script&gt;&#xA;&#xA;</content></entry><entry><title>Go Protobuf: The new Opaque API</title><id>tag:blog.golang.org,2013:blog.golang.org/protobuf-opaque</id><link rel="alternate" href="https://go.dev/blog/protobuf-opaque"></link><published>2024-12-16T00:00:00+00:00</published><updated>2024-12-16T00:00:00+00:00</updated><author><name>Michael Stapelberg</name></author><summary type="html">We are adding a new generated code API to Go Protobuf.</summary><content type="html">&#xA;&lt;div id=&#34;blog&#34;&gt;&lt;div id=&#34;content&#34;&gt;&#xA;  &lt;div id=&#34;content&#34;&gt;&#xA;&#xA;    &lt;div class=&#34;Article&#34; data-slug=&#34;/blog/protobuf-opaque&#34;&gt;&#xA;    &#xA;    &lt;h1 class=&#34;small&#34;&gt;&lt;a href=&#34;/blog/&#34;&gt;The Go Blog&lt;/a&gt;&lt;/h1&gt;&#xA;    &#xA;&#xA;    &lt;h1&gt;Go Protobuf: The new Opaque API&lt;/h1&gt;&#xA;      &#xA;      &lt;p class=&#34;author&#34;&gt;&#xA;      Michael Stapelberg&lt;br&gt;&#xA;      16 December 2024&#xA;      &lt;/p&gt;&#xA;      &#xA;      &lt;p&gt;[&lt;a href=&#34;https://en.wikipedia.org/wiki/Protocol_Buffers&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;Protocol Buffers (Protobuf)&lt;/a&gt;&#xA;is Google&amp;rsquo;s language-neutral data interchange format. See&#xA;&lt;a href=&#34;https://protobuf.dev/&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;protobuf.dev&lt;/a&gt;.]&lt;/p&gt;&#xA;&lt;p&gt;Back in March 2020, we released the &lt;code&gt;google.golang.org/protobuf&lt;/code&gt; module, &lt;a href=&#34;/blog/protobuf-apiv2&#34;&gt;a&#xA;major overhaul of the Go Protobuf API&lt;/a&gt;. This&#xA;package introduced first-class &lt;a href=&#34;https://pkg.go.dev/google.golang.org/protobuf/reflect/protoreflect&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;support for&#xA;reflection&lt;/a&gt;,&#xA;a &lt;a href=&#34;https://pkg.go.dev/google.golang.org/protobuf/types/dynamicpb&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;&lt;code&gt;dynamicpb&lt;/code&gt;&lt;/a&gt;&#xA;implementation and the&#xA;&lt;a href=&#34;https://pkg.go.dev/google.golang.org/protobuf/testing/protocmp&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;&lt;code&gt;protocmp&lt;/code&gt;&lt;/a&gt;&#xA;package for easier testing.&lt;/p&gt;&#xA;&lt;p&gt;That release introduced a new protobuf module with a new API. Today, we are&#xA;releasing an additional API for generated code, meaning the Go code in the&#xA;&lt;code&gt;.pb.go&lt;/code&gt; files created by the protocol compiler (&lt;code&gt;protoc&lt;/code&gt;). This blog post&#xA;explains our motivation for creating a new API and shows you how to use it in&#xA;your projects.&lt;/p&gt;&#xA;&lt;p&gt;To be clear: We are not removing anything. We will continue to support the&#xA;existing API for generated code, just like we still support the older protobuf&#xA;module (by wrapping the &lt;code&gt;google.golang.org/protobuf&lt;/code&gt; implementation). Go is&#xA;&lt;a href=&#34;/blog/compat&#34;&gt;committed to backwards compatibility&lt;/a&gt; and this&#xA;applies to Go Protobuf, too!&lt;/p&gt;&#xA;&lt;h2 id=&#34;background&#34;&gt;Background: the (existing) Open Struct API&lt;/h2&gt;&#xA;&lt;p&gt;We now call the existing API the Open Struct API, because generated struct types&#xA;are open to direct access. In the next section, we will see how it differs from&#xA;the new Opaque API.&lt;/p&gt;&#xA;&lt;p&gt;To work with protocol buffers, you first create a &lt;code&gt;.proto&lt;/code&gt; definition file like&#xA;this one:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;edition = &amp;quot;2023&amp;quot;;  // successor to proto2 and proto3&#xA;&#xA;package log;&#xA;&#xA;message LogEntry {&#xA;  string backend_server = 1;&#xA;  uint32 request_size = 2;&#xA;  string ip_address = 3;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Then, you &lt;a href=&#34;https://protobuf.dev/getting-started/gotutorial/&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;run the protocol compiler&#xA;(&lt;code&gt;protoc&lt;/code&gt;)&lt;/a&gt; to generate code&#xA;like the following (in a &lt;code&gt;.pb.go&lt;/code&gt; file):&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;package logpb&#xA;&#xA;type LogEntry struct {&#xA;  BackendServer *string&#xA;  RequestSize   *uint32&#xA;  IPAddress     *string&#xA;  // …internal fields elided…&#xA;}&#xA;&#xA;func (l *LogEntry) GetBackendServer() string { … }&#xA;func (l *LogEntry) GetRequestSize() uint32   { … }&#xA;func (l *LogEntry) GetIPAddress() string     { … }&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Now you can import the generated &lt;code&gt;logpb&lt;/code&gt; package from your Go code and call&#xA;functions like&#xA;&lt;a href=&#34;https://pkg.go.dev/google.golang.org/protobuf/proto#Marshal&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;&lt;code&gt;proto.Marshal&lt;/code&gt;&lt;/a&gt;&#xA;to encode &lt;code&gt;logpb.LogEntry&lt;/code&gt; messages into protobuf wire format.&lt;/p&gt;&#xA;&lt;p&gt;You can find more details in the &lt;a href=&#34;https://protobuf.dev/reference/go/go-generated/&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;Generated Code API&#xA;documentation&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;presence&#34;&gt;(Existing) Open Struct API: Field Presence&lt;/h3&gt;&#xA;&lt;p&gt;An important aspect of this generated code is how &lt;em&gt;field presence&lt;/em&gt; (whether a&#xA;field is set or not) is modeled. For instance, the above example models presence&#xA;using pointers, so you could set the &lt;code&gt;BackendServer&lt;/code&gt; field to:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;code&gt;proto.String(&amp;quot;zrh01.prod&amp;quot;)&lt;/code&gt;: the field is set and contains &amp;ldquo;zrh01.prod&amp;rdquo;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;proto.String(&amp;quot;&amp;quot;)&lt;/code&gt;: the field is set (non-&lt;code&gt;nil&lt;/code&gt; pointer) but contains an&#xA;empty value&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;nil&lt;/code&gt; pointer: the field is not set&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;If you are used to generated code not having pointers, you are probably using&#xA;&lt;code&gt;.proto&lt;/code&gt; files that start with &lt;code&gt;syntax = &amp;quot;proto3&amp;quot;&lt;/code&gt;. The field presence behavior&#xA;changed over the years:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;syntax = &amp;quot;proto2&amp;quot;&lt;/code&gt; uses &lt;em&gt;explicit presence&lt;/em&gt; by default&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;syntax = &amp;quot;proto3&amp;quot;&lt;/code&gt; used &lt;em&gt;implicit presence&lt;/em&gt; by default (where cases 2 and 3&#xA;cannot be distinguished and are both represented by an empty string), but was&#xA;later extended to allow &lt;a href=&#34;https://protobuf.dev/programming-guides/proto3/#field-labels&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;opting into explicit presence with the &lt;code&gt;optional&lt;/code&gt;&#xA;keyword&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;edition = &amp;quot;2023&amp;quot;&lt;/code&gt;, the &lt;a href=&#34;https://protobuf.dev/editions/overview/&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;successor to both proto2 and&#xA;proto3&lt;/a&gt;, uses &lt;a href=&#34;https://protobuf.dev/programming-guides/field_presence/&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;&lt;em&gt;explicit&#xA;presence&lt;/em&gt;&lt;/a&gt; by default&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;opaqueapi&#34;&gt;The new Opaque API&lt;/h2&gt;&#xA;&lt;p&gt;We created the new &lt;em&gt;Opaque API&lt;/em&gt; to uncouple the &lt;a href=&#34;https://protobuf.dev/reference/go/go-generated/&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;Generated Code&#xA;API&lt;/a&gt; from the underlying&#xA;in-memory representation. The (existing) Open Struct API has no such separation:&#xA;it allows programs direct access to the protobuf message memory. For example,&#xA;one could use the &lt;code&gt;flag&lt;/code&gt; package to parse command-line flag values into protobuf&#xA;message fields:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;var req logpb.LogEntry&#xA;flag.StringVar(&amp;amp;req.BackendServer, &amp;quot;backend&amp;quot;, os.Getenv(&amp;quot;HOST&amp;quot;), &amp;quot;…&amp;quot;)&#xA;flag.Parse() // fills the BackendServer field from -backend flag&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The problem with such a tight coupling is that we can never change how we lay&#xA;out protobuf messages in memory. Lifting this restriction enables many&#xA;implementation improvements, which we&amp;rsquo;ll see below.&lt;/p&gt;&#xA;&lt;p&gt;What changes with the new Opaque API? Here is how the generated code from the&#xA;above example would change:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;package logpb&#xA;&#xA;type LogEntry struct {&#xA;  xxx_hidden_BackendServer *string // no longer exported&#xA;  xxx_hidden_RequestSize   uint32  // no longer exported&#xA;  xxx_hidden_IPAddress     *string // no longer exported&#xA;  // …internal fields elided…&#xA;}&#xA;&#xA;func (l *LogEntry) GetBackendServer() string { … }&#xA;func (l *LogEntry) HasBackendServer() bool   { … }&#xA;func (l *LogEntry) SetBackendServer(string)  { … }&#xA;func (l *LogEntry) ClearBackendServer()      { … }&#xA;// …&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;With the Opaque API, the struct fields are hidden and can no longer be&#xA;directly accessed. Instead, the new accessor methods allow for getting, setting,&#xA;or clearing a field.&lt;/p&gt;&#xA;&lt;h3 id=&#34;lessmemory&#34;&gt;Opaque structs use less memory&lt;/h3&gt;&#xA;&lt;p&gt;One change we made to the memory layout is to model field presence for&#xA;elementary fields more efficiently:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The (existing) Open Struct API uses pointers, which adds a 64-bit word to the&#xA;space cost of the field.&lt;/li&gt;&#xA;&lt;li&gt;The Opaque API uses &lt;a href=&#34;https://en.wikipedia.org/wiki/Bit_field&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;bit&#xA;fields&lt;/a&gt;, which require one bit per&#xA;field (ignoring padding overhead).&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Using fewer variables and pointers also lowers load on the allocator and on the&#xA;garbage collector.&lt;/p&gt;&#xA;&lt;p&gt;The performance improvement depends heavily on the shapes of your protocol&#xA;messages: The change only affects elementary fields like integers, bools, enums,&#xA;and floats, but not strings, repeated fields, or submessages (because it is&#xA;&lt;a href=&#34;https://protobuf.dev/reference/go/opaque-faq/#memorylayout&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;less&#xA;profitable&lt;/a&gt;&#xA;for those types).&lt;/p&gt;&#xA;&lt;p&gt;Our benchmark results show that messages with few elementary fields exhibit&#xA;performance that is as good as before, whereas messages with more elementary&#xA;fields are decoded with significantly fewer allocations:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;             │ Open Struct API │             Opaque API             │&#xA;             │    allocs/op    │  allocs/op   vs base               │&#xA;Prod#1          360.3k ± 0%       360.3k ± 0%  +0.00% (p=0.002 n=6)&#xA;Search#1       1413.7k ± 0%       762.3k ± 0%  -46.08% (p=0.002 n=6)&#xA;Search#2        314.8k ± 0%       132.4k ± 0%  -57.95% (p=0.002 n=6)&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Reducing allocations also makes decoding protobuf messages more efficient:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;             │ Open Struct API │             Opaque API            │&#xA;             │   user-sec/op   │ user-sec/op  vs base              │&#xA;Prod#1         55.55m ± 6%        55.28m ± 4%  ~ (p=0.180 n=6)&#xA;Search#1       324.3m ± 22%       292.0m ± 6%  -9.97% (p=0.015 n=6)&#xA;Search#2       67.53m ± 10%       45.04m ± 8%  -33.29% (p=0.002 n=6)&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;(All measurements done on an AMD Castle Peak Zen 2. Results on ARM and Intel&#xA;CPUs are similar.)&lt;/p&gt;&#xA;&lt;p&gt;Note: proto3 with implicit presence similarly does not use pointers, so you will&#xA;not see a performance improvement if you are coming from proto3. If you were&#xA;using implicit presence for performance reasons, forgoing the convenience of&#xA;being able to distinguish empty fields from unset ones, then the Opaque API now&#xA;makes it possible to use explicit presence without a performance penalty.&lt;/p&gt;&#xA;&lt;h3 id=&#34;lazydecoding&#34;&gt;Motivation: Lazy Decoding&lt;/h3&gt;&#xA;&lt;p&gt;Lazy decoding is a performance optimization where the contents of a submessage&#xA;are decoded when first accessed instead of during&#xA;&lt;a href=&#34;https://pkg.go.dev/google.golang.org/protobuf/proto#Unmarshal&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;&lt;code&gt;proto.Unmarshal&lt;/code&gt;&lt;/a&gt;. Lazy&#xA;decoding can improve performance by avoiding unnecessarily decoding fields which&#xA;are never accessed.&lt;/p&gt;&#xA;&lt;p&gt;Lazy decoding can&amp;rsquo;t be supported safely by the (existing) Open Struct API. While&#xA;the Open Struct API provides getters, leaving the (un-decoded) struct fields&#xA;exposed would be extremely error-prone. To ensure that the decoding logic runs&#xA;immediately before the field is first accessed, we must make the field private&#xA;and mediate all accesses to it through getter and setter functions.&lt;/p&gt;&#xA;&lt;p&gt;This approach made it possible to implement lazy decoding with the Opaque&#xA;API. Of course, not every workload will benefit from this optimization, but for&#xA;those that do benefit, the results can be spectacular: We have seen logs&#xA;analysis pipelines that discard messages based on a top-level message condition&#xA;(e.g. whether &lt;code&gt;backend_server&lt;/code&gt; is one of the machines running a new Linux kernel&#xA;version) and can skip decoding deeply nested subtrees of messages.&lt;/p&gt;&#xA;&lt;p&gt;As an example, here are the results of the micro-benchmark we included,&#xA;demonstrating how lazy decoding saves over 50% of the work and over 87% of&#xA;allocations!&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;                  │   nolazy    │                lazy                │&#xA;                  │   sec/op    │   sec/op     vs base               │&#xA;Unmarshal/lazy-24   6.742µ ± 0%   2.816µ ± 0%  -58.23% (p=0.002 n=6)&#xA;&#xA;                  │    nolazy    │                lazy                 │&#xA;                  │     B/op     │     B/op      vs base               │&#xA;Unmarshal/lazy-24   3.666Ki ± 0%   1.814Ki ± 0%  -50.51% (p=0.002 n=6)&#xA;&#xA;                  │   nolazy    │               lazy                │&#xA;                  │  allocs/op  │ allocs/op   vs base               │&#xA;Unmarshal/lazy-24   64.000 ± 0%   8.000 ± 0%  -87.50% (p=0.002 n=6)&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h3 id=&#34;pointercomparison&#34;&gt;Motivation: reduce pointer comparison mistakes&lt;/h3&gt;&#xA;&lt;p&gt;Modeling field presence with pointers invites pointer-related bugs.&lt;/p&gt;&#xA;&lt;p&gt;Consider an enum, declared within the &lt;code&gt;LogEntry&lt;/code&gt; message:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;message LogEntry {&#xA;  enum DeviceType {&#xA;    DESKTOP = 0;&#xA;    MOBILE = 1;&#xA;    VR = 2;&#xA;  };&#xA;  DeviceType device_type = 1;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;A simple mistake is to compare the &lt;code&gt;device_type&lt;/code&gt; enum field like so:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;if cv.DeviceType == logpb.LogEntry_DESKTOP.Enum() { // incorrect!&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Did you spot the bug? The condition compares the memory address instead of the&#xA;value. Because the &lt;code&gt;Enum()&lt;/code&gt; accessor allocates a new variable on each call, the&#xA;condition can never be true. The check should have read:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;if cv.GetDeviceType() == logpb.LogEntry_DESKTOP {&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The new Opaque API prevents this mistake: Because fields are hidden, all access&#xA;must go through the getter.&lt;/p&gt;&#xA;&lt;h3 id=&#34;accidentalsharing&#34;&gt;Motivation: reduce accidental sharing mistakes&lt;/h3&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s consider a slightly more involved pointer-related bug. Assume you are&#xA;trying to stabilize an RPC service that fails under high load. The following&#xA;part of the request middleware looks correct, but still the entire service goes&#xA;down whenever just one customer sends a high volume of requests:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;logEntry.IPAddress = req.IPAddress&#xA;logEntry.BackendServer = proto.String(hostname)&#xA;// The redactIP() function redacts IPAddress to 127.0.0.1,&#xA;// unexpectedly not just in logEntry *but also* in req!&#xA;go auditlog(redactIP(logEntry))&#xA;if quotaExceeded(req) {&#xA;    // BUG: All requests end up here, regardless of their source.&#xA;    return fmt.Errorf(&amp;quot;server overloaded&amp;quot;)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Did you spot the bug? The first line accidentally copied the pointer (thereby&#xA;sharing the pointed-to variable between the &lt;code&gt;logEntry&lt;/code&gt; and &lt;code&gt;req&lt;/code&gt; messages)&#xA;instead of its value. It should have read:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;logEntry.IPAddress = proto.String(req.GetIPAddress())&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The new Opaque API prevents this problem as the setter takes a value&#xA;(&lt;code&gt;string&lt;/code&gt;) instead of a pointer:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;logEntry.SetIPAddress(req.GetIPAddress())&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h3 id=&#34;reflection&#34;&gt;Motivation: Fix Sharp Edges: reflection&lt;/h3&gt;&#xA;&lt;p&gt;To write code that works not only with a specific message type&#xA;(e.g. &lt;code&gt;logpb.LogEntry&lt;/code&gt;), but with any message type, one needs some kind of&#xA;reflection. The previous example used a function to redact IP addresses. To work&#xA;with any type of message, it could have been defined as &lt;code&gt;func redactIP(proto.Message) proto.Message { … }&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Many years ago, your only option to implement a function like &lt;code&gt;redactIP&lt;/code&gt; was to&#xA;reach for &lt;a href=&#34;/blog/laws-of-reflection&#34;&gt;Go&amp;rsquo;s &lt;code&gt;reflect&lt;/code&gt; package&lt;/a&gt;,&#xA;which resulted in very tight coupling: you had only the generator output and had&#xA;to reverse-engineer what the input protobuf message definition might have looked&#xA;like. The &lt;a href=&#34;/blog/protobuf-apiv2&#34;&gt;&lt;code&gt;google.golang.org/protobuf&lt;/code&gt; module&#xA;release&lt;/a&gt; (from March 2020) introduced&#xA;&lt;a href=&#34;https://pkg.go.dev/google.golang.org/protobuf/reflect/protoreflect&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;Protobuf&#xA;reflection&lt;/a&gt;,&#xA;which should always be preferred: Go&amp;rsquo;s &lt;code&gt;reflect&lt;/code&gt; package traverses the data&#xA;structure&amp;rsquo;s representation, which should be an implementation detail. Protobuf&#xA;reflection traverses the logical tree of protocol messages without regard to its&#xA;representation.&lt;/p&gt;&#xA;&lt;p&gt;Unfortunately, merely &lt;em&gt;providing&lt;/em&gt; protobuf reflection is not sufficient and&#xA;still leaves some sharp edges exposed: In some cases, users might accidentally&#xA;use Go reflection instead of protobuf reflection.&lt;/p&gt;&#xA;&lt;p&gt;For example, encoding a protobuf message with the &lt;code&gt;encoding/json&lt;/code&gt; package (which&#xA;uses Go reflection) was technically possible, but the result is not &lt;a href=&#34;https://protobuf.dev/programming-guides/proto3/#json&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;canonical&#xA;Protobuf JSON&#xA;encoding&lt;/a&gt;. Use the&#xA;&lt;a href=&#34;https://pkg.go.dev/google.golang.org/protobuf/encoding/protojson&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;&lt;code&gt;protojson&lt;/code&gt;&lt;/a&gt;&#xA;package instead.&lt;/p&gt;&#xA;&lt;p&gt;The new Opaque API prevents this problem because the message struct fields are&#xA;hidden: accidental usage of Go reflection will see an empty message. This is&#xA;clear enough to steer developers towards protobuf reflection.&lt;/p&gt;&#xA;&lt;h3 id=&#34;idealmemory&#34;&gt;Motivation: Making the ideal memory layout possible&lt;/h3&gt;&#xA;&lt;p&gt;The benchmark results from the &lt;a href=&#34;#lessmemory&#34;&gt;More Efficient Memory&#xA;Representation&lt;/a&gt; section have already shown that protobuf&#xA;performance heavily depends on the specific usage: How are the messages defined?&#xA;Which fields are set?&lt;/p&gt;&#xA;&lt;p&gt;To keep Go Protobuf as fast as possible for &lt;em&gt;everyone&lt;/em&gt;, we cannot implement&#xA;optimizations that help only one program, but hurt the performance of other&#xA;programs.&lt;/p&gt;&#xA;&lt;p&gt;The Go compiler used to be in a similar situation, up until &lt;a href=&#34;/blog/go1.20&#34;&gt;Go 1.20 introduced&#xA;Profile-Guided Optimization (PGO)&lt;/a&gt;. By recording the&#xA;production behavior (through &lt;a href=&#34;/blog/pprof&#34;&gt;profiling&lt;/a&gt;) and feeding&#xA;that profile back to the compiler, we allow the compiler to make better&#xA;trade-offs &lt;em&gt;for a specific program or workload&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;We think using profiles to optimize for specific workloads is a promising&#xA;approach for further Go Protobuf optimizations. The Opaque API makes those&#xA;possible: Program code uses accessors and does not need to be updated when the&#xA;memory representation changes, so we could, for example, move rarely set fields&#xA;into an overflow struct.&lt;/p&gt;&#xA;&lt;h2 id=&#34;migration&#34;&gt;Migration&lt;/h2&gt;&#xA;&lt;p&gt;You can migrate on your own schedule, or even not at all—the (existing) Open&#xA;Struct API will not be removed. But, if you’re not on the new Opaque API, you&#xA;won’t benefit from its improved performance, or future optimizations that target&#xA;it.&lt;/p&gt;&#xA;&lt;p&gt;We recommend you select the Opaque API for new development. Protobuf Edition&#xA;2024 (see &lt;a href=&#34;https://protobuf.dev/editions/overview/&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;Protobuf Editions Overview&lt;/a&gt;&#xA;if you are not yet familiar) will make the Opaque API the default.&lt;/p&gt;&#xA;&lt;h3 id=&#34;hybridapi&#34;&gt;The Hybrid API&lt;/h3&gt;&#xA;&lt;p&gt;Aside from the Open Struct API and Opaque API, there is also the Hybrid API,&#xA;which keeps existing code working by keeping struct fields exported, but also&#xA;enabling migration to the Opaque API by adding the new accessor methods.&lt;/p&gt;&#xA;&lt;p&gt;With the Hybrid API, the protobuf compiler will generate code on two API levels:&#xA;the &lt;code&gt;.pb.go&lt;/code&gt; is on the Hybrid API, whereas the &lt;code&gt;_protoopaque.pb.go&lt;/code&gt; version is&#xA;on the Opaque API and can be selected by building with the &lt;code&gt;protoopaque&lt;/code&gt; build&#xA;tag.&lt;/p&gt;&#xA;&lt;h3 id=&#34;rewriting&#34;&gt;Rewriting Code to the Opaque API&lt;/h3&gt;&#xA;&lt;p&gt;See the &lt;a href=&#34;https://protobuf.dev/reference/go/opaque-migration/&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;migration&#xA;guide&lt;/a&gt;&#xA;for detailed instructions. The high-level steps are:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Enable the Hybrid API.&lt;/li&gt;&#xA;&lt;li&gt;Update existing code using the &lt;code&gt;open2opaque&lt;/code&gt; migration tool.&lt;/li&gt;&#xA;&lt;li&gt;Switch to the Opaque API.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;publishing&#34;&gt;Advice for published generated code: Use Hybrid API&lt;/h3&gt;&#xA;&lt;p&gt;Small usages of protobuf can live entirely within the same repository, but&#xA;usually, &lt;code&gt;.proto&lt;/code&gt; files are shared between different projects that are owned by&#xA;different teams. An obvious example is when different companies are involved: To&#xA;call Google APIs (with protobuf), use the &lt;a href=&#34;https://github.com/googleapis/google-cloud-go&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Client Libraries for&#xA;Go&lt;/a&gt; from your project. Switching&#xA;the Cloud Client Libraries to the Opaque API is not an option, as that would be&#xA;a breaking API change, but switching to the Hybrid API is safe.&lt;/p&gt;&#xA;&lt;p&gt;Our advice for such packages that publish generated code (&lt;code&gt;.pb.go&lt;/code&gt; files) is to&#xA;switch to the Hybrid API please! Publish both the &lt;code&gt;.pb.go&lt;/code&gt; and the&#xA;&lt;code&gt;_protoopaque.pb.go&lt;/code&gt; files, please. The &lt;code&gt;protoopaque&lt;/code&gt; version allows your&#xA;consumers to migrate on their own schedule.&lt;/p&gt;&#xA;&lt;h3 id=&#34;enablelazy&#34;&gt;Enabling Lazy Decoding&lt;/h3&gt;&#xA;&lt;p&gt;Lazy decoding is available (but not enabled) once you migrate to the Opaque API!&#xA;🎉&lt;/p&gt;&#xA;&lt;p&gt;To enable: in your &lt;code&gt;.proto&lt;/code&gt; file, annotate your message-typed fields with the&#xA;&lt;code&gt;[lazy = true]&lt;/code&gt; annotation.&lt;/p&gt;&#xA;&lt;p&gt;To opt out of lazy decoding (despite &lt;code&gt;.proto&lt;/code&gt; annotations), the &lt;a href=&#34;https://pkg.go.dev/google.golang.org/protobuf/runtime/protolazy&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;&lt;code&gt;protolazy&lt;/code&gt;&#xA;package&#xA;documentation&lt;/a&gt;&#xA;describes the available opt-outs, which affect either an individual Unmarshal&#xA;operation or the entire program.&lt;/p&gt;&#xA;&lt;h2 id=&#34;nextsteps&#34;&gt;Next Steps&lt;/h2&gt;&#xA;&lt;p&gt;By using the open2opaque tool in an automated fashion over the last few years,&#xA;we have converted the vast majority of Google’s &lt;code&gt;.proto&lt;/code&gt; files and Go code to&#xA;the Opaque API. We continuously improved the Opaque API implementation as we&#xA;moved more and more production workloads to it.&lt;/p&gt;&#xA;&lt;p&gt;Therefore, we expect you should not encounter problems when trying the Opaque&#xA;API. In case you do encounter any issues after all, please &lt;a href=&#34;https://github.com/golang/protobuf/issues/&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;let us know on the&#xA;Go Protobuf issue tracker&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Reference documentation for Go Protobuf can be found on &lt;a href=&#34;https://protobuf.dev/reference/go/&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;protobuf.dev → Go&#xA;Reference&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;Article prevnext&#34;&gt;&#xA;    &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;        &lt;p&gt;&#xA;        &#xA;          &#xA;            &lt;b&gt;Next article: &lt;/b&gt;&lt;a href=&#34;/blog/survey2024-h2-results&#34;&gt;Go Developer Survey 2024 H2 Results&lt;/a&gt;&lt;br&gt;&#xA;          &#xA;        &#xA;        &#xA;          &#xA;            &lt;b&gt;Previous article: &lt;/b&gt;&lt;a href=&#34;/blog/15years&#34;&gt;Go Turns 15&lt;/a&gt;&lt;br&gt;&#xA;          &#xA;        &#xA;        &lt;b&gt;&lt;a href=&#34;/blog/all&#34;&gt;Blog Index&lt;/a&gt;&lt;/b&gt;&#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;      &#xA;    &#xA;    &lt;/div&gt;&#xA;    &#xA;&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;script src=&#34;/js/play.js&#34;&gt;&lt;/script&gt;&#xA;&#xA;</content></entry></feed>

If you would like to create a banner that links to this page (i.e. this validation result), do the following:

  1. Download the "valid Atom 1.0" banner.

  2. Upload the image to your own server. (This step is important. Please do not link directly to the image on this server.)

  3. Add this HTML to your page (change the image src attribute if necessary):

If you would like to create a text link instead, here is the URL you can use:

http://www.feedvalidator.org/check.cgi?url=http%3A//blog.golang.org/feed.atom

Copyright © 2002-9 Sam Ruby, Mark Pilgrim, Joseph Walton, and Phil Ringnalda