我正在尝试在 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)
。有没有办法在不依赖反射或类型装箱/拆箱的情况下做到这一点?
这种混乱可能是有道理的,因为类型参数提案建议使用像您这样的代码,但最终成为 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