go struct with generics 实现可比较
go struct with generics implementing comparable
考虑以下版本 go1.18beta2 的代码片段 linux/amd64
type Vector[T comparable] struct {
data_ []T
}
func (v *Vector[T]) Contains(e T) bool {
for _, x := range v.data_ {
if x == e {
return true
}
}
return false
}
func TestVector(t *testing.T) {
v2 := Vector[Vector[int]]{}
}
这不会编译并给出错误:“Vector[int] does not implement comparable”
仅仅是因为 Vector
没有定义相等运算符。但是,我找不到如何定义它们。
问题:是否不允许这种创建可比较结构的方法,为什么;还是文档还没写好?
语言规范预先声明并支持约束 comparable
。您不能“手动”使类型实现它。该文档在规范中可用(在 Type Constraints 下):
The predeclared interface type comparable denotes the set of all concrete (non-interface) types that are comparable. Specifically, a type T implements comparable if:
T
is not an interface type and T
supports the operations == and !=; or
T
is an interface type and each type in T
's type set implements comparable.
您的类型 Vector[T comparable]
不符合这些条件中的任何一个。它不是接口类型,并且它不支持相等操作,因为它的字段之一 data_ []T
由于是切片而无法比较 - 即使元素类型受 comparable
.
comparable
约束的目的实际上只是允许使用 ==
和 !=
运算符编写通用代码。如果一个类型在设计上是不可比较的,你就不能写这样的代码。即使 Vector
没有类型参数也是如此。
如果您的目标是实例化 Vector[Vector[T]]
并允许 Vector[T]
的实例之间进行相等性测试,您可能需要添加一个 Equal
方法来处理这个特定的用例——仅允许使用与接收器相同类型参数实例化的向量:
func (v *Vector[T]) Equal(e Vector[T]) bool {
// test equality in a way that makes sense for this type
}
值得一提的是,有一种方法可以使 Vector[T comparable]
具有可比性,即将 data_
字段更改为 pointer-to-slice:
type Vector[T comparable] struct {
data_ *[]T
}
现在使用 Vector[Vector[int]]
编译实例化。然而,除了使用结构文字 (playground) 进行初始化非常麻烦之外,它还带有指针比较的所有注意事项。更具体地说:
Two pointer values are equal if they point to the same variable or if both have value nil. Pointers to distinct zero-size variables may or may not be equal.
现在比较x == e
测试x
和e
中data_
字段中存储的内存地址相同。这可能会扭曲比较两个 Vector[T]
实例的语义——如果两个向量实例持有对同一切片的引用,那么说它们相等是否正确?可能是。这取决于您的程序想要做出的假设。就个人而言,我认为这实际上并不比使用单独的 Equal
方法 and/or 重新设计您的数据类型更好,但是像往常一样,YMMV。
另请注意,如果您实例化为 Vector[float64]
并比较 NaN
值,则比较将是错误的。
考虑以下版本 go1.18beta2 的代码片段 linux/amd64
type Vector[T comparable] struct {
data_ []T
}
func (v *Vector[T]) Contains(e T) bool {
for _, x := range v.data_ {
if x == e {
return true
}
}
return false
}
func TestVector(t *testing.T) {
v2 := Vector[Vector[int]]{}
}
这不会编译并给出错误:“Vector[int] does not implement comparable”
仅仅是因为 Vector
没有定义相等运算符。但是,我找不到如何定义它们。
问题:是否不允许这种创建可比较结构的方法,为什么;还是文档还没写好?
语言规范预先声明并支持约束 comparable
。您不能“手动”使类型实现它。该文档在规范中可用(在 Type Constraints 下):
The predeclared interface type comparable denotes the set of all concrete (non-interface) types that are comparable. Specifically, a type T implements comparable if:
T
is not an interface type andT
supports the operations == and !=; orT
is an interface type and each type inT
's type set implements comparable.
您的类型 Vector[T comparable]
不符合这些条件中的任何一个。它不是接口类型,并且它不支持相等操作,因为它的字段之一 data_ []T
由于是切片而无法比较 - 即使元素类型受 comparable
.
comparable
约束的目的实际上只是允许使用 ==
和 !=
运算符编写通用代码。如果一个类型在设计上是不可比较的,你就不能写这样的代码。即使 Vector
没有类型参数也是如此。
如果您的目标是实例化 Vector[Vector[T]]
并允许 Vector[T]
的实例之间进行相等性测试,您可能需要添加一个 Equal
方法来处理这个特定的用例——仅允许使用与接收器相同类型参数实例化的向量:
func (v *Vector[T]) Equal(e Vector[T]) bool {
// test equality in a way that makes sense for this type
}
值得一提的是,有一种方法可以使 Vector[T comparable]
具有可比性,即将 data_
字段更改为 pointer-to-slice:
type Vector[T comparable] struct {
data_ *[]T
}
现在使用 Vector[Vector[int]]
编译实例化。然而,除了使用结构文字 (playground) 进行初始化非常麻烦之外,它还带有指针比较的所有注意事项。更具体地说:
Two pointer values are equal if they point to the same variable or if both have value nil. Pointers to distinct zero-size variables may or may not be equal.
现在比较x == e
测试x
和e
中data_
字段中存储的内存地址相同。这可能会扭曲比较两个 Vector[T]
实例的语义——如果两个向量实例持有对同一切片的引用,那么说它们相等是否正确?可能是。这取决于您的程序想要做出的假设。就个人而言,我认为这实际上并不比使用单独的 Equal
方法 and/or 重新设计您的数据类型更好,但是像往常一样,YMMV。
另请注意,如果您实例化为 Vector[float64]
并比较 NaN
值,则比较将是错误的。