Go 1.19 added an atomic.Pointer
type that's a generic type
In Go 1.18, the Go developers were careful to keep generic types
out of the standard library, to the point where they decided not
to have a 'constraints
' package of generics helpers in the standard
library, never mind generic versions
of slices and maps. As the Go 1.18 release notes say, all three packages were instead
put under golang.org/x/exp.
However, Go 1.19 turns out to have somewhat quietly changed that,
adding the standard library's first public generic type in sync/atomic's new atomic.Pointer[T any]
type.
The atomic.Pointer type is a type-safe atomic pointer to a *T,
with the type safety assured at compile time. You could previously
do atomic pointers through atomic.Value
, but that was only type safe
at runtime and might panic if you violated type safety (and storing
a nil in one was complex (but possible)). The appeal of atomic.Pointer
as a generic type is pretty obvious and it also seems unlikely that
you could design a better generic type or a better API than the one
it uses (which atomic.Value had from Go 1.4 and Go 1.17 (for
.CompareAndSwap()
)). I suspect that all of this adds up to why
it was considered safe to add to the standard library in Go 1.19,
and thus commit Go to having it there as part of the compatibility
guarantee.
The background and discussion for this is in issue $47141, Updating the Go memory model and issue 50860, sync/atomic: add typed atomic values. Also, the commit message has some interesting additional details. Apparently using these new atomic types, including atomic.Pointer[T], will generally compile down to the relevant assembly level atomic instructions.
I'm not sure why I overlooked that this was added in Go 1.19 until
now; I would have thought I'd pay more attention to the first generic
type in the standard library. The Go 1.19 release notes mention it vaguely in New atomic types but don't specifically
call out that it's a generic type; however, the Go 1.19 release
announcement does specially talk about
atomic.Pointer[T]
, making it pretty clear that this is a generic
type.
Sidebar: Storing a nil in an atomic.Value
As the documentation says, atomic.Value will panic if you call
.Store(nil)
. However, Store takes an 'interface{}
' (now called
'any
'), which means that you can pass it a concretely typed nil
and it will accept it:
var i int var p *int var m atomic.Value m.Store(&i) // Both of these are accepted m.Store(p) m.Store((*int)(nil))
If you look at it from the right direction, this is part of how
nil is typed in theory but not always in practice.
One way to put it is that if atomic.Value
is handed a pure nil,
the nil is functionally untyped, which means that atomic.Value can't
verify that it's the 'right' type of value (or use it to establish
the type).
|
|