将接口与 golang 中的类型联合起来

Woo*_*193 6 generics go

我正在尝试在 Golang 中实现一些缓存函数,但我希望它们对字符串和实现该接口的其他对象都有效Stringer。我正在尝试使用 Golang 泛型,这是我到目前为止所做的:

import (
    "fmt"
)

type String interface {
    ~string | fmt.Stringer
}
Run Code Online (Sandbox Code Playgroud)

但是,这会产生错误cannot use fmt.Stringer in union (fmt.Stringer contains methods)。有没有办法在不依赖反射或类型装箱/拆箱的情况下做到这一点?

bla*_*een 5

这种混乱可能是有道理的,因为类型参数提案建议使用像您这样的代码,但最终成为 Go 1.18 中的实现限制。

它在规范和 Go 1.18 发行说明中提到。规格为规范参考:

实现限制:联合(具有多个术语)不能包含预先声明的标识符或指定方法的接口,也不能包含指定方法的comparable嵌入或接口。comparable

还有一个关于为什么 Go 1.18 版本中没有包含此内容的详细解释。tl;dr 正在简化联合类型集的计算(尽管在 Go 1.18 中,类型参数的方法集也不是隐式计算的......)。

还要考虑一下,无论有没有这个限制,除了传递给使用反射的函数之外,您可能不会获得任何有用的东西T。要调用方法,~string | fmt.Stringer您仍然必须进行类型断言或类型切换。

请注意,如果此类约束的目的只是打印字符串值,则可以仅使用fmt.Sprint,它使用反射。

对于更广泛的情况,当参数可以采用精确类型string(不带~) 和时,类型断言或 switch 就像 colm.anseo 的答案一样工作得很好fmt.Stringer。对于像这样的近似,~string 您无法详尽地处理所有可能的术语,因为这些类型集实际上是无限的。所以你又回到了反思。更好的实现可能是:

func StringLike(v any) string {
    // switch exact types first
    switch s := v.(type) {
    case fmt.Stringer:
        return s.String()

    case string:
        return s
    }

    // handle the remaining type set of ~string
    if r := reflect.ValueOf(v); r.Kind() == reflect.String {
        return r.String()
    }

    panic("invalid type")
}
Run Code Online (Sandbox Code Playgroud)

游乐场:https://go.dev/play/p/-wzo2KPKzWZ