最近写了个小 demo ,实现通过反射将结构体数组,转换成结构实现的接口类型的数组,到这部分没有问题。但是当打算从接口数组转换回结构结构体数组的时候,却转换失败,不知道原因是什么
代码见 https://go.dev/play/p/ajYUDEs6ssQ
如果不是通过反射,是可以通过类型断言将接口类型转换成底层的结构体,因为 go 的接口类型保存有底层结构的类型信息,但是通过反射却无法转换。
本来打算看看 go 反射的底层实现找找原因,但是水平有限没找到……有大手子可以帮忙解解惑吗?
1
Buges 2022-06-15 17:06:07 +08:00 via Android
因为转换成 Stringer 的时候 box 了一次,底层类型变成了 interface 类型。
加个检查,对 interface 解包 if fe.Kind() == reflect.Interface { fe = fe.Elem() } |
2
setsunakute 2022-06-15 17:06:14 +08:00
reflect.Value.Convert 支持 interface -> interface, 具体类型 -> interface 的转换, 不支持 interface 的转换
可以将 te.Set(fe.Convert(te.Type())) 修改为 te.Set(reflect.ValueOf(fe.Interface())), 这样就可以过了 |
3
setsunakute 2022-06-15 17:09:19 +08:00
不支持 interface -> 具体类型的转换, 补充一下
|
4
zmqiang OP @Buges 可以理解为,当*Data 传给 reflect.ValueOf 时,做了*Data->interface{}的转换,然后 convert 函数里,做的是 interface{} -> fmt.Stringer 的转换。这样 fmt.Stringer 的底层类型就变成了 interfaces{},*Data 的信息是包在了 interface{}里,fmt.Stringer 中是没法直接获取到?
|
5
zmqiang OP @setsunakute 哪里可以了解下为什么会有不支持 interface -> 具体类型转换?是 go 的类型系统设计的原因吗?
|
6
Mitt 2022-06-15 21:27:02 +08:00
fmt.Printf("%+v", ss) 可以看出,你两个值是 Nil
|
8
Mitt 2022-06-15 22:11:29 +08:00
@zmqiang #5
reflect/values.go if implements(dst, src) { if src.Kind() == Interface { return cvtI2I } return cvtT2I } reflect/types.go // implements reports whether the type V implements the interface type T. func implements(T, V *rtype) bool { if T.Kind() != Interface { return false } ... } 我猜测是这样的,Convert 的时候两个 Type 进行转换,前者是 fmt.Stringer 后者是 Data, Data => fmt.Stringer 可以因为实现了接口,但 fmt.Stringer => Data 不行, 如果两个被 Interface 抹掉了类型就会取 ValueType, 那两个底层 ValueType 都是 *Data 所以可以 |
9
SingeeKing 2022-06-15 22:27:30 +08:00 via iPhone
原理上面已经说的差不多了,提醒下范型已经发布可用了,op 这个 demo 正好就是范型的用处
|
10
Buges 2022-06-15 23:06:33 +08:00 via Android
@zmqiang interface 是一个 fat pointer ,一个指向值,一个指向类型。正常情况下你把某个类型赋值给 interface (隐式 box )、或把一个 interface 转换成另一个 interface 是不会改变它的具体值和类型的。而 reflect.ValueOf 则是把一个 interface 里的值拿出来,并提供一些 API 。
但是你 Convert 的时候把一个具体类型转换成 interface ,这里是对其进行了一次原地打包的。 转换前: ( Data 值,Data 类型) 转换后: ( Stringer 值,Stringer 类型) 其中 Stringer 值为 ( Data 值,Data 类型) |
11
zmqiang OP @SingeeKing 确实这个老问题用泛型解决正好,已经准备将项目最低的版本设置为 1.18 了
|