我想遍历struct中的字段并提示输入字符串值来表示string字段,然后对指向struct的字段进行递归操作。
目前这是我尝试过的方法,但是尝试在指针的字符串字段中设置此值时出现错误。
package main
import (
"fmt"
"reflect"
)
type Table struct {
PK *Field
}
type Field struct {
Name string
}
func main() {
PopulateStruct(&Table{})
}
func PopulateStruct(a interface{}) interface {} {
typeOf := reflect.TypeOf(a)
valueOf := reflect.ValueOf(a)
for i := 0; i < typeOf.Elem().NumField(); i++ {
switch typeOf.Elem().Field(i).Type.Kind() {
case reflect.String:
fmt.Print(typeOf.Elem().Field(i).Name)
var s string
fmt.Scanf("%s", &s)
valueOf.Elem().Field(i).SetString(s)
case reflect.Ptr:
ptr := reflect.New(valueOf.Elem().Field(i).Type())
PopulateStruct(ptr.Elem().Interface())
valueOf.Elem().Field(i).Set(ptr)
}
}
}
期望返回值包含带有指针字符串字段集的初始化结构。
设置指针的字符串字段时出错。
恐慌:反射:在零值上调用reflect.Value.Field
答案 0 :(得分:1)
我将您的代码原样放置到Go Playground中,但由于PopulateStruct
被声明为返回interface{}
,但实际上未返回任何内容,所以它无法构建。删除声明的返回类型会引起您提到的恐慌。
这是因为在进入外部PopulateStruct
调用时,您具有指向零值Table
的有效指针。零值Table
有一个元素:类型为*Field
的零指针。因此,您的循环将运行一次并找到一个reflect.Ptr
,正如您所期望的那样。添加更多诊断打印消息有助于了解发生了什么:
fmt.Printf("PopulateStruct: I have typeOf=%v, valueOf=%v\n", typeOf, valueOf)
for i := 0; i < typeOf.Elem().NumField(); i++ {
switch typeOf.Elem().Field(i).Type.Kind() {
// ... snipped some code ...
case reflect.Ptr:
ptr := reflect.New(valueOf.Elem().Field(i).Type())
fmt.Println("after allocating ptr, we have:", ptr.Type(), ptr,
"but its Elem is:", ptr.Elem().Type(), ptr.Elem())
此打印:
PopulateStruct: I have typeOf=*main.Table, valueOf=&{<nil>}
after allocating ptr, we have: **main.Field 0x40c138 but its Elem is: *main.Field <nil>
鉴于PopulateStruct
本身的构造方式,我们实际上必须在调用Field
之前分配一个真实的PopulateStruct
实例 now 。我们可以这样:
p2 := ptr.Elem()
ptr.Elem().Set(reflect.New(p2.Type().Elem()))
(code borrowed from json.Unmarshal
)。现在我们可以填写此Field
,其中有一个名为Name
,类型为String
的字段。
在我看来,这里的总体策略不是那么好:填充可能应该采用通用指针,而不是专门指向struct
的指针。然后,您可以在json解组器中模拟indirect
函数。但是,添加这两行(创建目标对象并使分配的指针指向该对象)足以使您的现有代码运行。
(或者,您可以从头开始创建并返回整个实例,在这种情况下,您所需要的只是类型-但我假设您有一个仅包含 some 字段的模式没有。)
Here's the complete Go Playground example.我做了一些其他更改,因为使用游乐场时无需扫描任何内容。