英文:
Is it possible to infer type parameters from what return values are assigned to?
问题
假设我写了两个这样的函数:
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))
编译器知道T
是int
。
然而,当我尝试从返回变量中传递类型时:
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()
,因为这样会导致问题。
英文:
Suppose I wrote two functions like this:
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
}
When I parse type from the input parameters, I can simply use
var m = []int{1, 2, 3}
fmt.Println(ToInterfaceSlice(m))
The compiler knows the T
is int
.
However when I try passing type from the return variables
var m []int
m, _ = FromInterfaceSlice([]interface{}{1, 2, 3})
fmt.Println(m)
The compiler gives error:
> .\scratch.go:29:27: cannot infer T
I must explicitly pass the type in the function call:
var m []int
m, _ = FromInterfaceSlice[int]([]interface{}{1, 2, 3})
fmt.Println(m)
Is there anything hard to infer type parameters from return type when the receiver vars are not interface? Or just not implemented, even not to implement on purpose?
Update #1 after the comment
I do know a, b := GenericFunc()
cannot refer the type of returned value. Currently Go does have "it depends" case whether requires the explicit instantiation or not from the user input.
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
}
It's okay to use both t := NewSet(1, 2, 3)
and t := NewSet[string]()
, but not var t NewSet[float64] = NewSet()
now because of this
答案1
得分: 1
当前的类型推断规则是显式的。不考虑如何使用返回值:
类型推断基于
- 类型参数列表
- 一个用已知类型参数初始化的替换映射 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})
// ^^^ 显式类型参数
此外,类型参数在使用 :=
简短声明时仍然无法推断出来:
// m 是什么类型???
m, err := FromInterfaceSlice([]interface{}{1, 2, 3})
英文:
The current rules for type inference are explicit. How the return values are used is not taken into account:
> Type inference is based on
>
> - a type parameter list
> - a substitution map M initialized with the known type arguments, if any
> - a (possibly empty) list of ordinary function arguments (in case of a function call only)
As of Go 1.18 might simply rewrite your function to accept an argument of the required type; this has also the benefit of not hiding allocations inside the function body:
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
}
And pass in a destination slice with the required length:
func main() {
src := []interface{}{1, 2, 3}
m := make([]int, len(src))
_ = FromInterfaceSlice(src, m)
fmt.Println(m)
}
If you can't or don't want to determine the slice's length beforehand, you are left with explicit instantiation:
var m []int
m, _ = FromInterfaceSlice[int]([]interface{}{1, 2, 3})
// ^^^ explicit type argument
<hr>
Also the type parameters are still not inferrable with :=
shorthand declaration:
// what is m???
m, err := FromInterfaceSlice([]interface{}{1, 2, 3})
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论