skip to content

cmp.Ordered and Compare

The cmp package supplies the constraint every ordering helper is written against, plus a Compare that has to decide where NaN sits relative to every other float.

part ofGo (Golang)overview, primer and where to startread it →
on this pageshow

questions

5

What does Go's cmp.Compare return, and which types can you call it on?

level: juniorimportance: must knowfreq 42%

answer

  1. a three-way answer, not a bool
  2. minus one, zero, plus one
  3. the constraint lists ints, floats, string
  4. tilde means your named types count too
  5. structs and time.Time are outside it

basics

~10 s

cmp.Compare returns -1 if the first argument is less, 0 if they are equal, and +1 if it is greater. It accepts any cmp.Ordered type: integers, floats, strings, and named types built on them.

solid answer

~40 s

The signature is `func Compare[T cmp.Ordered](x, y T) int`, and it returns exactly one of three values: `-1`, `0`, `+1`. It is a generic function, so the comparison is the built-in `<` operator applied to the concrete type — no method, no interface, no boxing. `cmp.Ordered` is a constraint whose type set is every integer kind, `float32`, `float64` and `string`, each written with a `~` so a defined type such as `type Score float64` satisfies it too. `cmp.Less` is the same idea returning a `bool`. You reach for `cmp.Compare` when you need a single integer that expresses less/equal/greater — for example to chain tie-breaks through `cmp.Or`. Types whose order lives in a method, such as `time.Time`, are not in the set; those carry their own `Compare` method instead.

code

go · 4 lines
go
fmt.Println(cmp.Compare(2, 10))    // -1
fmt.Println(cmp.Compare("b", "a")) // 1
fmt.Println(cmp.Compare(3.5, 3.5)) // 0
fmt.Println(cmp.Less(2, 10))       // true

go deeper

for a junior

Be ready to state the three possible results and name the kinds of types cmp.Ordered covers: integers, floats and string. Knowing cmp.Less is the bool-shaped twin is enough at this level.

for a middle

Explain why the constraint is written with the tilde approximation, so your own named types work, and why a struct such as time.Time is excluded and carries its own Compare method instead.

for a senior

Show you know when the three-way int actually earns its keep over a plain comparison operator: when the result has to be combined across keys, and when floating-point edge cases must be defined rather than left false.

for a principal

Own the API consequence: constraining to cmp.Ordered gives the cheapest call site but closes the door on element types whose order lives in a method, and that door cannot be reopened without changing an exported signature.

