Go 泛型什么时候不需要波浪号?

When is the tilde not necessary in Go generics?

对于 Golang 的新泛型,我们有波浪号运算符 ~ ,它将匹配基础类型。 NOT 匹配基础类型在什么情况下有效?我试图理解为什么波浪号的当前行为不是默认行为。好像没必要同时支持。

比如,你为什么要写

interface { int }

而不是

interface { ~int }

编写一个严格到不能接受类似

的方法对你有什​​么好处
type MyInt int

为什么波浪号行为不是默认行为,因此该语言不需要其他运算符?

Why is the tilde behavior not the default, and thus the language would not require another operator?

因为如果近似值是无条件的默认值,则您无法表达您的多态函数需要 int 而不是 MyInt 这一事实。然后,您将不得不引入一个运算符,如 strict 并编写 %int。一无所获。

不使用 ~ 运算符意味着您只接受列出的确切类型。为什么这很重要?

您可能希望使用确切类型的值来设置其他变量,否则将需要类型转换。又因为俗话说“新类型,新方法集”。具有相同基础类型的新类型有自己的 method sets.

您可能需要该值的“原始”行为,如果设置了不同的方法,该行为可能会发生变化。

例如,假设您想像这样打印数字:

type Num interface{ ~int }

func foo[T Num](v T) {
    fmt.Println(v)
}

如果MyInt有一个String() string方法:

type MyInt int

func (m MyInt) String() string { return "bar" }

输出可能不是 foo() 想要的,因为 fmt 包检查打印值是否有 String() string 方法,如果有,它会被调用以获取它的 string 表示:

foo(1)
foo(MyInt(1))

这将输出(在 Go Playground 上尝试):

1
bar

如果只允许 int:

type Num interface{ int }

您仍然可以调用 foo() 并使用类型 MyInt 的值,使用类型 conversion:

foo(1)
x := MyInt(1)
foo(int(x))

并且输出将是 foo() 想要的,而不是 MyInt 想要的(在 Go Playground 上试试这个):

1
1

是的,如果 foo() 本身进行转换,这也是可能的,但这清楚地表明您想要一个纯 int,具有 int 的行为,而不是int 具有不同的自定义行为。

Why is the tilde behavior not the default

因为编写像 func Foo[T int](v T) 这样接受非 int 类型参数的函数会造成混淆并且在语义上不合理。那么接口约束中int的含义就不会和其他地方一样了。 (More on this discussion)


What benefit to you would it be to write a method that is so strict [...]

确实,如果约束仅包含 一个确切的 类型,则使用类型参数没有实际意义。如果约束的类型集的基数为 1,您应该只删除类型参数。

函数如:

func Foo[T int](v T)

只能用int实例化,所以它可以(而且应该!)简单地用常规参数编写:

func Foo(v int)

当类型集基数为 N 时,它包括单波浪号类型,但也包括联合,这使得编写 exhaustive 类型开关基本上是不可能的,因为使用 ~case 语句中不允许(还?):

func Foo[T ~int | ~string](v T) {
    switch t := any(v).(type) {
        case int: // ok
        case string: // ok

        // how to match other possible types then? 
    }
} 

在这种特殊情况下,只有在约束包含确切类型时才能编写详尽类型开关:

func Foo[T int | string](v T) {
    switch t := any(v).(type) {
        case int: // ok
        case string: // ok

        default:
            panic("should not occur")
    }
}

这在实践中不应该经常出现:如果你发现自己打开了类型参数,你应该问问自己函数是否真的需要泛型。但是,在设计代码时用例是相关的。