如何在Golang中判断任意类型的变量是否为零?

13

因为不是所有类型都可以进行比较,例如切片。所以我们不能这样做。

var v ArbitraryType
v == reflect.Zero(reflect.TypeOf(v)).Interface()
4个回答

10

Go 1.13引入了reflect包中的Value.IsZero方法。以下是使用它来检查零值的方法:

if reflect.ValueOf(v).IsZero() {
    // v is zero, do something
}

除了基本类型之外,它也适用于Chan、Func、Array、Interface、Map、Ptr、Slice、UnsafePointer和Struct。

4

反射

这取决于你希望在接口 v 时产生什么行为(对于其他类型也是一样):

  • reflect.ValueOf(v).IsZero() 检查接口中包装的值是否为零
  • reflect.ValueOf(&v).Elem().IsZero() 检查接口变量是否为零

演示 (playground):

var v interface{} = ""
fmt.Println(reflect.ValueOf(v).IsZero())           // true, the empty string "" is zero
fmt.Println(reflect.ValueOf(&v).Elem().IsZero())   // false, the interface itself is not zero

这种差异是因为 ValueOf 的参数是 interface{}。如果传递一个接口,它将取消封装其中的动态值。Go 的 ValueOf 文档提醒:

ValueOf 返回一个新的 Value,其初始化为 接口 i 中存储的具体值

通过使用 ValueOf(&v),"接口 i 中存储的具体值" 将成为指向 v 的指针。然后你可以使用 Elem() 解引用得到原始值,最后检查 IsZero()
很多时候,你可能想要的是 reflect.ValueOf(&v).Elem().IsZero(),但你可能因情况而异。

Go 1.18 与泛型

使用泛型,你可以通过对 var zero T*new(T) 运用 == 运算符来检查变量是否为零值。类型参数必须是可比较的 (comparable 约束或可比较类型集)。
func IsZeroVar[T ~int64 | ~string](v T) bool {
    var zero T
    return v == zero
}

func IsZeroNew[T ~int64 | ~string](v T) bool {
    return v == *new(T)
}

如果类型参数不可比较,则必须退回到反射,如上所示。

使用泛型版本而不是反射,有什么好处呢?考虑到它引入了类型可比较的额外约束。 - Ryan Collingham
1
@RyanCollingham 如果你需要在任何类型上使用这个函数,通用版本实际上并没有提供优势。它只是一种替代反射的方式,当你计划比较的类型是可比较的或可以限制为可比较的类型时。 - blackgreen
回答自己的问题,但通用版本似乎快得多,快了几个数量级。 - Ryan Collingham

3
正如Peter Noyes所指出的那样,您只需要确保您没有比较不可比较的类型。幸运的是,使用reflect包非常简单:
func IsZero(v interface{}) (bool, error) {
    t := reflect.TypeOf(v)
    if !t.Comparable() {
        return false, fmt.Errorf("type is not comparable: %v", t)
    }
    return v == reflect.Zero(t).Interface(), nil
}

这里有一个示例使用


2
以下两个操作都会给我合理的结果(可能是因为它们是相同的吗?)
reflect.ValueOf(v) == reflect.Zero(reflect.TypeOf(v)))

reflect.DeepEqual(reflect.ValueOf(v), reflect.Zero(reflect.TypeOf(v)))

例如,各种整数0的类型和未初始化的struct都是“零”。

遗憾的是,空字符串和数组不是。而nil会导致异常。
如果需要,您可以特殊处理这些情况。


1
嗨,Rhythmic,感谢您的回复!实际上,“==”和“DeepEqual”是不同的。根据Go文档reflect.DeepEqual,DeepEqual适用于可比较和不可比较的变量。 - v1ct0r
我还不理解comparable,我会去查一下,谢谢。 - Rhythmic Fistman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接