## What the function is `cmp` is a very small standard-library package. It contains exactly four exported names: the constraint `cmp.Ordered`, and the functions `cmp.Compare`, `cmp.Less` and `cmp.Or`. The first three are about ordering; the fourth is about picking a first non-zero value. `cmp.Compare` has this shape: ```go func Compare[T cmp.Ordered](x, y T) int ``` It returns: - `-1` when `x` is less than `y` - `0` when `x` equals `y` - `+1` when `x` is greater than `y` Those are the only three values it can produce. It is not a C-style `strcmp` that returns "some negative number" such as the arithmetic difference — the documented result is exactly `-1`, `0` or `+1`, so it is safe to compare the result against those constants directly, though idiomatic Go tests `< 0`, `== 0`, `> 0`. ## What cmp.Ordered admits `cmp.Ordered` is a **constraint**, not an ordinary interface. Its type set is: - every signed integer kind: `int`, `int8`, `int16`, `int32`, `int64` - every unsigned integer kind: `uint`, `uint8`, `uint16`, `uint32`, `uint64`, `uintptr` - both floating-point kinds: `float32`, `float64` - `string` Each element is written with the `~` approximation, so the set contains not only those predeclared types but every defined type whose *underlying* type is one of them. That means your own `type Score float64` or `type UserID int64` can be passed to `cmp.Compare` with no extra work. What is **not** in the set is just as important: `bool`, complex numbers, pointers, channels, arrays, structs, slices and maps. `time.Time` is a struct, so `cmp.Compare(t1, t2)` does not compile — `time.Time` supplies its own `Compare` method (and `Before`/`After`) for that job. A type that wants to be ordered by a helper constrained to `cmp.Ordered` has to *be* a number or a string underneath; it cannot opt in by declaring a method. Because `cmp.Ordered` contains type-set elements rather than methods, it can only be used as a type constraint. Writing `var x cmp.Ordered` is a compile error — there is no such interface value at run time. ## Why a constraint and not an interface The reason the ordering helpers in the standard library are generic over `cmp.Ordered` rather than taking a `Comparable` interface is that you cannot add a method to `int`. Methods can only be declared on types defined in your own package, so an interface-based ordering API would force every caller to wrap `int`, `string` and `float64` in a named type before it could sort them. A constraint sidesteps that entirely: the compiler instantiates the function for the concrete type and emits the machine comparison directly, with no interface value to allocate and no dynamic dispatch on the hot path. The price is that the set is closed. If your element ordering needs more than `<` — a struct sorted by two fields, a case-insensitive string order, a locale-aware collation — a constraint cannot express it, and the API has to accept a comparison function instead. ## cmp.Compare versus cmp.Less versus the < operator `cmp.Less[T cmp.Ordered](x, y T) bool` answers the same question as `x < y` for a bool-shaped caller. `cmp.Compare` answers it in one integer, which is what you want when the result must be *combined* — for example folding several keys into a single decision with `cmp.Or`, where a `0` from the first key means "undecided, look at the next key". For integers and strings, `cmp.Compare(x, y)` and the raw operators agree completely. For floating-point values they do not: `cmp.Compare` defines an order for NaN and for negative zero, while the bare `<` and `==` operators report false for every comparison involving a NaN. That difference is the whole reason the function exists rather than each caller writing three lines of `if`. ## A small example ```go cmp.Compare(2, 10) // -1 cmp.Compare("b", "a") // +1 cmp.Compare(3.5, 3.5) // 0 cmp.Less(2, 10) // true ``` Type inference picks `T` from the arguments, so you almost never write the type argument explicitly. Both arguments must have the same type: `cmp.Compare(1, "a")` does not compile, and neither does `cmp.Compare(int32(1), int64(1))` without a conversion.

  • What does cmp.Less give you that cmp.Compare does not?
    `cmp.Less` returns a `bool`, which reads better inside an `if`. `cmp.Compare` returns an `int` so the answer can be combined — a `0` means "these keys are tied, consult the next one", which a `bool` cannot express. Both apply the same floating-point rules, so they never disagree.
  • Why can't you call cmp.Compare on two time.Time values?
    `cmp.Ordered`'s type set is the integer kinds, `float32`, `float64` and `string`. `time.Time` is a struct, so it is not in the set and the call does not compile. Its ordering lives in methods instead: `time.Time.Compare` returns the same -1/0/+1, and `Before`/`After` return bools.
  • Does cmp.Ordered accept a defined type such as type Score float64?
    Yes. Every element of the type set is written with the `~` approximation, which matches any type whose underlying type is that basic kind. So `Score`, `type UserID int64` and `type Name string` all satisfy `cmp.Ordered` without any declaration on your side.
  • Can you declare a variable of type cmp.Ordered?
    No. `cmp.Ordered` is an interface that contains type-set elements rather than methods, and such interfaces may only be used as constraints. `var x cmp.Ordered` is a compile error; the type only exists to bound a type parameter, never to hold a value at run time.

saying these in an interview costs you the question

  • Says cmp.Compare returns a bool
  • Thinks it returns the arithmetic difference of the two values
  • Believes cmp.Ordered includes structs such as time.Time
  • Thinks the element type must declare a Compare method
  • Assumes it works on numbers only, not on strings
open as a page

How does Go's cmp.Compare order a NaN float64, and what does it return for two NaNs?

level: middleimportance: should knowfreq 28%

basics

~10 s

cmp.Compare treats a NaN as smaller than every non-NaN value, so NaNs come first, and reports two NaNs as equal by returning 0. Negative and positive zero also compare equal, giving a total order.

open as a page

What does Go's cmp.Or return, and does it stop evaluating arguments at the first non-zero one?

level: middleimportance: should knowfreq 32%

basics

~10 s

cmp.Or returns the first argument that is not the zero value of its type, or the zero value if none is. It is an ordinary call, so every argument is evaluated first: no short-circuiting.

open as a page

A float64 comparator returns 0 whenever either score is NaN; ordering the same leaderboard twice yields different results. Why?

level: seniorimportance: should knowfreq 25%

basics

~20 s

Returning 0 for NaN pairs claims a NaN equals every score, so two differing scores both equal it. Equality is no longer transitive, the comparison is not a valid ordering, and the result depends on input order.

open as a page

cmp.Ordered or a comparator function: which shape do you commit to for an ordering API other teams import?

level: principalimportance: nice to knowfreq 20%

basics

~10 s

Make the comparator-taking function the primitive and add a cmp.Ordered-constrained wrapper on top. Adding an exported function later is compatible; changing one's parameters is not, so commit to the shape that excludes nobody.

open as a page