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测试xedata_字段中存储的内存地址相同。这可能会扭曲比较两个 Vector[T] 实例的语义——如果两个向量实例持有对同一切片的引用,那么说它们相等是否正确?可能是。这取决于您的程序想要做出的假设。就个人而言,我认为这实际上并不比使用单独的 Equal 方法 and/or 重新设计您的数据类型更好,但是像往常一样,YMMV。

另请注意,如果您实例化为 Vector[float64] 并比较 NaN 值,则比较将是错误的。