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-07-07T00:00:00+00:00</updated><entry><title>Generic interfaces</title><id>tag:blog.golang.org,2013:blog.golang.org/generic-interfaces</id><link rel="alternate" href="https://go.dev/blog/generic-interfaces"></link><published>2025-07-07T00:00:00+00:00</published><updated>2025-07-07T00:00:00+00:00</updated><author><name>Axel Wagner</name></author><summary type="html">Adding type parameters to interface types is surprisingly powerful</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/generic-interfaces&#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;Generic interfaces&lt;/h1&gt;&#xA;      &#xA;      &lt;p class=&#34;author&#34;&gt;&#xA;      Axel Wagner&lt;br&gt;&#xA;      7 July 2025&#xA;      &lt;/p&gt;&#xA;      &#xA;      &lt;p&gt;There is an idea that is not obvious until you hear about it for the first time: as interfaces are types themselves, they too can have type parameters.&#xA;This idea proves to be surprisingly powerful when it comes to expressing constraints on generic functions and types.&#xA;In this post, we&amp;rsquo;ll demonstrate it, by discussing the use of interfaces with type parameters in a couple of common scenarios.&lt;/p&gt;&#xA;&lt;h2 id=&#34;a-simple-tree-set&#34;&gt;A simple tree set&lt;/h2&gt;&#xA;&lt;p&gt;As a motivating example, assume we need a generic version of a &lt;a href=&#34;https://en.wikipedia.org/wiki/Binary_search_tree&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;binary search tree&lt;/a&gt;.&#xA;The elements stored in such a tree need to be ordered, so our type parameter needs a constraint that determines the ordering to use.&#xA;A simple option is to use the &lt;a href=&#34;/pkg/cmp#Ordered&#34;&gt;cmp.Ordered&lt;/a&gt; constraint, introduced in Go 1.21.&#xA;It restricts a type parameter to ordered types (strings and numbers) and allows methods of the type to use the built-in ordering operators.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;// The zero value of a Tree is a ready-to-use empty tree.&#xA;type Tree[E cmp.Ordered] struct {&#xA;    root *node[E]&#xA;}&#xA;&#xA;func (t *Tree[E]) Insert(element E) {&#xA;    t.root = t.root.insert(element)&#xA;}&#xA;&#xA;type node[E cmp.Ordered] struct {&#xA;    value E&#xA;    left  *node[E]&#xA;    right *node[E]&#xA;}&#xA;&#xA;func (n *node[E]) insert(element E) *node[E] {&#xA;    if n == nil {&#xA;        return &amp;amp;node[E]{value: element}&#xA;    }&#xA;    switch {&#xA;    case element &amp;lt; n.value:&#xA;        n.left = n.left.insert(element)&#xA;    case element &amp;gt; n.value:&#xA;        n.right = n.right.insert(element)&#xA;    }&#xA;    return n&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;(&lt;a href=&#34;/play/p/H7-n33X7P2h&#34;&gt;playground&lt;/a&gt;)&lt;/p&gt;&#xA;&lt;p&gt;However, this approach has the disadvantage that it only works on basic types for which &lt;code&gt;&amp;lt;&lt;/code&gt; is defined;&#xA;you cannot insert struct types, like &lt;a href=&#34;/pkg/time#Time&#34;&gt;time.Time&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;We can remedy that by requiring the user to provide a comparison function:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;// A FuncTree must be created with NewTreeFunc.&#xA;type FuncTree[E any] struct {&#xA;    root *funcNode[E]&#xA;    cmp  func(E, E) int&#xA;}&#xA;&#xA;func NewFuncTree[E any](cmp func(E, E) int) *FuncTree[E] {&#xA;    return &amp;amp;FuncTree[E]{cmp: cmp}&#xA;}&#xA;&#xA;func (t *FuncTree[E]) Insert(element E) {&#xA;    t.root = t.root.insert(t.cmp, element)&#xA;}&#xA;&#xA;type funcNode[E any] struct {&#xA;    value E&#xA;    left  *funcNode[E]&#xA;    right *funcNode[E]&#xA;}&#xA;&#xA;func (n *funcNode[E]) insert(cmp func(E, E) int, element E) *funcNode[E] {&#xA;    if n == nil {&#xA;        return &amp;amp;funcNode[E]{value: element}&#xA;    }&#xA;    sign := cmp(element, n.value)&#xA;    switch {&#xA;    case sign &amp;lt; 0:&#xA;        n.left = n.left.insert(cmp, element)&#xA;    case sign &amp;gt; 0:&#xA;        n.right = n.right.insert(cmp, element)&#xA;    }&#xA;    return n&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;(&lt;a href=&#34;/play/p/tiEjuxCHtFF&#34;&gt;playground&lt;/a&gt;)&lt;/p&gt;&#xA;&lt;p&gt;This works, but it also comes with downsides.&#xA;We can no longer use the zero value of our container type, because it needs to have an explicitly initialized comparison function.&#xA;And the use of a function field makes it harder for the compiler to inline the comparison calls, which can introduce a significant runtime overhead.&lt;/p&gt;&#xA;&lt;p&gt;Using a method on the element type can solve these issues, because methods are directly associated with a type.&#xA;A method does not have to be explicitly passed and the compiler can see the target of the call and may be able to inline it.&#xA;But how can we express the constraint to require that element types provide the necessary method?&lt;/p&gt;&#xA;&lt;h2 id=&#34;using-the-receiver-in-constraints&#34;&gt;Using the receiver in constraints&lt;/h2&gt;&#xA;&lt;p&gt;The first approach we might try is to define a plain old interface with a &lt;code&gt;Compare&lt;/code&gt; method:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;type Comparer interface {&#xA;    Compare(Comparer) int&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;However, we quickly realize that this does not work well.&#xA;To implement this interface, the method&amp;rsquo;s parameter must itself be &lt;code&gt;Comparer&lt;/code&gt;.&#xA;Not only does that mean that the implementation of this method must type-assert the parameter to its own type, it also requires that every type must explicitly refer to our package with the &lt;code&gt;Comparer&lt;/code&gt; type by name (otherwise the method signatures would not be identical).&#xA;That is not very orthogonal.&lt;/p&gt;&#xA;&lt;p&gt;A better approach is to make the &lt;code&gt;Comparer&lt;/code&gt; interface itself generic:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;type Comparer[T any] interface {&#xA;    Compare(T) int&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;This &lt;code&gt;Comparer&lt;/code&gt; now describes a whole family of interfaces, one for each type that &lt;code&gt;Comparer&lt;/code&gt; may be instantiated with.&#xA;A type that implements &lt;code&gt;Comparer[T]&lt;/code&gt; declares &amp;ldquo;I can compare myself to a &lt;code&gt;T&lt;/code&gt;&amp;rdquo;.&#xA;For instance, &lt;code&gt;time.Time&lt;/code&gt; naturally implements &lt;code&gt;Comparer[time.Time]&lt;/code&gt; because &lt;a href=&#34;/pkg/time#Time.Compare&#34;&gt;it has a matching &lt;code&gt;Compare&lt;/code&gt; method&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;// Implements Comparer[Time]&#xA;func (t Time) Compare(u Time) int&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;This is better, but not enough.&#xA;What we really want is a constraint that says that a type parameter can be compared to &lt;em&gt;itself&lt;/em&gt;: we want the constraint to be self-referential.&#xA;The subtle insight is that the self-referential aspect does not have to be part of the interface definition itself; specifically, the constraint for &lt;code&gt;T&lt;/code&gt; in the &lt;code&gt;Comparer&lt;/code&gt; type is just &lt;code&gt;any&lt;/code&gt;.&#xA;Instead, it is a consequence of how we use &lt;code&gt;Comparer&lt;/code&gt; as a constraint for the type parameter of &lt;code&gt;MethodTree&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;// The zero value of a MethodTree is a ready-to-use empty tree.&#xA;type MethodTree[E Comparer[E]] struct {&#xA;    root *methodNode[E]&#xA;}&#xA;&#xA;func (t *MethodTree[E]) Insert(element E) {&#xA;    t.root = t.root.insert(element)&#xA;}&#xA;&#xA;type methodNode[E Comparer[E]] struct {&#xA;    value E&#xA;    left  *methodNode[E]&#xA;    right *methodNode[E]&#xA;}&#xA;&#xA;func (n *methodNode[E]) insert(element E) *methodNode[E] {&#xA;    if n == nil {&#xA;        return &amp;amp;methodNode[E]{value: element}&#xA;    }&#xA;    sign := element.Compare(n.value)&#xA;    switch {&#xA;    case sign &amp;lt; 0:&#xA;        n.left = n.left.insert(element)&#xA;    case sign &amp;gt; 0:&#xA;        n.right = n.right.insert(element)&#xA;    }&#xA;    return n&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;(&lt;a href=&#34;/play/p/LuhzYej_2SP&#34;&gt;playground&lt;/a&gt;)&lt;/p&gt;&#xA;&lt;p&gt;Because &lt;code&gt;time.Time&lt;/code&gt; implements &lt;code&gt;Comparer[time.Time]&lt;/code&gt; it is now a valid type argument for this container, and we can still use the zero value as an empty container:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;var t MethodTree[time.Time]&#xA;t.Insert(time.Now())&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;For full flexibility, a library can provide all three API versions.&#xA;If we want to minimize repetition, all versions could use a shared implementation.&#xA;We could use the function version for that, as it is the most general:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;type node[E any] struct {&#xA;    value E&#xA;    left  *node[E]&#xA;    right *node[E]&#xA;}&#xA;&#xA;func (n *node[E]) insert(cmp func(E, E) int, element E) *node[E] {&#xA;    if n == nil {&#xA;        return &amp;amp;node[E]{value: element}&#xA;    }&#xA;    sign := cmp(element, n.value)&#xA;    switch {&#xA;    case sign &amp;lt; 0:&#xA;        n.left = n.left.insert(cmp, element)&#xA;    case sign &amp;gt; 0:&#xA;        n.right = n.right.insert(cmp, element)&#xA;    }&#xA;    return n&#xA;}&#xA;&#xA;// Insert inserts element into the tree, if E implements cmp.Ordered.&#xA;func (t *Tree[E]) Insert(element E) {&#xA;    t.root = t.root.insert(cmp.Compare[E], element)&#xA;}&#xA;&#xA;// Insert inserts element into the tree, using the provided comparison function.&#xA;func (t *FuncTree[E]) Insert(element E) {&#xA;    t.root = t.root.insert(t.cmp, element)&#xA;}&#xA;&#xA;// Insert inserts element into the tree, if E implements Comparer[E].&#xA;func (t *MethodTree[E]) Insert(element E) {&#xA;    t.root = t.root.insert(E.Compare, element)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;(&lt;a href=&#34;/play/p/jzmoaH5eaIv&#34;&gt;playground&lt;/a&gt;)&lt;/p&gt;&#xA;&lt;p&gt;An important observation here is that the shared implementation (the function-based variant) is not constrained in any way.&#xA;It must remain maximally flexible to serve as a common core.&#xA;We also do not store the comparison function in a struct field.&#xA;Instead, we pass it as a parameter because function arguments are easier for the compiler to analyze than struct fields.&lt;/p&gt;&#xA;&lt;p&gt;There is still some amount of boilerplate involved, of course.&#xA;All the exported implementations need to replicate the full API with slightly different call patterns.&#xA;But this part is straightforward to write and to read.&lt;/p&gt;&#xA;&lt;h2 id=&#34;combining-methods-and-type-sets&#34;&gt;Combining methods and type sets&lt;/h2&gt;&#xA;&lt;p&gt;We can use our new tree data structure to implement an ordered set, providing element lookup in logarithmic time.&#xA;Let&amp;rsquo;s now imagine we need to make lookup run in constant time; we might try to do this by maintaining an ordinary Go map alongside the tree:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;type OrderedSet[E Comparer[E]] struct {&#xA;    tree     MethodTree[E] // for efficient iteration in order&#xA;    elements map[E]bool    // for (near) constant time lookup&#xA;}&#xA;&#xA;func (s *OrderedSet[E]) Has(e E) bool {&#xA;    return s.elements[e]&#xA;}&#xA;&#xA;func (s *OrderedSet[E]) Insert(e E) {&#xA;    if s.elements == nil {&#xA;        s.elements = make(map[E]bool)&#xA;    }&#xA;    if s.elements[e] {&#xA;        return&#xA;    }&#xA;    s.elements[e] = true&#xA;    s.tree.Insert(e)&#xA;}&#xA;&#xA;func (s *OrderedSet[E]) All() iter.Seq[E] {&#xA;    return func(yield func(E) bool) {&#xA;        s.tree.root.all(yield)&#xA;    }&#xA;}&#xA;&#xA;func (n *node[E]) all(yield func(E) bool) bool {&#xA;    return n == nil || (n.left.all(yield) &amp;amp;&amp;amp; yield(n.value) &amp;amp;&amp;amp; n.right.all(yield))&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;(&lt;a href=&#34;/play/p/TANUnnSnDqf&#34;&gt;playground&lt;/a&gt;)&lt;/p&gt;&#xA;&lt;p&gt;However, compiling this code will produce an error:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;invalid map key type E (missing comparable constraint)&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;The error message tells us that we need to further constrain our type parameter to be able to use it as a map key.&#xA;The &lt;code&gt;comparable&lt;/code&gt; constraint is a special predeclared constraint that is satisfied by all types for which the equality operators &lt;code&gt;==&lt;/code&gt; and &lt;code&gt;!=&lt;/code&gt; are defined.&#xA;In Go, that is also the set of types which can be used as keys for the built-in &lt;code&gt;map&lt;/code&gt; type.&lt;/p&gt;&#xA;&lt;p&gt;We have three options to add this constraint to our type parameter, all with different tradeoffs:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;We can &lt;a href=&#34;/ref/spec#Embedded_interfaces&#34;&gt;embed&lt;/a&gt; &lt;code&gt;comparable&lt;/code&gt; into our original &lt;code&gt;Comparer&lt;/code&gt; definition (&lt;a href=&#34;/play/p/g8NLjZCq97q&#34;&gt;playground&lt;/a&gt;):&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;type Comparer[E any] interface {&#xA;    comparable&#xA;    Compare(E) int&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;This has the downside that it would also make our &lt;code&gt;Tree&lt;/code&gt; types only usable with types that are &lt;code&gt;comparable&lt;/code&gt;.&#xA;In general, we do not want to unnecessarily restrict generic types.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;We can add a new constraint definition (&lt;a href=&#34;/play/p/Z2eg4X8xK5Z&#34;&gt;playground&lt;/a&gt;).&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;type Comparer[E any] interface {&#xA;    Compare(E) int&#xA;}&#xA;&#xA;type ComparableComparer[E any] interface {&#xA;    comparable&#xA;    Comparer[E]&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;This is tidy, but it introduces a new identifier (&lt;code&gt;ComparableComparer&lt;/code&gt;) into our API, and naming is hard.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;We can add the constraint inline into our more constrained type (&lt;a href=&#34;/play/p/ZfggVma_jNc&#34;&gt;playground&lt;/a&gt;):&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;type OrderedSet[E interface {&#xA;    comparable&#xA;    Comparer[E]&#xA;}] struct {&#xA;    tree     Tree[E]&#xA;    elements map[E]struct{}&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;This can become a bit hard to read, especially if it needs to happen often.&#xA;It also makes it harder to reuse the constraint in other places.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Which of these to use is a style choice and ultimately up to personal preference.&lt;/p&gt;&#xA;&lt;h2 id=&#34;not-constraining-generic-interfaces&#34;&gt;(Not) constraining generic interfaces&lt;/h2&gt;&#xA;&lt;p&gt;At this point it is worth discussing constraints on generic interfaces.&#xA;You might want to define an interface for a generic container type.&#xA;For example, say you have an algorithm that requires a set data structure.&#xA;There are many different kinds of set implementations with different tradeoffs.&#xA;Defining an interface for the set operations you require can add flexibility to your package, leaving the decision of what tradeoffs are right for the specific application to the user:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;type Set[E any] interface {&#xA;    Insert(E)&#xA;    Delete(E)&#xA;    Has(E) bool&#xA;    All() iter.Seq[E]&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;A natural question here is what the constraint on this interface should be.&#xA;If possible, type parameters on generic interfaces should use &lt;code&gt;any&lt;/code&gt; as a constraint, allowing arbitrary types.&lt;/p&gt;&#xA;&lt;p&gt;From our discussions above, the reasons should be clear:&#xA;Different concrete implementations might require different constraints.&#xA;All the &lt;code&gt;Tree&lt;/code&gt; types we have examined above, as well as the &lt;code&gt;OrderedSet&lt;/code&gt; type, can implement &lt;code&gt;Set&lt;/code&gt; for their element types, even though these types have different constraints.&lt;/p&gt;&#xA;&lt;p&gt;The point of defining an interface is to leave the implementation up to the user.&#xA;Since one cannot predict what kinds of constraints a user may want to impose on their implementation, try to leave any constraints (stronger than &lt;code&gt;any&lt;/code&gt;) to concrete implementations, not the interfaces.&lt;/p&gt;&#xA;&lt;h2 id=&#34;pointer-receivers&#34;&gt;Pointer receivers&lt;/h2&gt;&#xA;&lt;p&gt;Let us try to use the &lt;code&gt;Set&lt;/code&gt; interface in an example.&#xA;Consider a function that removes duplicate elements in a sequence:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;// Unique removes duplicate elements from the input sequence, yielding only&#xA;// the first instance of any element.&#xA;func Unique[E comparable](input iter.Seq[E]) iter.Seq[E] {&#xA;    return func(yield func(E) bool) {&#xA;        seen := make(map[E]bool)&#xA;        for v := range input {&#xA;            if seen[v] {&#xA;                continue&#xA;            }&#xA;            if !yield(v) {&#xA;                return&#xA;            }&#xA;            seen[v] = true&#xA;        }&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;(&lt;a href=&#34;/play/p/hsYoFjkU9kA&#34;&gt;playground&lt;/a&gt;)&lt;/p&gt;&#xA;&lt;p&gt;This uses a &lt;code&gt;map[E]bool&lt;/code&gt; as a simple set of &lt;code&gt;E&lt;/code&gt; elements.&#xA;Consequently, it works only for types that are &lt;code&gt;comparable&lt;/code&gt; and which therefore define the built-in equality operators.&#xA;If we want to generalize this to arbitrary types, we need to replace that with a generic set:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;// Unique removes duplicate elements from the input sequence, yielding only&#xA;// the first instance of any element.&#xA;func Unique[E any](input iter.Seq[E]) iter.Seq[E] {&#xA;    return func(yield func(E) bool) {&#xA;        var seen Set[E]&#xA;        for v := range input {&#xA;            if seen.Has(v) {&#xA;                continue&#xA;            }&#xA;            if !yield(v) {&#xA;                return&#xA;            }&#xA;            seen.Insert(v)&#xA;        }&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;(&lt;a href=&#34;/play/p/FZYPNf56nnY&#34;&gt;playground&lt;/a&gt;)&lt;/p&gt;&#xA;&lt;p&gt;However, this does not work.&#xA;&lt;code&gt;Set[E]&lt;/code&gt; is an interface type, and the &lt;code&gt;seen&lt;/code&gt; variable will be initialized to &lt;code&gt;nil&lt;/code&gt;.&#xA;We need to use a concrete implementation of the &lt;code&gt;Set[E]&lt;/code&gt; interface.&#xA;But as we have seen in this post, there is no general implementation of a set that works for &lt;code&gt;any&lt;/code&gt; element type.&lt;/p&gt;&#xA;&lt;p&gt;We have to ask the user to provide a concrete implementation we can use, as an extra type parameter:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;// Unique removes duplicate elements from the input sequence, yielding only&#xA;// the first instance of any element.&#xA;func Unique[E any, S Set[E]](input iter.Seq[E]) iter.Seq[E] {&#xA;    return func(yield func(E) bool) {&#xA;        var seen S&#xA;        for v := range input {&#xA;            if seen.Has(v) {&#xA;                continue&#xA;            }&#xA;            if !yield(v) {&#xA;                return&#xA;            }&#xA;            seen.Insert(v)&#xA;        }&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;(&lt;a href=&#34;/play/p/kjkGy5cNz8T&#34;&gt;playground&lt;/a&gt;)&lt;/p&gt;&#xA;&lt;p&gt;However, if we instantiate this with our set implementation, we run into another problem:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;// OrderedSet[E] does not satisfy Set[E] (method All has pointer receiver)&#xA;Unique[E, OrderedSet[E]](slices.Values(s))&#xA;// panic: invalid memory address or nil pointer dereference&#xA;Unique[E, *OrderedSet[E]](slices.Values(s))&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The first problem is clear from the error message: Our type constraint says that the type argument for &lt;code&gt;S&lt;/code&gt; needs to implement the &lt;code&gt;Set[E]&lt;/code&gt; interface.&#xA;And as the methods on &lt;code&gt;OrderedSet&lt;/code&gt; use a pointer receiver, the type argument also has to be the pointer type.&lt;/p&gt;&#xA;&lt;p&gt;When trying to do that, we run into the second problem.&#xA;This stems from the fact that we declare a variable in the implementation:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;var seen S&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;If &lt;code&gt;S&lt;/code&gt; is &lt;code&gt;*OrderedSet[E]&lt;/code&gt;, the variable is initialized with &lt;code&gt;nil&lt;/code&gt;, as before.&#xA;Calling &lt;code&gt;seen.Insert&lt;/code&gt; panics.&lt;/p&gt;&#xA;&lt;p&gt;If we only have the pointer type, we cannot get a valid variable of the value type.&#xA;And if we only have the value type, we cannot call pointer-methods on it.&#xA;The consequence is that we need both the value &lt;em&gt;and&lt;/em&gt; the pointer type.&#xA;So we have to introduce an additional type parameter &lt;code&gt;PS&lt;/code&gt; with a new constraint &lt;code&gt;PtrToSet&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;// PtrToSet is implemented by a pointer type implementing the Set[E] interface.&#xA;type PtrToSet[S, E any] interface {&#xA;    *S&#xA;    Set[E]&#xA;}&#xA;&#xA;// Unique removes duplicate elements from the input sequence, yielding only&#xA;// the first instance of any element.&#xA;func Unique[E, S any, PS PtrToSet[S, E]](input iter.Seq[E]) iter.Seq[E] {&#xA;    return func(yield func(E) bool) {&#xA;        // We convert to PS, as only that is constrained to have the methods.&#xA;        // The conversion is allowed, because the type set of PS only contains *S.&#xA;        seen := PS(new(S))&#xA;        for v := range input {&#xA;            if seen.Has(v) {&#xA;                continue&#xA;            }&#xA;            if !yield(v) {&#xA;                return&#xA;            }&#xA;            seen.Insert(v)&#xA;        }&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;(&lt;a href=&#34;/play/p/Kp1jJRVjmYa&#34;&gt;playground&lt;/a&gt;)&lt;/p&gt;&#xA;&lt;p&gt;The trick here is the connection of the two type parameters in the function signature via the extra type parameter on the &lt;code&gt;PtrToSet&lt;/code&gt; interface.&#xA;&lt;code&gt;S&lt;/code&gt; itself is unconstrained, but &lt;code&gt;PS&lt;/code&gt; must have type &lt;code&gt;*S&lt;/code&gt; and it must have the methods we need.&#xA;So effectively, we are restricting &lt;code&gt;S&lt;/code&gt; to have some methods, but those methods need to use a pointer receiver.&lt;/p&gt;&#xA;&lt;p&gt;While the definition of a function with this kind of constraint requires an additional type parameter, importantly this does not complicate code using it:&#xA;as long as this extra type parameter is at the end of the type parameter list, it &lt;a href=&#34;/blog/type-inference&#34;&gt;can be inferred&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;// The third type argument is inferred to be *OrderedSet[int]&#xA;Unique[int, OrderedSet[int]](slices.Values(s))&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;This is a general pattern, and worth remembering: for when you encounter it in someone else&amp;rsquo;s work, or when you want to use it in your own.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;func SomeFunction[T any, PT interface{ *T; SomeMethods }]()&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;If you have two type parameters, where one is constrained to be a pointer to the other, the constraint ensures that the relevant methods use a pointer receiver.&lt;/p&gt;&#xA;&lt;h2 id=&#34;should-you-constrain-to-pointer-receivers&#34;&gt;Should you constrain to pointer receivers?&lt;/h2&gt;&#xA;&lt;p&gt;At this point, you might feel pretty overwhelmed.&#xA;This is rather complicated and it seems unreasonable to expect every Go programmer to understand what is going on in this function signature.&#xA;We also had to introduce yet more names into our API.&#xA;When people cautioned against adding generics to Go in the first place, this is one of the things they were worried about.&lt;/p&gt;&#xA;&lt;p&gt;So if you find yourself entangled in these problems, it is worth taking a step back.&#xA;We can often avoid this complexity by thinking about our problem in a different way.&#xA;In this example, we built a function that takes an &lt;code&gt;iter.Seq[E]&lt;/code&gt; and returns an &lt;code&gt;iter.Seq[E]&lt;/code&gt; with the unique elements.&#xA;But to do the deduplication, we needed to collect the unique elements into a set.&#xA;And as this requires us to allocate the space for the entire result, we do not really benefit from representing the result as a stream.&lt;/p&gt;&#xA;&lt;p&gt;If we rethink this problem, we can avoid the extra type parameter altogether by using &lt;code&gt;Set[E]&lt;/code&gt; as a regular interface value:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;// InsertAll adds all unique elements from seq into set.&#xA;func InsertAll[E any](set Set[E], seq iter.Seq[E]) {&#xA;    for v := range seq {&#xA;        set.Insert(v)&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;(&lt;a href=&#34;/play/p/woZcHodAgaa&#34;&gt;playground&lt;/a&gt;)&lt;/p&gt;&#xA;&lt;p&gt;By using &lt;code&gt;Set&lt;/code&gt; as a plain interface type, it is clear that the caller has to provide a valid value of their concrete implementation.&#xA;This is a very common pattern.&#xA;And if they need an &lt;code&gt;iter.Seq[E]&lt;/code&gt;, they can simply call &lt;code&gt;All()&lt;/code&gt; on the &lt;code&gt;set&lt;/code&gt; to obtain one.&lt;/p&gt;&#xA;&lt;p&gt;This complicates things for callers slightly, but it has another advantage over the constraint to pointer receivers:&#xA;remember that we started with a &lt;code&gt;map[E]bool&lt;/code&gt; as a simple set type.&#xA;It is easy to implement the &lt;code&gt;Set[E]&lt;/code&gt; interface on that basis:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;type HashSet[E comparable] map[E]bool&#xA;&#xA;func (s HashSet[E]) Insert(v E)       { s[v] = true }&#xA;func (s HashSet[E]) Delete(v E)       { delete(s, v) }&#xA;func (s HashSet[E]) Has(v E) bool     { return s[v] }&#xA;func (s HashSet[E]) All() iter.Seq[E] { return maps.Keys(s) }&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;(&lt;a href=&#34;/play/p/KPPpWa7M93d&#34;&gt;playground&lt;/a&gt;)&lt;/p&gt;&#xA;&lt;p&gt;This implementation does not use pointer receivers.&#xA;So while this is perfectly valid, it would not be usable with the complicated constraint to pointer receivers.&#xA;But it works fine with our &lt;code&gt;InsertAll&lt;/code&gt; version.&#xA;As with many constraints, enforcing that methods use a pointer receiver might actually be overly restrictive for many practical use cases.&lt;/p&gt;&#xA;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;&#xA;&lt;p&gt;I hope this illustrates some of the patterns and trade-offs that type parameters on interfaces enable.&#xA;It is a powerful tool, but it also comes with a cost.&#xA;The primary take-aways are:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Use generic interfaces to express constraints on the receiver by using them self-referentially.&lt;/li&gt;&#xA;&lt;li&gt;Use them to create constrained relationships between different type parameters.&lt;/li&gt;&#xA;&lt;li&gt;Use them to abstract over different implementations with different kinds of constraints.&lt;/li&gt;&#xA;&lt;li&gt;When you find yourself in a situation where you need to constrain to pointer receivers, consider whether you can refactor your code to avoid the extra complexity. See &lt;a href=&#34;#should-you-constrain-to-pointer-receivers&#34;&gt;&amp;ldquo;Should you constrain to pointer receivers?&amp;rdquo;&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;As always, do not over-engineer things: a less flexible but simpler and more readable solution may ultimately be the wiser choice.&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/error-syntax&#34;&gt;[ On | No ] syntactic support for error handling&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;      &#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>[ On | No ] syntactic support for error handling</title><id>tag:blog.golang.org,2013:blog.golang.org/error-syntax</id><link rel="alternate" href="https://go.dev/blog/error-syntax"></link><published>2025-06-03T00:00:00+00:00</published><updated>2025-06-03T00:00:00+00:00</updated><author><name>Robert Griesemer</name></author><summary type="html">Go team plans around error handling support</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/error-syntax&#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;[ On | No ] syntactic support for error handling&lt;/h1&gt;&#xA;      &#xA;      &lt;p class=&#34;author&#34;&gt;&#xA;      Robert Griesemer&lt;br&gt;&#xA;      3 June 2025&#xA;      &lt;/p&gt;&#xA;      &#xA;      &lt;p&gt;One of the oldest and most persistent complaints about Go concerns the verbosity of error handling.&#xA;We are all intimately (some may say painfully) familiar with this code pattern:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-Go&#34;&gt;x, err := call()&#xA;if err != nil {&#xA;        // handle err&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The test &lt;code&gt;if err != nil&lt;/code&gt; can be so pervasive that it drowns out the rest of the code.&#xA;This typically happens in programs that do a lot of API calls, and where handling errors&#xA;is rudimentary and they are simply returned.&#xA;Some programs end up with code that looks like this:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-Go&#34;&gt;func printSum(a, b string) error {&#xA;    x, err := strconv.Atoi(a)&#xA;    if err != nil {&#xA;        return err&#xA;    }&#xA;    y, err := strconv.Atoi(b)&#xA;    if err != nil {&#xA;        return err&#xA;    }&#xA;    fmt.Println(&amp;quot;result:&amp;quot;, x + y)&#xA;    return nil&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Of the ten lines of code in this function body, only four (the calls and the last two lines) appear to do real work.&#xA;The remaining six lines come across as noise.&#xA;The verbosity is real, and so it&amp;rsquo;s no wonder that complaints about error handling have topped&#xA;our annual user surveys for years.&#xA;(For a while, the lack of generics surpassed complaints about error handling, but now that&#xA;Go supports generics, error handling is back on top.)&lt;/p&gt;&#xA;&lt;p&gt;The Go team takes community feedback seriously, and so for many years now we have tried to&#xA;come up with a solution for this problem, together with input from the Go community.&lt;/p&gt;&#xA;&lt;p&gt;The first explicit attempt by the Go team dates back to 2018, when Russ Cox&#xA;&lt;a href=&#34;https://go.googlesource.com/proposal/+/master/design/go2draft-error-handling-overview.md&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;formally described the problem&lt;/a&gt;&#xA;as part of what we called the Go 2 effort at that time.&#xA;He outlined a possible solution based on a&#xA;&lt;a href=&#34;https://go.googlesource.com/proposal/+/master/design/go2draft-error-handling.md&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;draft design&lt;/a&gt;&#xA;by Marcel van Lohuizen.&#xA;The design was based on a &lt;code&gt;check&lt;/code&gt; and &lt;code&gt;handle&lt;/code&gt; mechanism and was fairly comprehensive.&#xA;The draft includes a detailed analysis of alternative solutions, including comparisons with&#xA;approaches taken by other languages.&#xA;If you&amp;rsquo;re wondering if your particular error handling idea was previously considered,&#xA;read this document!&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-Go&#34;&gt;// printSum implementation using the proposed check/handle mechanism.&#xA;func printSum(a, b string) error {&#xA;    handle err { return err }&#xA;    x := check strconv.Atoi(a)&#xA;    y := check strconv.Atoi(b)&#xA;    fmt.Println(&amp;quot;result:&amp;quot;, x + y)&#xA;    return nil&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The &lt;code&gt;check&lt;/code&gt; and &lt;code&gt;handle&lt;/code&gt; approach was deemed too complicated and almost a year later, in 2019,&#xA;we followed up with the much simplified and by now&#xA;&lt;a href=&#34;/issue/32437#issuecomment-2278932700&#34;&gt;infamous&lt;/a&gt;&#xA;&lt;a href=&#34;https://go.googlesource.com/proposal/+/master/design/32437-try-builtin.md&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;&lt;code&gt;try&lt;/code&gt; proposal&lt;/a&gt;.&#xA;It was based on the ideas of &lt;code&gt;check&lt;/code&gt; and &lt;code&gt;handle&lt;/code&gt;, but the &lt;code&gt;check&lt;/code&gt; pseudo-keyword became&#xA;the &lt;code&gt;try&lt;/code&gt; built-in function and the &lt;code&gt;handle&lt;/code&gt; part was omitted.&#xA;To explore the impact of the &lt;code&gt;try&lt;/code&gt; built-in, we wrote a simple tool&#xA;(&lt;a href=&#34;https://github.com/griesemer/tryhard&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;tryhard&lt;/a&gt;)&#xA;that rewrites existing error handling code using &lt;code&gt;try&lt;/code&gt;.&#xA;The proposal was argued over intensively, approaching 900 comments on the &lt;a href=&#34;/issue/32437&#34;&gt;GitHub issue&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-Go&#34;&gt;// printSum implementation using the proposed try mechanism.&#xA;func printSum(a, b string) error {&#xA;    // use a defer statement to augment errors before returning&#xA;    x := try(strconv.Atoi(a))&#xA;    y := try(strconv.Atoi(b))&#xA;    fmt.Println(&amp;quot;result:&amp;quot;, x + y)&#xA;    return nil&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;However, &lt;code&gt;try&lt;/code&gt; affected control flow by returning from the enclosing function in case of an error,&#xA;and did so from potentially deeply nested expressions, thus hiding this control flow from view.&#xA;This made the proposal unpalatable to many, and despite significant investment&#xA;into this proposal we decided to abandon this effort too.&#xA;In retrospect it might have been better to introduce a new keyword,&#xA;something that we could do now since we have fine-grained control over the language version&#xA;via &lt;code&gt;go.mod&lt;/code&gt; files and file-specific directives.&#xA;Restricting the use of &lt;code&gt;try&lt;/code&gt; to assignments and statements might have alleviated some&#xA;of the other concerns. A &lt;a href=&#34;/issue/73376&#34;&gt;recent proposal&lt;/a&gt; by Jimmy Frasche, which essentially&#xA;goes back to the original &lt;code&gt;check&lt;/code&gt; and &lt;code&gt;handle&lt;/code&gt; design and addresses some of that design&amp;rsquo;s&#xA;shortcomings, pursues that direction.&lt;/p&gt;&#xA;&lt;p&gt;The repercussions of the &lt;code&gt;try&lt;/code&gt; proposal led to much soul searching including a series of blog&#xA;posts by Russ Cox: &lt;a href=&#34;https://research.swtch.com/proposals-intro&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;&amp;ldquo;Thinking about the Go Proposal Process&amp;rdquo;&lt;/a&gt;.&#xA;One conclusion was that we likely diminished our chances for a better outcome by presenting an almost&#xA;fully baked proposal with little space for community feedback and a &amp;ldquo;threatening&amp;rdquo; implementation&#xA;timeline. Per &lt;a href=&#34;https://research.swtch.com/proposals-large&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;&amp;ldquo;Go Proposal Process: Large Changes&amp;rdquo;&lt;/a&gt;:&#xA;&amp;ldquo;in retrospect, &lt;code&gt;try&lt;/code&gt; was a large enough change that the new design we published [&amp;hellip;] should have&#xA;been a second draft design, not a proposal with an implementation timeline&amp;rdquo;.&#xA;But irrespective of a possible process and communication failure in this case, the user sentiment towards&#xA;the proposal was very strongly not in favor.&lt;/p&gt;&#xA;&lt;p&gt;We didn&amp;rsquo;t have a better solution at that time and didn&amp;rsquo;t pursue syntax changes for error handling for several years.&#xA;Plenty of people in the community were inspired, though, and we received a steady trickle&#xA;of error handling proposals, many very similar to each other, some interesting, some incomprehensible,&#xA;and some infeasible.&#xA;To keep track of the expanding landscape, another year later, Ian Lance Taylor created an&#xA;&lt;a href=&#34;/issue/40432&#34;&gt;umbrella issue&lt;/a&gt;&#xA;which summarizes the current state of proposed changes for improved error handling.&#xA;A &lt;a href=&#34;/wiki/Go2ErrorHandlingFeedback&#34;&gt;Go Wiki&lt;/a&gt; was created to collect related feedback, discussions, and articles.&#xA;Independently, other people have started tracking all the many error handling proposals&#xA;over the years.&#xA;It&amp;rsquo;s amazing to see the sheer volume of them all, for instance in Sean K. H. Liao&amp;rsquo;s blog post on&#xA;&lt;a href=&#34;https://seankhliao.com/blog/12020-11-23-go-error-handling-proposals/&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;&amp;ldquo;go error handling proposals&amp;rdquo;&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The complaints about the verbosity of error handling persisted&#xA;(see &lt;a href=&#34;/blog/survey2024-h1-results&#34;&gt;Go Developer Survey 2024 H1 Results&lt;/a&gt;),&#xA;and so, after a series of increasingly refined Go team internal proposals, Ian Lance Taylor published&#xA;&lt;a href=&#34;/issue/71203&#34;&gt;&amp;ldquo;reduce error handling boilerplate using &lt;code&gt;?&lt;/code&gt;&amp;rdquo;&lt;/a&gt; in 2024.&#xA;This time the idea was to borrow from a construct implemented in&#xA;&lt;a href=&#34;https://www.rust-lang.org/&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;Rust&lt;/a&gt;, specifically the&#xA;&lt;a href=&#34;https://doc.rust-lang.org/std/result/index.html#the-question-mark-operator-&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;&lt;code&gt;?&lt;/code&gt; operator&lt;/a&gt;.&#xA;The hope was that by leaning on an existing mechanism using an established notation, and taking into&#xA;account what we had learned over the years, we should be able to finally make some progress.&#xA;In small informal user studies where programmers were shown Go code using &lt;code&gt;?&lt;/code&gt;, the vast majority&#xA;of participants correctly guessed the meaning of the code, which further convinced us to give it another&#xA;shot.&#xA;To be able to see the impact of the change, Ian wrote a tool that converts ordinary Go code&#xA;into code that uses the proposed new syntax, and we also prototyped the feature in the&#xA;compiler.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-Go&#34;&gt;// printSum implementation using the proposed &amp;quot;?&amp;quot; statements.&#xA;func printSum(a, b string) error {&#xA;    x := strconv.Atoi(a) ?&#xA;    y := strconv.Atoi(b) ?&#xA;    fmt.Println(&amp;quot;result:&amp;quot;, x + y)&#xA;    return nil&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Unfortunately, as with the other error handling ideas, this new proposal was also quickly overrun&#xA;with comments and many suggestions for minor tweaks, often based on individual preferences.&#xA;Ian closed the proposal and moved the content into a &lt;a href=&#34;/issue/71460&#34;&gt;discussion&lt;/a&gt;&#xA;to facilitate the conversation and to collect further feedback.&#xA;A slightly modified version was received&#xA;&lt;a href=&#34;https://github.com/golang/go/discussions/71460#discussioncomment-12060294&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;a bit more positively&lt;/a&gt;&#xA;but broad support remained elusive.&lt;/p&gt;&#xA;&lt;p&gt;After so many years of trying, with three full-fledged proposals by the Go team and&#xA;literally &lt;a href=&#34;/issues?q=+is%3Aissue+label%3Aerror-handling&#34;&gt;hundreds&lt;/a&gt; (!)&#xA;of community proposals, most of them variations on a theme,&#xA;all of which failed to attract sufficient (let alone overwhelming) support,&#xA;the question we now face is: how to proceed? Should we proceed at all?&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;We think not.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;To be more precise, we should stop trying to solve the &lt;em&gt;syntactic problem&lt;/em&gt;, at least for the foreseeable&#xA;future.&#xA;The &lt;a href=&#34;https://github.com/golang/proposal?tab=readme-ov-file#consensus-and-disagreement&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;proposal process&lt;/a&gt;&#xA;provides justification for this decision:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;The goal of the proposal process is to reach general consensus about the outcome in a timely manner.&#xA;If proposal review cannot identify a general consensus in the discussion of the issue on the issue tracker,&#xA;the usual result is that the proposal is declined.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Furthermore:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;It can happen that proposal review may not identify a general consensus and yet it is clear that the&#xA;proposal should not be outright declined.&#xA;[&amp;hellip;]&#xA;If the proposal review group cannot identify a consensus nor a next step for the proposal,&#xA;the decision about the path forward passes to the Go architects [&amp;hellip;], who review the discussion and&#xA;aim to reach a consensus among themselves.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;None of the error handling proposals reached anything close to a consensus,&#xA;so they were all declined.&#xA;Even the most senior members of the Go team at Google do not unanimously agree&#xA;on the best path forward &lt;em&gt;at this time&lt;/em&gt; (perhaps that will change at some point).&#xA;But without a strong consensus we cannot reasonably move forward.&lt;/p&gt;&#xA;&lt;p&gt;There are valid arguments in favor of the status quo:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;If Go had introduced specific syntactic sugar for error handling early on, few would argue over it today.&#xA;But we are 15 years down the road, the opportunity has passed, and Go has&#xA;a perfectly fine way to handle errors, even if it may seem verbose at times.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Looking from a different angle, let&amp;rsquo;s assume we came across the perfect solution today.&#xA;Incorporating it into the language would simply lead from one unhappy group of users&#xA;(the one that roots for the change) to another (the one that prefers the status quo).&#xA;We were in a similar situation when we decided to add generics to the language, albeit with an&#xA;important difference:&#xA;today nobody is forced to use generics, and good generic libraries are written such that users&#xA;can mostly ignore the fact that they are generic, thanks to type inference.&#xA;On the contrary, if a new syntactic construct for error handling gets added to the language,&#xA;virtually everybody will need to start using it, lest their code become unidiomatic.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Not adding extra syntax is in line with one of Go&amp;rsquo;s design rules:&#xA;do not provide multiple ways of doing the same thing.&#xA;There are exceptions to this rule in areas with high &amp;ldquo;foot traffic&amp;rdquo;: assignments come to mind.&#xA;Ironically, the ability to &lt;em&gt;redeclare&lt;/em&gt; a variable in&#xA;&lt;a href=&#34;/ref/spec#Short_variable_declarations&#34;&gt;short variable declarations&lt;/a&gt; (&lt;code&gt;:=&lt;/code&gt;) was introduced to address a problem&#xA;that arose because of error handling:&#xA;without redeclarations, sequences of error checks require a differently named &lt;code&gt;err&lt;/code&gt; variable for&#xA;each check (or additional separate variable declarations).&#xA;At that time, a better solution might have been to provide more syntactic support for error handling.&#xA;Then, the redeclaration rule may not have been needed, and with it gone, so would be various&#xA;associated &lt;a href=&#34;/issue/377&#34;&gt;complications&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Going back to actual error handling code, verbosity fades into the background if errors are&#xA;actually &lt;em&gt;handled&lt;/em&gt;.&#xA;Good error handling often requires additional information added to an error.&#xA;For instance, a recurring comment in user surveys is about the lack of stack traces associated&#xA;with an error.&#xA;This could be addressed with support functions that produce and return an augmented&#xA;error.&#xA;In this (admittedly contrived) example, the relative amount of boilerplate is much smaller:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-Go&#34;&gt;func printSum(a, b string) error {&#xA;    x, err := strconv.Atoi(a)&#xA;    if err != nil {&#xA;        return fmt.Errorf(&amp;quot;invalid integer: %q&amp;quot;, a)&#xA;    }&#xA;    y, err := strconv.Atoi(b)&#xA;    if err != nil {&#xA;        return fmt.Errorf(&amp;quot;invalid integer: %q&amp;quot;, b)&#xA;    }&#xA;    fmt.Println(&amp;quot;result:&amp;quot;, x + y)&#xA;    return nil&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;New standard library functionality can help reduce error handling boilerplate as well,&#xA;very much in the vein of Rob Pike&amp;rsquo;s 2015 blog post&#xA;&lt;a href=&#34;/blog/errors-are-values&#34;&gt;&amp;ldquo;Errors are values&amp;rdquo;&lt;/a&gt;.&#xA;For instance, in some cases &lt;a href=&#34;/pkg/cmp#Or&#34;&gt;&lt;code&gt;cmp.Or&lt;/code&gt;&lt;/a&gt; may be used to deal with a&#xA;series of errors all at once:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-Go&#34;&gt;func printSum(a, b string) error {&#xA;    x, err1 := strconv.Atoi(a)&#xA;    y, err2 := strconv.Atoi(b)&#xA;    if err := cmp.Or(err1, err2); err != nil {&#xA;        return err&#xA;    }&#xA;    fmt.Println(&amp;quot;result:&amp;quot;, x+y)&#xA;    return nil&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Writing, reading, and debugging code are all quite different activities.&#xA;Writing repeated error checks can be tedious, but today&amp;rsquo;s IDEs provide powerful, even LLM-assisted&#xA;code completion.&#xA;Writing basic error checks is straightforward for these tools.&#xA;The verbosity is most obvious when reading code, but tools might help here as well;&#xA;for instance an IDE with a Go language setting could provide a toggle switch to hide error handling&#xA;code.&#xA;Such switches already exist for other code sections such as function bodies.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;When debugging error handling code, being able to quickly add a &lt;code&gt;println&lt;/code&gt; or&#xA;have a dedicated line or source location for setting a breakpoint in a debugger is helpful.&#xA;This is easy when there is already a dedicated &lt;code&gt;if&lt;/code&gt; statement.&#xA;But if all the error handling logic is hidden behind a &lt;code&gt;check&lt;/code&gt;, &lt;code&gt;try&lt;/code&gt;, or &lt;code&gt;?&lt;/code&gt;, the code may have to&#xA;be changed into an ordinary &lt;code&gt;if&lt;/code&gt; statement first, which complicates debugging&#xA;and may even introduce subtle bugs.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;There are also practical considerations:&#xA;Coming up with a new syntax idea for error handling is cheap;&#xA;hence the proliferation of a multitude of proposals from the community.&#xA;Coming up with a good solution that holds up to scrutiny: not so much.&#xA;It takes a concerted effort to properly design a language change and to actually implement it.&#xA;The real cost still comes afterwards:&#xA;all the code that needs to be changed, the documentation that needs to be updated,&#xA;the tools that need to be adjusted.&#xA;Taken all into account, language changes are very expensive, the Go team is relatively small,&#xA;and there are a lot of other priorities to address.&#xA;(These latter points may change: priorities can shift, team sizes can go up or down.)&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;On a final note, some of us recently had the opportunity to attend&#xA;&lt;a href=&#34;https://cloud.withgoogle.com/next/25&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Next 2025&lt;/a&gt;,&#xA;where the Go team had a booth and where we also hosted a small Go Meetup.&#xA;Every single Go user we had a chance to ask was adamant that we should not change the&#xA;language for better error handling.&#xA;Many mentioned that the lack of specific error handling support in Go is most apparent&#xA;when coming freshly from another language that has that support.&#xA;As one becomes more fluent and writes more idiomatic Go code, the issue becomes much less important.&#xA;This is of course not a sufficiently large set of people to be representative,&#xA;but it may be a different set of people than we see on GitHub, and their feedback serves as yet another data point.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Of course, there are also valid arguments in favor of change:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Lack of better error handling support remains the top complaint in our user surveys.&#xA;If the Go team really does take user feedback seriously, we ought to do something about this eventually.&#xA;(Although there does not seem to be&#xA;&lt;a href=&#34;https://github.com/golang/go/discussions/71460#discussioncomment-11977299&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;overwhelming support&lt;/a&gt;&#xA;for a language change either.)&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Perhaps the singular focus on reducing the character count is misguided.&#xA;A better approach might be to make default error handling highly visible with a keyword&#xA;while still removing boilerplate (&lt;code&gt;err != nil&lt;/code&gt;).&#xA;Such an approach might make it easier for a reader (a code reviewer!) to see that an error&#xA;is handled, without &amp;ldquo;looking twice&amp;rdquo;, resulting in improved code quality and safety.&#xA;This would bring us back to the beginnings of &lt;code&gt;check&lt;/code&gt; and &lt;code&gt;handle&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;We don&amp;rsquo;t really know how much the issue is the straightforward syntactic verbosity of&#xA;error checking, versus the verbosity of good error handling:&#xA;constructing errors that are a useful part of an API and meaningful to developers and&#xA;end-users alike.&#xA;This is something we&amp;rsquo;d like to study in greater depth.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Still, no attempt to address error handling so far has gained sufficient traction.&#xA;If we are honestly taking stock of where we are, we can only admit that we&#xA;neither have a shared understanding of the problem,&#xA;nor do we all agree that there is a problem in the first place.&#xA;With this in mind, we are making the following pragmatic decision:&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;For the foreseeable future, the Go team will stop pursuing syntactic language changes&#xA;for error handling.&#xA;We will also close all open and incoming proposals that concern themselves primarily&#xA;with the syntax of error handling, without further investigation.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;The community has put tremendous effort into exploring, discussing, and debating these issues.&#xA;While this may not have resulted in any changes to error handling syntax, these efforts have&#xA;resulted in many other improvements to the Go language and our processes.&#xA;Maybe, at some point in the future, a clearer picture will emerge on error handling.&#xA;Until then, we look forward to focusing this incredible passion on new opportunities&#xA;to make Go better for everyone.&lt;/p&gt;&#xA;&lt;p&gt;Thank you!&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/generic-interfaces&#34;&gt;Generic interfaces&lt;/a&gt;&lt;br&gt;&#xA;          &#xA;        &#xA;        &#xA;          &#xA;            &lt;b&gt;Previous article: &lt;/b&gt;&lt;a href=&#34;/blog/tob-crypto-audit&#34;&gt;Go Cryptography Security Audit&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;      &#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 Cryptography Security Audit</title><id>tag:blog.golang.org,2013:blog.golang.org/tob-crypto-audit</id><link rel="alternate" href="https://go.dev/blog/tob-crypto-audit"></link><published>2025-05-19T00:00:00+00:00</published><updated>2025-05-19T00:00:00+00:00</updated><author><name>Roland Shoemaker and Filippo Valsorda</name></author><summary type="html">Go&amp;#39;s cryptography libraries underwent an audit by Trail of Bits.</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/tob-crypto-audit&#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 Cryptography Security Audit&lt;/h1&gt;&#xA;      &#xA;      &lt;p class=&#34;author&#34;&gt;&#xA;      Roland Shoemaker and Filippo Valsorda&lt;br&gt;&#xA;      19 May 2025&#xA;      &lt;/p&gt;&#xA;      &#xA;      &lt;p&gt;Go ships with a full suite of cryptography packages in the standard library to help developers build secure applications. Google recently contracted the independent security firm &lt;a href=&#34;https://www.trailofbits.com/&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;Trail of Bits&lt;/a&gt; to complete an audit of the core set of packages that are also validated as part of the &lt;a href=&#34;/doc/go1.24#fips140&#34;&gt;new native FIPS 140-3 module&lt;/a&gt;. The audit produced a single low-severity finding, in the legacy and unsupported &lt;a href=&#34;/doc/security/fips140#goboringcrypto&#34;&gt;Go+BoringCrypto integration&lt;/a&gt;, and a handful of informational findings. The full text of the audit report can be found &lt;a href=&#34;https://github.com/trailofbits/publications/blob/d47e8fafa7e3323e5620d228f2f3f3bf58ed5978/reviews/2025-03-google-gocryptographiclibraries-securityreview.pdf&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The scope of the audit included our implementations of key exchange (ECDH and post-quantum ML-KEM), digital signature (ECDSA, RSA, and Ed25519), encryption (AES-GCM, AES-CBC, and AES-CTR), hashing (SHA-1, SHA-2, and SHA-3), key derivation (HKDF and PBKDF2), and authentication (HMAC), as well as the cryptographic random number generator. Low-level big integer and elliptic curve implementations, with their delicate assembly cores, were included. Higher level protocols like TLS and X.509 were not in scope. Three Trail of Bits engineers worked on the audit for a month.&lt;/p&gt;&#xA;&lt;p&gt;We are proud of the security track record of the Go cryptography packages, and of the outcome of this audit, which is just one of many ways we gain assurance of the packages’ correctness. First, we aggressively limit their complexity, guided by the &lt;a href=&#34;/design/cryptography-principles&#34;&gt;Cryptography Principles&lt;/a&gt; which for example prioritize security over performance. Further, we &lt;a href=&#34;https://www.youtube.com/watch?v=lkEH3V3PkS0&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;thoroughly test them&lt;/a&gt; with an array of different techniques. We make a point of leveraging safe APIs even for internal packages, and naturally we can rely on the Go language properties to avoid memory management issues. Finally, we focus on readability to make maintenance easier and code review and audits more effective.&lt;/p&gt;&#xA;&lt;h2 id=&#34;one-low-severity-finding&#34;&gt;One low-severity finding&lt;/h2&gt;&#xA;&lt;p&gt;The only potentially exploitable issue, TOB-GOCL-3, has &lt;em&gt;low severity&lt;/em&gt;, meaning it had minor impact and was difficult to trigger. This issue has been fixed in Go 1.24.&lt;/p&gt;&#xA;&lt;p&gt;Crucially, TOB-GOCL-3 (&lt;a href=&#34;#cgo-memory-management&#34;&gt;discussed further below&lt;/a&gt;) concerns memory management in the &lt;a href=&#34;/doc/security/fips140#goboringcrypto&#34;&gt;legacy Go+BoringCrypto GOEXPERIMENT&lt;/a&gt;, which is not enabled by default and unsupported for use outside of Google.&lt;/p&gt;&#xA;&lt;h2 id=&#34;five-informational-findings&#34;&gt;Five informational findings&lt;/h2&gt;&#xA;&lt;p&gt;The remaining findings are &lt;em&gt;informational&lt;/em&gt;, meaning they do not pose an immediate risk but are relevant to security best practices. We addressed these in the current Go 1.25 development tree.&lt;/p&gt;&#xA;&lt;p&gt;Findings TOB-GOCL-1, TOB-GOCL-2, and TOB-GOCL-6 concern possible timing side-channels in various cryptographic operations. Of these three findings, only TOB-GOCL-2 affects operations that were expected to be constant time due to operating on secret values, but it only affects Power ISA targets (GOARCH ppc64 and ppc64le). TOB-GOCL-4 highlights misuse risk in an internal API, should it be repurposed beyond its current use case. TOB-GOCL-5 points out a missing check for a limit that is impractical to reach.&lt;/p&gt;&#xA;&lt;h2 id=&#34;timing-side-channels&#34;&gt;Timing Side-Channels&lt;/h2&gt;&#xA;&lt;p&gt;Findings TOB-GOCL-1, TOB-GOCL-2, and TOB-GOCL-6 concern minor timing side-channels. TOB-GOCL-1 and TOB-GOCL-6 are related to functions which we do not use for sensitive values, but could be used for such values in the future, and TOB-GOCL-2 is related to the assembly implementation of P-256 ECDSA on Power ISA.&lt;/p&gt;&#xA;&lt;h3 id=&#34;cryptoecdhcryptoecdsa-conversion-from-bytes-to-field-elements-is-not-constant-time-tob-gocl-1&#34;&gt;&lt;code&gt;crypto/ecdh,crypto/ecdsa&lt;/code&gt;: conversion from bytes to field elements is not constant time (TOB-GOCL-1)&lt;/h3&gt;&#xA;&lt;p&gt;The internal implementation of NIST elliptic curves provided a method to convert field elements between an internal and external representation which operated in variable time.&lt;/p&gt;&#xA;&lt;p&gt;All usages of this method operated on public inputs which are not considered secret (public ECDH values, and ECDSA public keys), so we determined that this was not a security issue. That said, we decided to &lt;a href=&#34;/cl/650579&#34;&gt;make the method constant time anyway&lt;/a&gt;, in order to prevent accidentally using this method in the future with secret values, and so that we don&amp;rsquo;t have to think about whether it is an issue or not.&lt;/p&gt;&#xA;&lt;h3 id=&#34;cryptoecdsa-p-256-conditional-negation-is-not-constant-time-in-power-isa-assembly-tob-gocl-2-cve-2025-22866&#34;&gt;&lt;code&gt;crypto/ecdsa&lt;/code&gt;: P-256 conditional negation is not constant time in Power ISA assembly (TOB-GOCL-2, CVE-2025-22866)&lt;/h3&gt;&#xA;&lt;p&gt;Beyond the &lt;a href=&#34;/wiki/PortingPolicy#first-class-ports&#34;&gt;first class Go platforms&lt;/a&gt;, Go also supports a number of additional platforms, including some less common architectures. During the review of our assembly implementations of various underlying cryptographic primitives, the Trail of Bits team found one issue that affected the ECDSA implementation on the ppc64 and ppc64le architectures.&lt;/p&gt;&#xA;&lt;p&gt;Due to the usage of a conditional branching instruction in the implementation of the conditional negation of P-256 points, the function operated in variable-time, rather than constant-time, as expected. The fix for this was relatively simple, &lt;a href=&#34;/cl/643735&#34;&gt;replacing the conditional branching instruction&lt;/a&gt; with a pattern we already use elsewhere to conditionally select the correct result in constant time. We assigned this issue CVE-2025-22866.&lt;/p&gt;&#xA;&lt;p&gt;To prioritize the code that reaches most of our users, and due to the specialized knowledge required to target specific ISAs, we generally rely on community contributions to maintain assembly for non-first class platforms. We thank our partners at IBM for helping provide review for our fix.&lt;/p&gt;&#xA;&lt;h3 id=&#34;cryptoed25519-scalarsetcanonicalbytes-is-not-constant-time-tob-gocl-6&#34;&gt;&lt;code&gt;crypto/ed25519&lt;/code&gt;: Scalar.SetCanonicalBytes is not constant time (TOB-GOCL-6)&lt;/h3&gt;&#xA;&lt;p&gt;The internal edwards25519 package provided a method to convert between an internal and external representation of scalars which operated in variable time.&lt;/p&gt;&#xA;&lt;p&gt;This method was only used on signature inputs to ed25519.Verify, which are not considered secret, so we determined that this was not a security issue. That said, similarly to the TOB-GOCL-1 finding, we decided to &lt;a href=&#34;/cl/648035&#34;&gt;make the method constant time anyway&lt;/a&gt;, in order to prevent accidentally using this method in the future with secret values, and because we are aware that people fork this code outside of the standard library, and may be using it with secret values.&lt;/p&gt;&#xA;&lt;h2 id=&#34;cgo-memory-management&#34;&gt;Cgo Memory Management&lt;/h2&gt;&#xA;&lt;p&gt;Finding TOB-GOCL-3 concerns a memory management issue in the Go+BoringCrypto integration.&lt;/p&gt;&#xA;&lt;h3 id=&#34;cryptoecdh-custom-finalizer-may-free-memory-at-the-start-of-a-c-function-call-using-this-memory-tob-gocl-3&#34;&gt;&lt;code&gt;crypto/ecdh&lt;/code&gt;: custom finalizer may free memory at the start of a C function call using this memory (TOB-GOCL-3)&lt;/h3&gt;&#xA;&lt;p&gt;During the review, there were a number of questions about our cgo-based Go+BoringCrypto integration, which provides a FIPS 140-2 compliant cryptography mode for internal usage at Google. The Go+BoringCrypto code is not supported by the Go team for external use, but has been critical for Google’s internal usage of Go.&lt;/p&gt;&#xA;&lt;p&gt;The Trail of Bits team found one vulnerability and one &lt;a href=&#34;/cl/644120&#34;&gt;non-security relevant bug&lt;/a&gt;, both of which were results of the manual memory management required to interact with a C library. Since the Go team does not support usage of this code outside of Google, we have chosen not to issue a CVE or Go vulnerability database entry for this issue, but we &lt;a href=&#34;/cl/644119&#34;&gt;fixed it in Go 1.24&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;This kind of pitfall is one of the many reasons that we decided to move away from the Go+BoringCrypto integration. We have been working on a &lt;a href=&#34;/doc/security/fips140&#34;&gt;native FIPS 140-3 mode&lt;/a&gt; that uses the regular pure Go cryptography packages, allowing us to avoid the complex cgo semantics in favor of the traditional Go memory model.&lt;/p&gt;&#xA;&lt;h2 id=&#34;implementation-completeness&#34;&gt;Implementation Completeness&lt;/h2&gt;&#xA;&lt;p&gt;Findings TOB-GOCL-4 and TOB-GOCL-5 concern limited implementations of two specifications, &lt;a href=&#34;https://csrc.nist.gov/pubs/sp/800/90/a/r1/final&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;NIST SP 800-90A&lt;/a&gt; and &lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc8018&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;RFC 8018&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;cryptointernalfips140drbg-ctr-drbg-api-presents-multiple-misuse-risks-tob-gocl-4&#34;&gt;&lt;code&gt;crypto/internal/fips140/drbg&lt;/code&gt;: CTR_DRBG API presents multiple misuse risks (TOB-GOCL-4)&lt;/h3&gt;&#xA;&lt;p&gt;As part of the &lt;a href=&#34;/doc/security/fips140&#34;&gt;native FIPS 140-3 mode&lt;/a&gt; that we are introducing, we needed an implementation of the NIST CTR_DRBG (an AES-CTR based deterministic random bit generator) to provide compliant randomness.&lt;/p&gt;&#xA;&lt;p&gt;Since we only need a small subset of the functionality of the NIST SP 800-90A Rev. 1 CTR_DRBG for our purposes, we did not implement the full specification, in particular omitting the derivation function and personalization strings. These features can be critical to safely use the DRBG in generic contexts.&lt;/p&gt;&#xA;&lt;p&gt;As our implementation is tightly scoped to the specific use case we need, and since the implementation is not publicly exported, we determined that this was acceptable and worth the decreased complexity of the implementation. We do not expect this implementation to ever be used for other purposes internally, and have &lt;a href=&#34;/cl/647815&#34;&gt;added a warning to the documentation&lt;/a&gt; that details these limitations.&lt;/p&gt;&#xA;&lt;h3 id=&#34;cryptopbkdf2-pbkdf2-does-not-enforce-output-length-limitations-tob-gocl-5&#34;&gt;&lt;code&gt;crypto/pbkdf2&lt;/code&gt;: PBKDF2 does not enforce output length limitations (TOB-GOCL-5)&lt;/h3&gt;&#xA;&lt;p&gt;In Go 1.24, we began the process of moving packages from &lt;a href=&#34;https://golang.org/x/crypto&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;golang.org/x/crypto&lt;/a&gt; into the standard library, ending a confusing pattern where first-party, high-quality, and stable Go cryptography packages were kept outside of the standard library for no particular reason.&lt;/p&gt;&#xA;&lt;p&gt;As part of this process we moved the &lt;a href=&#34;https://golang.org/x/crypto/pbkdf2&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;golang.org/x/crypto/pbkdf2&lt;/a&gt; package into the standard library, as crypto/pbkdf2. While reviewing this package, the Trail of Bits team noticed that we did not enforce the limit on the size of derived keys defined in &lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc8018&#34; rel=&#34;noreferrer&#34; target=&#34;_blank&#34;&gt;RFC 8018&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The limit is &lt;code&gt;(2^32 - 1) * &amp;lt;hash length&amp;gt;&lt;/code&gt;, after which the key would loop. When using SHA-256, exceeding the limit would take a key of more than 137GB. We do not expect anyone has ever used PBKDF2 to generate a key this large, especially because PBKDF2 runs the iterations at every block, but for the sake of correctness, we &lt;a href=&#34;/cl/644122&#34;&gt;now enforce the limit as defined by the standard&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h1 id=&#34;whats-next&#34;&gt;What’s Next&lt;/h1&gt;&#xA;&lt;p&gt;The results of this audit validate the effort the Go team has put into developing high-quality, easy to use cryptography libraries and should provide confidence to our users who rely on them to build safe and secure software.&lt;/p&gt;&#xA;&lt;p&gt;We’re not resting on our laurels, though: the Go contributors are continuing to develop and improve the cryptography libraries we provide users.&lt;/p&gt;&#xA;&lt;p&gt;Go 1.24 now includes a FIPS 140-3 mode written in pure Go, which is currently undergoing CMVP testing. This will provide a supported FIPS 140-3 compliant mode for all users of Go, replacing the currently unsupported Go+BoringCrypto integration.&lt;/p&gt;&#xA;&lt;p&gt;We are also working to implement modern post-quantum cryptography, introducing a ML-KEM-768 and ML-KEM-1024 implementation in Go 1.24 in the &lt;a href=&#34;/pkg/crypto/mlkem&#34;&gt;crypto/mlkem package&lt;/a&gt;, and adding support to the crypto/tls package for the hybrid X25519MLKEM768 key exchange.&lt;/p&gt;&#xA;&lt;p&gt;Finally, we are planning on introducing new easier to use high-level cryptography APIs, designed to reduce the barrier for picking and using high-quality algorithms for basic use cases. We plan to begin with offering a simple password hashing API that removes the need for users to decide which of the myriad of possible algorithms they should be relying on, with mechanisms to automatically migrate to newer algorithms as the state-of-the-art changes.&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/error-syntax&#34;&gt;[ On | No ] syntactic support for error handling&lt;/a&gt;&lt;br&gt;&#xA;          &#xA;        &#xA;        &#xA;          &#xA;            &lt;b&gt;Previous 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;        &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;      &#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>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;    &#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/tob-crypto-audit&#34;&gt;Go Cryptography Security Audit&lt;/a&gt;&lt;br&gt;&#xA;          &#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;    &#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;    &#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;    &#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;    &#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;    &#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;    &#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></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