or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

cmp-core.mdcmp-options.mdcmp-path.mdcmpopts.mdindex.md
tile.json

cmp-options.mddocs/

Options and Customization

The cmp package provides a powerful option system for customizing comparison behavior through Comparers, Transformers, and Filters.

Import

import "github.com/google/go-cmp/cmp"

Option Types

Comparer

func Comparer(f interface{}) Option

Comparer returns an Option that determines whether two values are equal using a custom equality function.

Parameters:

  • f interface{} - Must be a function func(T, T) bool. Implicitly filtered to input values assignable to T. If T is an interface, f may be called with different concrete types that implement T.

Returns:

  • Option - Configuration option

Requirements: The equality function must be:

  • Symmetric: equal(x, y) == equal(y, x)
  • Deterministic: equal(x, y) always returns the same result
  • Pure: equal(x, y) does not modify x or y

Example - Approximate Float Comparison:

// Compare floats within tolerance
approxEqual := cmp.Comparer(func(x, y float64) bool {
    tolerance := 0.01
    return math.Abs(x-y) < tolerance
})

a := 1.0001
b := 1.0002
cmp.Equal(a, b, approxEqual) // returns true

Example - Custom Type Comparison:

type Version struct {
    Major, Minor, Patch int
}

// Compare versions by semantic version rules
versionComparer := cmp.Comparer(func(x, y Version) bool {
    return x.Major == y.Major &&
           x.Minor == y.Minor &&
           x.Patch == y.Patch
})

v1 := Version{1, 2, 3}
v2 := Version{1, 2, 3}
cmp.Equal(v1, v2, versionComparer) // returns true

Example - String Case-Insensitive Comparison:

caseInsensitive := cmp.Comparer(func(x, y string) bool {
    return strings.EqualFold(x, y)
})

cmp.Equal("Hello", "hello", caseInsensitive) // returns true

Transformer

func Transformer(name string, f interface{}) Option

Transformer returns an Option that applies a transformation function that converts values of one type to another before comparison.

Parameters:

  • name string - User-provided label for the transformation. Must be a valid Go identifier or qualified identifier. If empty, an arbitrary name is used. Appears in Diff output.
  • f interface{} - Must be a function func(T) R that converts values of type T to type R. Implicitly filtered to input values assignable to T. Must not mutate T.

Returns:

  • Option - Configuration option

Cycle Prevention: Implicit filter prevents transformer from being applied to its own output (e.g., when T and R are the same type). For more control, use cmpopts.AcyclicTransformer.

Example - Compare Structs by Specific Fields:

type Person struct {
    Name string
    Age  int
    ID   string // Don't care about ID in comparison
}

// Transform to only name and age
nameAndAge := cmp.Transformer("NameAndAge", func(p Person) struct{ Name string; Age int } {
    return struct{ Name string; Age int }{p.Name, p.Age}
})

p1 := Person{Name: "Alice", Age: 30, ID: "123"}
p2 := Person{Name: "Alice", Age: 30, ID: "456"}

cmp.Equal(p1, p2, nameAndAge) // returns true (IDs ignored)

Example - Normalize Before Comparison:

// Normalize strings by trimming and lowercasing
normalize := cmp.Transformer("Normalize", func(s string) string {
    return strings.ToLower(strings.TrimSpace(s))
})

cmp.Equal("  Hello  ", "hello", normalize) // returns true

Example - Sort Slices Before Comparison:

// Transform slice to sorted slice
sortInts := cmp.Transformer("SortInts", func(s []int) []int {
    sorted := append([]int(nil), s...)
    sort.Ints(sorted)
    return sorted
})

a := []int{3, 1, 2}
b := []int{1, 2, 3}
cmp.Equal(a, b, sortInts) // returns true

Ignore

func Ignore() Option

Ignore is an Option that causes all comparisons to be ignored.

Returns:

  • Option - Configuration option

Important: This value is intended to be combined with FilterPath or FilterValues. It is an error to pass an unfiltered Ignore option to Equal.

Example - Ignore Specific Fields:

type Record struct {
    ID        int
    Timestamp time.Time
    Data      string
}

// Ignore timestamp field
ignoreTimestamp := cmp.FilterPath(func(p cmp.Path) bool {
    return p.String() == "Timestamp"
}, cmp.Ignore())

r1 := Record{ID: 1, Timestamp: time.Now(), Data: "test"}
r2 := Record{ID: 1, Timestamp: time.Now().Add(time.Hour), Data: "test"}

cmp.Equal(r1, r2, ignoreTimestamp) // returns true (timestamps ignored)

