我正在尝试使用泛型来创建一个参数化类型,它可以是:
T, *T, T[], map[interface{}]interface{}
其中:T
是comparable
类型,但不是接口。
我曾试图通过受约束的类型集来制定它,但由于MisplacedTypeParam编译器错误而失败:
type myType[T comparable] interface {
T | *T | T[] | map[interface{}]interface{}
}
我在使用reflect
时也有一个问题,即获取reflect.Kind
或者接口的reflect.Type
将返回接口下的值的类型,这意味着我还没有弄清楚如何断言类型不是接口。
由此,我想知道表示这种类型的最佳替代方式是什么?
这是我正在进行的工作(https://github.com/mcwalrus/go-jitjson)及其主要部件:
type JitJSON[T any] struct {
data []byte
val *T
}
func (jit *JitJSON[T]) Unmarshal() (T, error) {
if jit.val != nil {
return *jit.val, nil
}
var val T
if jit.data == nil {
return val, nil
}
jit.val = &val
err := json.Unmarshal(jit.data, jit.val)
if err != nil {
return val, err
}
return *jit.val, nil
}
不幸的是,您不能**将其简单地作为类型约束来实现。因此,您必须依赖运行时检查。
您可以通过reflect
实现类型检查,但这在您的应用程序中可能是多余的,因为标准库json
在尝试封送或解组不兼容的数据类型时已经产生了适当的错误。
如果您确实想使用reflect
来检查参数化类型的确切类型,这里有一种简单但不正确的方法:
func f[T any]() {
var zero T
typ := reflect.TypeOf(zero)
fmt.Println(typ)
}
这适用于大多数类型,但接口类型除外,对于接口类型,它将始终将typ
报告为<nil>
。这是因为Go中接口值的分配方式。以下是正确的方法:
func f[T any]() {
var zero T
typ := reflect.TypeOf(&zero).Elem()
fmt.Println(typ)
}
这是必要的,因为接口值在运行时只携带它们引用的具体类型的类型信息。通过获取指向接口变量的指针,获取指针的类型,然后获取指针类型的元素,可以提取接口变量的实际类型。
**我似乎找不到一个简洁的解释来解释为什么不能做到这一点,但这里有一些困难:
comparable
不能在类型并集中使用- 无法组合类型约束
- 类型约束不能递归定义
- 不能否定类型约束
- 一般来说,没有与结构匹配的接口
从Go 1.19开始,约束comparable
只能由严格可比较的类型实现,即保证==
和!=
在运行时不会死机的类型。
因此,这确实排除了接口。然而,它也排除了其他不能严格比较但也不是接口的类型,例如[5]any
或struct{ Data any }
。
我不知道这对你来说是否足够好。否则,就无法用减法定义约束类型集,即用集表示法a\B。根据规范:
接口类型由接口元素列表指定。接口元素是方法或类型元素,其中类型元素是一个或多个类型项的并集类型术语是单个类型或单个基础类型。
如果您需要从计算中排除某些类型,您可能不得不返回到反射。还要考虑,如果您的目标是解组JSON,那么使用any
约束和*T
可能是可以接受的(==
检查两个指针是否指向同一变量(。毕竟,您应该将一个可寻址值传递给json.Unmarshal
:
// code simplified for illustrative purposes
func (jit *JitJSON[T]) Unmarshal() (T, error) {
var val T
err := json.Unmarshal(jit.data, &val) // needs a pointer anyway
if err != nil {
return val, err
}
jit.val = &val
return *jit.val, nil
}