【问题标题】:Is it possible to infer type parameters from what return values are assigned to?是否可以从分配的返回值推断类型参数?
【发布时间】:2022-07-17 14:21:20
【问题描述】:

假设我写了两个这样的函数:

func ToInterfaceSlice[T any](s []T) []interface{} {
    res := make([]interface{}, len(s))
    for i, v := range s {
        res[i] = v
    }
    return res
}

func FromInterfaceSlice[T any](s []interface{}) (res []T, err error) {
    res = make([]T, len(s))
    for i, v := range s {
        vt, ok := v.(T)
        if !ok {
            return nil, fmt.Errorf("%v (type=%T) doesn't fit the target type %T", v, v, res)
        }
        res[i] = vt
    }
    return
}

当我从输入参数解析类型时,我可以简单地使用

    var m = []int{1, 2, 3}
    fmt.Println(ToInterfaceSlice(m))

编译器知道Tint

但是,当我尝试从返回变量传递类型时

    var m []int
    m, _ = FromInterfaceSlice([]interface{}{1, 2, 3})
    fmt.Println(m)

编译器报错:

.\scratch.go:29:27: 无法推断 T

我必须在函数调用中显式传递类型:

    var m []int
    m, _ = FromInterfaceSlice[int]([]interface{}{1, 2, 3})
    fmt.Println(m)

当接收者变量不是接口时,从返回类型推断类型参数有什么困难吗?或者只是没有实施,甚至没有故意实施?

评论后更新#1

我知道a, b := GenericFunc() 不能引用返回值的类型。目前 Go 确实有“取决于”情况是否需要用户输入的显式实例化。

type Set[T comparable] map[T]struct{}

func NewSet[T comparable](eles ...T) Set[T] {
    s := make(Set[T])
    for _, ele := range eles {
        s[ele] = struct{}{}
    }
    return s
}

可以同时使用t := NewSet(1, 2, 3)t := NewSet[string](),但现在不能使用var t NewSet[float64] = NewSet(),因此

【问题讨论】:

  • 这个还没有实现,这个功能有问题/建议github.com/golang/go/issues/50285
  • 你对这种情况有什么期望:a, b, c := FunctionXXX(…)
  • @TiagoPeczenyj 应该是编译错误,无法推断变量a的类型。这些天 Go 确实有机会。

标签: go generics type-inference


【解决方案1】:

类型推断的当前规则是explicit。未考虑返回值的使用方式:

类型推断基于

  • 类型参数列表
  • 使用已知类型参数(如果有)初始化的替换映射 M
  • 普通函数参数的(可能为空)列表(仅在函数调用的情况下)

从 Go 1.18 开始,可能会简单地重写你的函数以接受所需类型的参数;这还有一个好处是不在函数体内隐藏分配:

func FromInterfaceSlice[T any](s []interface{}, dst []T) error {
    if len(s) != len(dst) {
        return errors.New("lengths don't match")
    }
    for i, v := range s {
        vt, ok := v.(T)
        if !ok {
            return nil, fmt.Errorf("%v (type=%T) doesn't fit the target type %T", v, v, res)
        }
        dst[i] = vt
    }
    return nil
}

并传入所需长度的目标切片:

func main() {
    src := []interface{}{1, 2, 3}
    m := make([]int, len(src))
    _ = FromInterfaceSlice(src, m)
    fmt.Println(m)
}

如果您不能或不想事先确定切片的长度,则剩下的是显式实例化:

var m []int
m, _ = FromInterfaceSlice[int]([]interface{}{1, 2, 3})
//                        ^^^ explicit type argument

此外,类型参数仍然无法通过 := 简写声明推断:

// what is m???
m, err := FromInterfaceSlice([]interface{}{1, 2, 3})

【讨论】:

  • 谢谢。到目前为止,显式实例化似乎是该案例最简单的方法。
猜你喜欢
  • 2017-10-15
  • 1970-01-01
  • 2020-12-26
  • 1970-01-01
  • 2016-07-23
  • 2019-01-07
  • 2020-02-28
  • 2019-12-06
  • 2022-01-06
相关资源
最近更新 更多