Go 1.19 added an atomic.Pointer type that's a generic type

September 1, 2022

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).

Written on 01 September 2022.
« ZFS DVA offsets are in 512-byte blocks on disk but zdb misleads you about them
An rsyslog(d) syslog forwarding setup for Grafana Loki (via Promtail) »

Page tools: View Source.
Search:
Login: Password:

Last modified: Thu Sep 1 22:48:40 2022
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.