Deeper cmp package in Go 1.21

Nahuel Costamagna

Nahuel Costamagna

· 5 min read
cmp package in Go 1.21 - Nahuel Costamagna

Introduction

Welcome to an insightful overview of the new 'cmp' package within Go version 1.21. In this post, we'll explore how these updates optimize value comparisons, making your code more efficient and intuitive. Stay tuned to discover the improvements that Go 1.21 brings with the 'cmp' package and how they can elevate your coding experience.

The first step is to import the cmp package

import (
    "cmp"
)

We can use this package to perform comparisons. It provides 2 methods: Compare and Less.

Compare

The 'Compare' method compares 2 values:

  • if the first value is less than the second value, the method will return -1
  • if both values are equal, the method will return 0
  • if the first value is greater than the second value, the method will return +1
func Compare[T Ordered](x, y T) int
// in this case, the result will be +1
cmp.Compare(3,2)

Less

We can use this method to check if the first value is less than the second value. In this case, the result will be true; otherwise, it will be false.

(if both values are equal, the result will be false)

func Less[T Ordered](x, y T) bool
// in this case, the result will be true
cmp.Less(1,2)

NaN values

We can't perform comparisons between NaN values:

NaN < NaN  // false
NaN > NaN  // false
NaN == NaN // false

However, something interesting is that with those methods, we can work with NaN values. In this case:

  • NaN is always less than any non-NaN value.
  • NaN is considered equal to a NaN value.

Go Source code

The following is the source code for Compare

func Compare[T Ordered](x, y T) int {
    xNaN := isNaN(x)
    yNaN := isNaN(y)
    if xNaN && yNaN {
      return 0
    }
    if xNaN || x < y {
      return -1
    }
    if yNaN || x > y {
      return +1
    }
    return 0
}

There is a private isNaN function that checks whether the value is NaN.

func isNaN[T Ordered](x T) bool {
    return x != x
}

When we compare if NaN is equal to NaN, the result will always be false. Likewise, if we compare that some NaN value is different from another NaN value, the result will be true.

The same applies to the Less method; they use this private function for performing comparisons.

func Less[T Ordered](x, y T) bool {
    return (isNaN(x) && !isNaN(y)) || x < y
}

Source code:here

Ordered Type

In Go version 1.21, we can use the Ordered Type instead of the external package constraints to make comparisons in Generics (ordered comparisons).

For example, in versions prior to 1.21, we used:

import (
    "fmt"
    "golang.org/x/exp/constraints"
)

func main(){
    oldOrderedValues(2, 4)
}

func oldOrderedValues[N constraints.Ordered](v1, v2 N){
    fmt.Println(v1, v2)
    fmt.Println(v1 !=v2)
    fmt.Println(v1 < v2)
    fmt.Println(v1 > v2)
}
Output:
2 4
true
true
false

however, in go 1.21 we can use:

import (
    "cmp"
    "fmt"
)

func main(){
    orderedValues(2,4)
}

func orderedValues[N cmp.Ordered](v1, v2 N){
    fmt.Println(v1, v2)
    fmt.Println(v1 !=v2)
    fmt.Println(v1 < v2)
    fmt.Println(v1 > v2)
}
Output:
2 4
true
true
false

Conclusion

To sum up, With the cmp package, we can perform comparisons, and it works really well with NaN values. Additionally, we can use the Ordered type instead of the external 'constraints' package for performing comparisons in Generics.

Nahuel Costamagna

Nahuel Costamagna

FullStack Developer & DevOps