[Go 1.22](https://go.dev/doc/go1.22) has been released for a couple of months as of this writing. It’s long past time to wrap up [my series on what I worked on for 1.22](/series/go-news/). Sorry for the long delay, I’ve been busy with life stuff. Be sure to catch up on my posts about [reflect.TypeFor](/post/2024/golang-reflect-type-for/) and [slices.Concat](/post/2024/golang-slices-concat/) if you missed those. The final function I [proposed](https://github.com/golang/go/issues/60204) and [implemented](https://go-review.googlesource.com/c/go/+/504883) for Go 1.22 is [cmp.Or](https://pkg.go.dev/cmp#Or). On [Go Time](/post/2024/go-time-podcast-122/), I called it “the hidden gem of 1.22”. It’s a simple function with a lot of potential uses and a surprisingly long backstory. First let’s take a look at the code: ```go // Or returns the first of its arguments that is not equal to the zero value. // If no argument is non-zero, it returns the zero value. func Or[T comparable](vals ...T) T { var zero T for _, val := range vals { if val != zero { return val } } return zero } ``` As is usual for my contributions to Go, it’s very short and simple. It just compares its arguments and returns the first one that isn’t `0` or `nil` or `""` or whatever the zero value is for its type. --- How do you use it?   The primary use for `cmp.Or` is for taking strings and returning the first one that isn’t blank. For example, searching public open source Go repositories, I found a lot of code online that tries to fetch an environmental variable but return a default value if it’s blank. With `cmp.Or`, this would look like `cmp.Or(os.Getenv("SOME_VARIABLE"), "default")`. It also works with numbers and pointers. Here are a handful of actual uses from [a real codebase of mine](https://github.com/spotlightpa/almanack): ```go body := cmp.Or(page.Body, rawContent) name := cmp.Or(jwt.Username(), "Almanack") credits = append(credits, cmp.Or(credit.Name, credit.Byline)) metadata.InternalID = cmp.Or( xhtml.InnerText(rows.Value("slug")), xhtml.InnerText(rows.Value("internal id")), metadata.InternalID, ) scope.SetTag("username", cmp.Or(userinfo.Username(), "anonymous")) currentUl = cmp.Or( xhtml.Closest(currentUl.Parent, xhtml.WithAtom(atom.Ul)), currentUl, ) ``` As you can see, most uses just look at strings to provide a fallback value, but the final example is looking for a non-nil `*html.Node`. The other major use for `cmp.Or` is to use with [cmp.Compare](https://pkg.go.dev/cmp#Compare) to create [multipart comparisons](https://pkg.go.dev/cmp#example-Or-Sort): ```go type Order struct { Product string Customer string Price float64 } orders := []Order{ {"foo", "alice", 1.00}, {"bar", "bob", 3.00}, {"baz", "carol", 4.00}, {"foo", "alice", 2.00}, {"bar", "carol", 1.00}, {"foo", "bob", 4.00}, } // Sort by customer first, product second, and last by higher price slices.SortFunc(orders, func(a, b Order) int { return cmp.Or( cmp.Compare(a.Customer, b.Customer), cmp.Compare(a.Product, b.Product), cmp.Compare(b.Price, a.Price), ) }) ``` ``` foo alice 2.00 foo alice 1.00 bar bob 3.00 foo bob 4.00 bar carol 1.00 baz carol 4.00 ``` Note that because `cmp.Or` cannot do short-circuit evaluation, this will compare the product name and price of each item, even if the customer name is different, which makes doing so redundant. --- The road that led to `cmp.Or` is long. All the way back in 2016, [Stephen Kampmann](https://github.com/abbgrade) proposed [strings.First](https://github.com/golang/go/issues/14423), but this was before the modern Go proposal system, so the proposal wasn’t actually evaluated. In 2020, I [proposed a new operator](https://github.com/golang/go/issues/37165), `??`, that would work like `cmp.Or` but with short circuiting. It could be used like `port := os.Getenv("PORT") ?? DefaultPort`. [Ian Lance Taylor](https://github.com/ianlancetaylor) [noted at the time](https://github.com/golang/go/issues/37165#issuecomment-587931923) that if Go ever got generics, this could be implemented (without short-circuiting) as a generic helper function. Shortly after opening the issue for `??`, I ended up writing a [stringutils.First](https://github.com/spotlightpa/almanack/commit/baed3c2406a8fd0c93b9a8f6ce214ef8c7b34a9a) helper function for my own personal use, and I soon found myself using it everywhere in my code. When generics were finally added to beta versions of Go in 2021, I [wrote a package called truthy](https://github.com/earthboundkid/truthy/commit/c6827510aa9eaf8028461dca07a40f849cedcdf0) that uses [reflect.Value.IsZero()](https://pkg.go.dev/reflect#Value.IsZero) to report whether any value is zero, but I found that using reflection caused allocations (this was eventually improved) and was 50x slower than a normal comparison (this has improved to only being 25x in Go 1.22). Using just generics with `comparable` like `cmp.Or` and not reflection has about a 2x penalty in the current version of Go. In 2022, I made [a proposal for a generic universal zero value](https://github.com/golang/go/issues/52307) that was closed as a duplicate of an existing discussion, but Russ Cox made [essentially the same proposal](https://github.com/golang/go/issues/61372) in 2023 and it was accepted. According to the proposal, the built in constant `zero` would be available in generic functions for return or comparison. However, after the proposal was accepted, [it was retracted](https://github.com/golang/go/issues/61372#issuecomment-1731525247) due to continued community push back against the idea of having both `nil` and `zero` in the language. I still think this is a shame and hope that someday `cmp.Or` can be made fully generic and work for any type, but for now, it only works for `comparable` types. The [crosseyed `*new(T)`](https://mastodon.social/@carlmjohnson/109553120280035157) lives on… Initially, I [proposed just to add `strings.First`](https://github.com/golang/go/issues/60204) to the standard library, but in the course of the discussion, several commenters preferred a generic version of the function and Russ Cox [proposed the name `cmp.Or`](https://github.com/golang/go/issues/60204#issuecomment-1581245334), which was ultimately accepted. So that wraps up my work on Go 1.22! Come back this summer for the story of [reflect.Value.Seq()](https://github.com/golang/go/issues/66056).