【问题标题】:Unioning an interface with a type in golang在 golang 中将接口与类型联合
【发布时间】:2022-06-16 07:12:29
【问题描述】:

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

import (
    "fmt"
)

type String interface {
    ~string | fmt.Stringer
}

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

【问题讨论】:

  • 类型字符串接口 { fmt.Stringer /n ~string }
  • @Para 我认为这行不通,因为这需要任何实现 String 的东西都继承自 string 并实现 fmt.Stringer

标签: go generics


【解决方案1】:

这种混淆可能是有道理的,因为类型参数提案 suggests code like yours 最终成为 Go 1.18 中的实现限制。

specs 和 Go 1.18 发行说明中提到了它。规格是规范性参考:

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

还有一个有点广泛的 explanation 说明为什么它没有包含在 Go 1.18 版本中。 tl;dr 正在简化联合类型集的计算(尽管在 Go 1.18 中,类型参数的方法集也不是隐式计算的......)。

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

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

对于更广泛的情况,当参数可以采用string(没有~)和fmt.Stringer 的确切类型时,在colm.anseo 的答案中键入断言或开关就可以正常工作。对于~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")
}

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

【讨论】:

  • 这是对问题的一个很好的解释。从我的角度来看,目标介于您建议的修复和反思之间。我想从实现fmt.Stringer 的任何类型或字符串本身生成一个字符串,同时保持强大的编译时类型检查,但似乎这对于 go1.18 是不可能的。不管怎样,谢谢你的详细回答。
  • @Woody1193 将来他们可能会取消此限制。我从 Go 问题跟踪器链接的评论暗示 unions-with-methods 的实现是可行的。在开关中允许使用波浪号类型也会有所帮助,这也可能在未来被允许。在 Go 1.18 中,一切都是保守的,以避免在出现关键设计缺陷的情况下把事情搞砸。类型安全的替代方法可能是声明 stringLike 未导出并导出访问器 String[S ~string](v S)Stringer(s fmt.Stringer) 调用 stringLike
  • 没错,这很好
  • 仅供参考:要从 reflect.Value 中获取 string,您知道它是“kind”字符串,您可以简单地:r.String()
【解决方案2】:

泛型——理论上允许使用多种类型——在编译时选择一个具体的类型。接口允许在运行时 使用多种类型。您希望同时将这两者结合起来 - 不幸的是,这是不可能的。


不使用反射最接近的方法是使用运行时类型断言:

func StringLike(v any) string {

    if s, ok := v.(string); ok {
        return s
    }

    if s, ok := v.(fmt.Stringer); ok {
        return s.String()
    }

    panic("non string invalid type")
}

https://go.dev/play/p/p4QHuT6R8yO

【讨论】:

  • 这也是我最终得出的解决方案。我希望 Golang 泛型能够提供这个功能;也许他们会在以后的版本中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-02
  • 1970-01-01
相关资源
最近更新 更多