如何将包含错误对象的reflect.Value分配给类型为error的普通变量?

时间:2017-05-03 22:54:03

标签: go reflection

我正在尝试编写一个函数,该函数返回给调用者,从reflect.ValueOf(somefunc).Call(someargs)的结果切片中获取错误结果。

我尝试了很多参考变种。调用,并键入断言。但似乎无法让编译器让我将reflect.Value切片中的实际具体值放回普通的错误变量中。

以下是代码,使用os.Getwd作为函数:

   var somefunc interface{}
   var errToCaller *error
   somefunc = os.Getwd
   ftype := reflect.TypeOf(somefunc)
   errType := reflect.TypeOf(errToCaller).Elem()
   resType := ftype.Out(1)
   fmt.Println("resType.Implements(errType) = ",
      resType.Implements(errType))
   res := reflect.ValueOf(somefunc).Call([]reflect.Value{})
   fmt.Printf("res[1] as %%v = %v\n", res[1])
   fmt.Printf("res[1] as %%#v = %#v\n", res[1])
   fmt.Printf("ValueOf(res[1]) as %%v = %v\n",
      reflect.ValueOf(res[1]))
   fmt.Printf("ValueOf(res[1]) as %%#v = %#v\n",
      reflect.ValueOf(res[1]))
   fmt.Printf("ValueOf(res[1]).Type() as %%#v = %#v\n",
      reflect.ValueOf(res[1]).Type())
   fmt.Printf("ValueOf(res[1]).Interface() as %%#v = %#v\n",
      reflect.ValueOf(res[1]).Interface())
   // *errToCaller = reflect.ValueOf(res[1])
   // *errToCaller = reflect.ValueOf(res[1]).Interface()
   // *errToCaller = reflect.ValueOf(res[1]).Interface().(error)

使用以下输出:

resType.Implements(errType) =  true
res[1] as %v = <nil>
res[1] as %#v = error(nil)
ValueOf(res[1]) as %v = <error Value>
ValueOf(res[1]) as %#v = reflect.Value{typ:(*reflect.rtype)(0x4b9a60), ptr:(unsafe.Pointer)(0xc42000a3f0), flag:0x94}
ValueOf(res[1]).Type() as %#v = &reflect.rtype{size:0x18, ptrdata:0x10, hash:0x500c1abc, tflag:0x7, align:0x8, fieldAlign:0x8, kind:0x19, alg:(*reflect.typeAlg)(0x4a62d0), gcdata:(*uint8)(0x4daa41), str:21371, ptrToThis:184032}
ValueOf(res[1]).Interface() as %#v = error(nil)

我缩写了这个例子,删除了许多其他Printf语句,这些语句指示(至少对我来说)类型是相同的(即使我认为是reflect.Value结构的相关字段)。 为什么,当所有各种打印语句似乎都告诉我结果是错误值时,我不能将它分配给我的本地变量吗?

取消注释上面代码示例中的第一个作业会导致编译器提出此抱怨:

./passerror.go:30: cannot use reflect.ValueOf(res[1]) (type reflect.Value) as type error in assignment:
        reflect.Value does not implement error (missing Error method)

所以我认为我需要Interface()结果,但仍然没有运气(使用上面注释的第二个作业):

./passerror.go:31: cannot use reflect.ValueOf(res[1]).Interface() (type interface {}) as type error in assignment:
        interface {} does not implement error (missing Error method)

最后,Interface()返回值的类型断言导致恐慌:

panic: interface conversion: reflect.Value is not error: missing method Error

无论我尝试过什么,我似乎无法摆脱可怕的reflect.Value,这阻止了我对普通错误变量的分配。我试过Set()也没有成功,但可能不正确。

我会永远感激这种洞察力和/或神奇的咒语。

修改 感谢https://stackoverflow.com/users/965900/mkopriva发表评论。代码需要一个真正的错误变量,而不仅仅是一个*错误,之后localerr = res[n].Interface().(error)完美地运行。 (还使用伪造的参数将函数更改为os.Chdir以触发非零错误值)

1 个答案:

答案 0 :(得分:1)

Call返回的值是reflect.Value的一个片段,所以不需要像在示例代码中那样将结果包装在另一个reflect.ValueOf调用中:

reflect.ValueOf(res[1]) // not what you want

这样做会将值的基础类型从error更改为reflect.Value,这就是为什么随后调用.Interface().(error)导致程序出现恐慌的原因。

因此,要修复代码,只需直接在结果上调用.Interface().(error),如下所示:

res[1].Interface().(error)

正如Cerise Limón已经指出的那样,当你做类型断言时,最好使用“逗号确定”的习惯来避免不必要的恐慌。

err, ok := res[1].Interface().(error)
if !ok {
     // oops
}

或者,更简洁的替代方案:

if err, ok := res[1].Interface().(error); ok && err != nil {
     // it's a non-nil error
}