Skip to content

Commit c40bffd

Browse files
elwinarianlancetaylor
authored andcommitted
reflect: add Value.IsZero
Fixes #7501 Change-Id: Iac7c79cd4b30a90b14ed84bf1eba758972232a6c Reviewed-on: https://go-review.googlesource.com/c/go/+/171337 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
1 parent 6d64dd7 commit c40bffd

File tree

2 files changed

+147
-0
lines changed

2 files changed

+147
-0
lines changed

Diff for: src/reflect/all_test.go

+107
Original file line numberDiff line numberDiff line change
@@ -1061,6 +1061,113 @@ func TestIsNil(t *testing.T) {
10611061
NotNil(fi, t)
10621062
}
10631063

1064+
func TestIsZero(t *testing.T) {
1065+
for i, tt := range []struct {
1066+
x interface{}
1067+
want bool
1068+
}{
1069+
// Booleans
1070+
{true, false},
1071+
{false, true},
1072+
// Numeric types
1073+
{int(0), true},
1074+
{int(1), false},
1075+
{int8(0), true},
1076+
{int8(1), false},
1077+
{int16(0), true},
1078+
{int16(1), false},
1079+
{int32(0), true},
1080+
{int32(1), false},
1081+
{int64(0), true},
1082+
{int64(1), false},
1083+
{uint(0), true},
1084+
{uint(1), false},
1085+
{uint8(0), true},
1086+
{uint8(1), false},
1087+
{uint16(0), true},
1088+
{uint16(1), false},
1089+
{uint32(0), true},
1090+
{uint32(1), false},
1091+
{uint64(0), true},
1092+
{uint64(1), false},
1093+
{float32(0), true},
1094+
{float32(1.2), false},
1095+
{float64(0), true},
1096+
{float64(1.2), false},
1097+
{math.Copysign(0, -1), false},
1098+
{complex64(0), true},
1099+
{complex64(1.2), false},
1100+
{complex128(0), true},
1101+
{complex128(1.2), false},
1102+
{complex(math.Copysign(0, -1), 0), false},
1103+
{complex(0, math.Copysign(0, -1)), false},
1104+
{complex(math.Copysign(0, -1), math.Copysign(0, -1)), false},
1105+
{uintptr(0), true},
1106+
{uintptr(128), false},
1107+
// Array
1108+
{Zero(TypeOf([5]string{})).Interface(), true},
1109+
{[5]string{"", "", "", "", ""}, true},
1110+
{[5]string{}, true},
1111+
{[5]string{"", "", "", "a", ""}, false},
1112+
// Chan
1113+
{(chan string)(nil), true},
1114+
{make(chan string), false},
1115+
{time.After(1), false},
1116+
// Func
1117+
{(func())(nil), true},
1118+
{New, false},
1119+
// Interface
1120+
{New(TypeOf(new(error)).Elem()).Elem(), true},
1121+
{(io.Reader)(strings.NewReader("")), false},
1122+
// Map
1123+
{(map[string]string)(nil), true},
1124+
{map[string]string{}, false},
1125+
{make(map[string]string), false},
1126+
// Ptr
1127+
{(*func())(nil), true},
1128+
{(*int)(nil), true},
1129+
{new(int), false},
1130+
// Slice
1131+
{[]string{}, false},
1132+
{([]string)(nil), true},
1133+
{make([]string, 0), false},
1134+
// Strings
1135+
{"", true},
1136+
{"not-zero", false},
1137+
// Structs
1138+
{T{}, true},
1139+
{T{123, 456.75, "hello", &_i}, false},
1140+
// UnsafePointer
1141+
{(unsafe.Pointer)(nil), true},
1142+
{(unsafe.Pointer)(new(int)), false},
1143+
} {
1144+
var x Value
1145+
if v, ok := tt.x.(Value); ok {
1146+
x = v
1147+
} else {
1148+
x = ValueOf(tt.x)
1149+
}
1150+
1151+
b := x.IsZero()
1152+
if b != tt.want {
1153+
t.Errorf("%d: IsZero((%s)(%+v)) = %t, want %t", i, x.Kind(), tt.x, b, tt.want)
1154+
}
1155+
1156+
if !Zero(TypeOf(tt.x)).IsZero() {
1157+
t.Errorf("%d: IsZero(Zero(TypeOf((%s)(%+v)))) is false", i, x.Kind(), tt.x)
1158+
}
1159+
}
1160+
1161+
func() {
1162+
defer func() {
1163+
if r := recover(); r == nil {
1164+
t.Error("should panic for invalid value")
1165+
}
1166+
}()
1167+
(Value{}).IsZero()
1168+
}()
1169+
}
1170+
10641171
func TestInterfaceExtraction(t *testing.T) {
10651172
var s struct {
10661173
W io.Writer

Diff for: src/reflect/value.go

+40
Original file line numberDiff line numberDiff line change
@@ -1071,6 +1071,46 @@ func (v Value) IsValid() bool {
10711071
return v.flag != 0
10721072
}
10731073

1074+
// IsZero reports whether v is a zero value for its type.
1075+
// It panics if the argument is invalid.
1076+
func (v Value) IsZero() bool {
1077+
switch v.kind() {
1078+
case Bool:
1079+
return !v.Bool()
1080+
case Int, Int8, Int16, Int32, Int64:
1081+
return v.Int() == 0
1082+
case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
1083+
return v.Uint() == 0
1084+
case Float32, Float64:
1085+
return math.Float64bits(v.Float()) == 0
1086+
case Complex64, Complex128:
1087+
c := v.Complex()
1088+
return math.Float64bits(real(c)) == 0 && math.Float64bits(imag(c)) == 0
1089+
case Array:
1090+
for i := 0; i < v.Len(); i++ {
1091+
if !v.Index(i).IsZero() {
1092+
return false
1093+
}
1094+
}
1095+
return true
1096+
case Chan, Func, Interface, Map, Ptr, Slice, UnsafePointer:
1097+
return v.IsNil()
1098+
case String:
1099+
return v.Len() == 0
1100+
case Struct:
1101+
for i := 0; i < v.NumField(); i++ {
1102+
if !v.Field(i).IsZero() {
1103+
return false
1104+
}
1105+
}
1106+
return true
1107+
default:
1108+
// This should never happens, but will act as a safeguard for
1109+
// later, as a default value doesn't makes sense here.
1110+
panic(&ValueError{"reflect.Value.IsZero", v.Kind()})
1111+
}
1112+
}
1113+
10741114
// Kind returns v's Kind.
10751115
// If v is the zero Value (IsValid returns false), Kind returns Invalid.
10761116
func (v Value) Kind() Kind {

0 commit comments

Comments
 (0)