无法通过反射将 interface{}转换成结构题的问题

2022-06-15 16:17:53 +08:00
 zmqiang

最近写了个小 demo ,实现通过反射将结构体数组,转换成结构实现的接口类型的数组,到这部分没有问题。但是当打算从接口数组转换回结构结构体数组的时候,却转换失败,不知道原因是什么

代码见 https://go.dev/play/p/ajYUDEs6ssQ

如果不是通过反射,是可以通过类型断言将接口类型转换成底层的结构体,因为 go 的接口类型保存有底层结构的类型信息,但是通过反射却无法转换。

本来打算看看 go 反射的底层实现找找原因,但是水平有限没找到……有大手子可以帮忙解解惑吗?

2067 次点击
所在节点    Go 编程语言
11 条回复
Buges
2022-06-15 17:06:07 +08:00
因为转换成 Stringer 的时候 box 了一次,底层类型变成了 interface 类型。
加个检查,对 interface 解包
if fe.Kind() == reflect.Interface {
fe = fe.Elem()
}
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())), 这样就可以过了
setsunakute
2022-06-15 17:09:19 +08:00
不支持 interface -> 具体类型的转换, 补充一下
zmqiang
2022-06-15 17:44:42 +08:00
@Buges 可以理解为,当*Data 传给 reflect.ValueOf 时,做了*Data->interface{}的转换,然后 convert 函数里,做的是 interface{} -> fmt.Stringer 的转换。这样 fmt.Stringer 的底层类型就变成了 interfaces{},*Data 的信息是包在了 interface{}里,fmt.Stringer 中是没法直接获取到?
zmqiang
2022-06-15 18:12:42 +08:00
@setsunakute 哪里可以了解下为什么会有不支持 interface -> 具体类型转换?是 go 的类型系统设计的原因吗?
Mitt
2022-06-15 21:27:02 +08:00
fmt.Printf("%+v", ss) 可以看出,你两个值是 Nil
Mitt
2022-06-15 21:35:46 +08:00
@Mitt #6 哦不是,我的问题
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 所以可以
SingeeKing
2022-06-15 22:27:30 +08:00
原理上面已经说的差不多了,提醒下范型已经发布可用了,op 这个 demo 正好就是范型的用处
Buges
2022-06-15 23:06:33 +08:00
@zmqiang interface 是一个 fat pointer ,一个指向值,一个指向类型。正常情况下你把某个类型赋值给 interface (隐式 box )、或把一个 interface 转换成另一个 interface 是不会改变它的具体值和类型的。而 reflect.ValueOf 则是把一个 interface 里的值拿出来,并提供一些 API 。
但是你 Convert 的时候把一个具体类型转换成 interface ,这里是对其进行了一次原地打包的。
转换前:
( Data 值,Data 类型)
转换后:
( Stringer 值,Stringer 类型)
其中 Stringer 值为 ( Data 值,Data 类型)
zmqiang
2022-06-16 11:21:14 +08:00
@SingeeKing 确实这个老问题用泛型解决正好,已经准备将项目最低的版本设置为 1.18 了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/859806

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX