英文:
How to know if a variable of arbitrary type is Zero in Golang?
问题
因为不是所有类型都可以进行比较,例如切片。所以我们不能这样做:
var v ArbitraryType
v == reflect.Zero(reflect.TypeOf(v)).Interface()
英文:
Because not all types are comparable, e.g. a slice. So we can't do this
var v ArbitraryType
v == reflect.Zero(reflect.TypeOf(v)).Interface()
答案1
得分: 8
Go 1.13在reflect包中引入了Value.IsZero方法。使用它可以检查零值,示例如下:
if reflect.ValueOf(v).IsZero() {
	// v是零值,执行某些操作
}
除了基本类型外,它还适用于Chan、Func、Array、Interface、Map、Ptr、Slice、UnsafePointer和Struct类型。
英文:
Go 1.13 introduced Value.IsZero method in reflect package. This is how you can check for zero value using it:
if reflect.ValueOf(v).IsZero() {
	// v is zero, do something
}
Apart from basic types, it also works for Chan, Func, Array, Interface, Map, Ptr, Slice, UnsafePointer, and Struct.
答案2
得分: 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
}
在这里可以看到一个使用示例。
英文:
As Peter Noyes points out, you just need to make sure you're not comparing a type which isn't comparable. Luckily, this is very straightforward with the reflect package:
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
}
See an example use here.
答案3
得分: 3
反射
这取决于当 v 是一个接口时你想要的行为(对于其他类型来说是一样的):
reflect.ValueOf(v).IsZero()检查接口中封装的值是否为零reflect.ValueOf(&v).Elem().IsZero()检查接口变量本身是否为零
演示(playground):
var v interface{} = ""
fmt.Println(reflect.ValueOf(v).IsZero())           // true,空字符串""是零值
fmt.Println(reflect.ValueOf(&v).Elem().IsZero())   // false,接口本身不是零值
这种差异是由于 ValueOf 的参数是 interface{}。如果传递一个接口,它将解封装其中的动态值。ValueOf 的 Go 文档提醒:
> 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)
}
如果类型参数不可比较,你必须退回到反射,如上所示。
英文:
Reflect
It depends of what behavior you want when v is an interface (for other types it's the same):
reflect.ValueOf(v).IsZero()checks whether the value boxed in the interface is zeroreflect.ValueOf(&v).Elem().IsZero()checks whether the interface variable is zero
Demonstration (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
The difference is due to the argument of ValueOf being interface{}. If you pass an interface, it will unbox the dynamic value boxed in it. The Go docs of ValueOf remind that:
> ValueOf returns a new Value initialized to the concrete value stored in the interface i
By using ValueOf(&v) instead "the concrete value stored in the interface i" will be a pointer to v. Then you dereference with Elem() to get the original value and finally check IsZero().
Most often than not, what you want is probably reflect.ValueOf(&v).Elem().IsZero(), though YMMV.
Go 1.18 and generics
With generics, you can check if a variable is zero value by using the == operator on a var zero T or *new(T). The type parameter must be comparable (comparable constraint or type set of comparable types).
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)
}
If the type param is not comparable, you must fall back to reflection, as shown above.
答案4
得分: 2
以下两个都给我合理的结果(可能是因为它们是相同的?)
reflect.ValueOf(v) == reflect.Zero(reflect.TypeOf(v)))
reflect.DeepEqual(reflect.ValueOf(v), reflect.Zero(reflect.TypeOf(v)))
例如,各种整数0的形式和未初始化的“struct”都是“zero”。
不幸的是,空字符串和数组不是。而nil会引发异常。
如果你愿意,你可以对这些进行特殊处理。
英文:
Both of the following give me reasonable results (probably because they're the same?)
reflect.ValueOf(v) == reflect.Zero(reflect.TypeOf(v)))
reflect.DeepEqual(reflect.ValueOf(v), reflect.Zero(reflect.TypeOf(v)))
e.g. various integer 0 flavours and uninitialized structs are "zero"
Sadly, empty strings and arrays are not. and nil gives an exception.
You could special case these if you wanted.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论