Filters

FilterPath

func FilterPath(f func(Path) bool, opt Option) Option

FilterPath returns a new Option where opt is only evaluated if filter f returns true for the current Path in the value tree.

Parameters:

  • f func(Path) bool - Filter function that examines the current path. Must be symmetric (identical result regardless of whether missing value is from x or y).
  • opt Option - Option to conditionally apply. May be Ignore, Transformer, Comparer, Options, or previously filtered Option.

Returns:

  • Option - Filtered configuration option

Note: Called even if slice element or map entry is missing, providing opportunity to ignore such cases.

Example - Ignore Field by Path:

type Config struct {
    Name     string
    Internal struct {
        Secret string
        Debug  bool
    }
}

// Ignore Internal.Secret field
ignoreSecret := cmp.FilterPath(func(p cmp.Path) bool {
    return p.String() == "Internal.Secret"
}, cmp.Ignore())

c1 := Config{Name: "prod", Internal: struct{Secret string; Debug bool}{"key1", false}}
c2 := Config{Name: "prod", Internal: struct{Secret string; Debug bool}{"key2", false}}

cmp.Equal(c1, c2, ignoreSecret) // returns true

Example - Apply Comparer to Specific Path:

type Data struct {
    Exact  float64
    Approx float64
}

// Apply approximate comparison only to Approx field
approxForApproxField := cmp.FilterPath(
    func(p cmp.Path) bool {
        return p.String() == "Approx"
    },
    cmp.Comparer(func(x, y float64) bool {
        return math.Abs(x-y) < 0.01
    }),
)

d1 := Data{Exact: 1.5, Approx: 2.001}
d2 := Data{Exact: 1.5, Approx: 2.002}

cmp.Equal(d1, d2, approxForApproxField) // returns true

FilterValues

func FilterValues(f interface{}, opt Option) Option

FilterValues returns a new Option where opt is only evaluated if filter f returns true for the current pair of values being compared.

Parameters:

  • f interface{} - Must be a function func(T, T) bool. Implicitly returns false if either value is invalid or not assignable to T. Must be symmetric and deterministic. If T is an interface, may be called with different concrete types that implement T.
  • opt Option - Option to conditionally apply. May be Ignore, Transformer, Comparer, Options, or previously filtered Option.

Returns:

  • Option - Filtered configuration option

Example - Ignore Zero Values:

// Ignore comparison when both values are empty strings
ignoreEmptyStrings := cmp.FilterValues(func(x, y string) bool {
    return x == "" && y == ""
}, cmp.Ignore())

type User struct {
    Name     string
    Nickname string
}

u1 := User{Name: "Alice", Nickname: ""}
u2 := User{Name: "Alice", Nickname: ""}

cmp.Equal(u1, u2, ignoreEmptyStrings) // returns true

Example - Conditional Comparer:

// Only use approximate comparison for values > 1000
largeNumberApprox := cmp.FilterValues(func(x, y float64) bool {
    return x > 1000 || y > 1000
}, cmp.Comparer(func(x, y float64) bool {
    return math.Abs(x-y) / math.Max(x, y) < 0.01 // 1% tolerance
}))

cmp.Equal(1000.0, 1005.0, largeNumberApprox) // uses exact comparison, returns false
cmp.Equal(2000.0, 2010.0, largeNumberApprox) // uses approximate, returns true

Example - Type-Specific Handling:

import "time"

// Ignore time comparisons when either is zero
ignoreZeroTime := cmp.FilterValues(func(x, y time.Time) bool {
    return x.IsZero() || y.IsZero()
}, cmp.Ignore())

t1 := time.Time{} // zero time
t2 := time.Now()

type Event struct {
    Name      string
    Timestamp time.Time
}

e1 := Event{Name: "test", Timestamp: t1}
e2 := Event{Name: "test", Timestamp: t2}

cmp.Equal(e1, e2, ignoreZeroTime) // returns true (zero time ignored)

Combining Options

Multiple options can be combined by passing them all to Equal or Diff:

result := cmp.Equal(x, y,
    cmpopts.IgnoreUnexported(MyType{}),
    cmpopts.EquateEmpty(),
    cmp.Comparer(customCompare),
    cmp.Transformer("MyTransform", myTransform),
)

Options can also be grouped using the Options type:

type Options []Option

Example:

standardOpts := cmp.Options{
    cmpopts.IgnoreUnexported(Config{}),
    cmpopts.EquateEmpty(),
}

cmp.Equal(x, y, standardOpts)