Go 中新波浪符号 ~ 的含义是什么?

What's the meaning of the new tilde token ~ in Go?

Go 引入新令牌 ~

~T means the set of all types with underlying type T

但是我没看懂,求大佬帮忙解释一下

下面是一个例子。

type Ordered interface {
      Integer | Float | ~string
}

不仅有新的标记,还有接口的新语法。除了方法约束之外,您还可以声明一个带有类型约束的接口。

要满足接口,类型必须同时满足方法约束和类型约束。

来自docs

An interface representing all types with underlying type int which implement the String method.

interface {
  ~int
  String() string
}

对于具有 int 的“底层类型”的类型,这意味着该类型采用以下形式:

type SomeType int

并且为了满足方法约束,必须声明一个具有指定签名的方法:

func (v SomeType) String() string {
  return fmt.Sprintf("%d", v)
}

在泛型提案中,~ 波浪号标记以 ~T 的形式使用,表示基础类型为 T.

的类型集

它在generics proposal中也被称为“近似”约束元素,这用通俗易懂的语言解释了它的好处:

Listing a single type is useless by itself. For constraint satisfaction, we want to be able to say not just int, but “any type whose underlying type is int”. [...] If a program uses type MyString string, the program can use the < operator with values of type MyString. It should be possible to instantiate [a function] with the type MyString.

如果您想要正式参考,语言规范已将 基础类型 的定义放在 its own section:

Each type T has an underlying type: If T is one of the predeclared boolean, numeric, or string types, or a type literal, the corresponding underlying type is T itself. Otherwise, T's underlying type is the underlying type of the type to which T refers in its type declaration.

这涵盖了非常常见的类型文字和其他具有绑定标识符的复合类型,或者您在预声明标识符上定义的类型,这是泛型提案中提到的情况:

// underlying type = struct literal -> itself -> struct { n int }
type Foo struct {
    n int
}

// underlying type = slice literal -> itself -> []byte
type ByteSlice []byte

// underlying type = predeclared -> itself -> int8
type MyInt8 int8

// underlying type = predeclared -> itself -> string
type MyString string

实际含义是类型集仅包含精确元素的接口约束不允许您自己定义的类型:

// hypothetical constraint without approximation elements
type ExactSigned interface {
    int | int8 | int16 | int32 | int64
}

// CANNOT instantiate with MyInt8
func echoExact[T ExactSigned](t T) T { return t }

// constraints.Signed uses approximation elements e.g. ~int8
// CAN instantiate with MyInt8
func echo[T constraints.Signed](t T) T { return t }

与其他约束元素一样,您可以在联合中使用近似元素,如在 constraints.Signed 中或在带有或不带有语法糖的匿名约束中。值得注意的是只有一个 approx 元素的语法糖是有效的:

// anonymous constraint
func echoFixedSize[T interface { ~int8 | ~int32 | ~int64 }](t T) T { 
    return t 
}

// anonymous constraint with syntactic sugar
func echoFixedSizeSugar[T ~int8 | ~int32 | ~int64](t T) T { 
    return t 
}

// anonymous constraint with syntactic sugar and one element
func echoFixedSizeSugarOne[T ~int8](t T) T { 
    return t 
}

正如上面预期的那样,近似元素的一个常见用例是需要具有方法的复合类型(切片、结构等)。在这种情况下,您必须绑定标识符:

// must bind identifier in order to declare methods
type ByteSeq []byte

func (b ByteSeq) DoSomething() {}

现在近似元素很方便允许实例化 ByteSeq:

// ByteSeq not allowed, or must convert func argument first
func foobar[T interface { []byte }](t T) { /* ... */ }


// ByteSeq allowed
func bazquux[T interface { ~[]byte }](t T) { /* ... */ }

func main() {
    b := []byte{0x00, 0x01}
    seq := ByteSeq{0x02, 0x03}

    foobar(b)           // ok
    foobar(seq)         // compiler error
    foobar([]byte(seq)) // ok, allows inference
    foobar[[]byte](seq) // ok, explicit instantiation, then can assign seq to argument type []byte

    bazquux(b)          // ok
    bazquux(seq)        // ok
}

注意:您不能将近似标记与类型参数一起使用:

// INVALID!
type AnyApprox[T any] interface {
    ~T
}