diff --git a/option.go b/option.go index 33d5135..bfdbdc2 100644 --- a/option.go +++ b/option.go @@ -252,14 +252,19 @@ func (o Option[T]) IsZero() bool { return true } + // A nil pointer can implement zeroer through a value-receiver method, + // which cannot be called without dereferencing the nil pointer. + rv := reflect.ValueOf(o.value) + if !rv.IsValid() || rv.Kind() == reflect.Ptr && rv.IsNil() { + return true + } + var v any = o.value if v, ok := v.(zeroer); ok { return v.IsZero() } - // a nil interface yields an invalid reflect.Value, on which IsZero panics - rv := reflect.ValueOf(o.value) - return !rv.IsValid() || rv.IsZero() + return rv.IsZero() } // MarshalText implements the encoding.TextMarshaler interface. diff --git a/option_nil_zero_test.go b/option_nil_zero_test.go new file mode 100644 index 0000000..3c89b64 --- /dev/null +++ b/option_nil_zero_test.go @@ -0,0 +1,23 @@ +package mo + +import ( + "testing" + "time" +) + +func TestOptionIsZeroNilZeroer(t *testing.T) { + var value *time.Time + if !Some(value).IsZero() { + t.Error("a nil time pointer should be zero") + } + if !Some[any](value).IsZero() { + t.Error("an interface holding a nil time pointer should be zero") + } + if !Some(&time.Time{}).IsZero() { + t.Error("a pointer to zero time should still use time.IsZero") + } + nonzero := time.Unix(1, 0) + if Some(&nonzero).IsZero() { + t.Error("a nonzero time pointer should not be zero") + } +}