func (Attr) Equal
func (a Attr) Equal(b Attr) boolEqual reports whether a and b have equal keys and values.
That’s a pretty obvious, and opaque statement. How is equality determined? We have to look at the source to determine:
func (a Attr) Equal(b Attr) bool {
return a.Key == b.Key && a.Value.Equal(b.Value)
}
Okay, so it returns true if the keys are equal (that’s simple—they’re just strings) AND if the values are equal. But what does THAT mean?
Back to the source:
func (v Value) Equal(w Value) bool {
k1 := v.Kind()
k2 := w.Kind()
if k1 != k2 {
return false
}
switch k1 {
case KindInt64, KindUint64, KindBool, KindDuration:
return v.num == w.num
case KindString:
return v.str() == w.str()
case KindFloat64:
return v.float() == w.float()
case KindTime:
return v.time().Equal(w.time())
case KindAny, KindLogValuer:
return v.any == w.any // may panic if non-comparable
case KindGroup:
return slices.EqualFunc(v.group(), w.group(), Attr.Equal)
default:
panic(fmt.Sprintf("bad kind: %s", k1))
}
}
Now that is informative. It does a type-wise comparison first, then a direct value comparison. This has some interesting implications:
- Custom types compare differently.
type myBool bool
a := slog.Any("a", myBool(true))
b := slog.Any("a", true)
fmt.Println(a.Equal(b)) // Prints: false
Integers, even across Go types, compare directly:
a := slog.Any("a", int32(0))
b := slog.Any("a", int64(0))
fmt.Println(a.Equal(b)) // Prints: true
But the most interesting implication, at least to me, is that non-compariable values panic:
a := slog.Any("a", []int{1, 2, 3})
b := slog.Any("a", []int{1, 2, 3})
fmt.Println(a.Equal(b))
result:
panic: runtime error: comparing uncomparable type []int
goroutine 1 [running]:
log/slog.Value.Equal({{}, 0x4bb780?, {0x4bb780?, 0x1497ff76a048?}}, {{}, 0x10101cde30f7108?, {0x4bb780?, 0x1497ff76a060?}})
/usr/local/go-faketime/src/log/slog/value.go:437 +0x3cd
log/slog.Attr.Equal(...)
/usr/local/go-faketime/src/log/slog/attr.go:99
main.main()
/tmp/sandbox393141892/prog.go:13 +0x1d9
I suppose this makes sense, since equality is literally undefined for non-comparable values, so neither true nor false makes sense. But it’s also surprising, becuase it means you can’t test for equality on unknown slog attributes or values, without the risk of a panic.