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

huangapple go评论87阅读模式
英文:

How can I assign a reflect.Value that holds an error object to an ordinary variable of type error?

问题

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

我尝试了许多不同的refer.<function>调用和类型断言的变体,但似乎无法让编译器将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的准确评论。代码需要一个真正的错误变量,而不仅仅是一个*error,之后localerr = res[n].Interface().(error)完美地工作了。(还将函数更改为使用虚假参数触发非nil错误值的os.Chdir)

英文:

I am trying to write a function that returns back to its caller an error result taken from the result slice of reflect.ValueOf(somefunc).Call(someargs).

I've tried numerous variants of refer.<function> calls, and type assertions. but cannot seem to get the compiler to let me put the actual concrete value from the reflect.Value slice back into an ordinary error variable.

Here's the code, using os.Getwd as the function:

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

With the following output:

resType.Implements(errType) =  true
res[1] as %v = &lt;nil&gt;
res[1] as %#v = error(nil)
ValueOf(res[1]) as %v = &lt;error Value&gt;
ValueOf(res[1]) as %#v = reflect.Value{typ:(*reflect.rtype)(0x4b9a60), ptr:(unsafe.Pointer)(0xc42000a3f0), flag:0x94}
ValueOf(res[1]).Type() as %#v = &amp;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)

I've abbreviated the example to remove lots of other Printf statements that indicate (to me at least) that the types are the same (even in what I think are the relevant fields of the reflect.Value struct). Why, when all of the various print statements seem to be telling me the result is an error value, can't I assign it to my local variable?

Uncommenting the first assignment in the code example above results in this complaint from the compiler:

./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)

So I figured I needed the Interface() result, but still no luck (using the 2nd assignment commented out above):

./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)

Finally, a type assertion on the Interface() return value causes a panic:

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

No matter what I've tried, I can't seem to escape from the dreaded reflect.Value which prevents me from doing the assignment to ordinary error variable. I have tried Set() also without success, but perhaps incorrectly.

I would be forever grateful for insight and/or the magic incantation to do this.

EDIT
Thanks https://stackoverflow.com/users/965900/mkopriva for the spot on comment. The code requires a real error variable, not just a *error, after which localerr = res[n].Interface().(error) worked perfectly. (also changed function to os.Chdir using a bogus argument to trigger a non-nil error value)

答案1

得分: 1

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

reflect.ValueOf(res[1]) // 这不是你想要的

这样做会将值的底层类型从 error 更改为 reflect.Value,这就是为什么随后调用 .Interface().(error) 会导致程序崩溃的原因。

所以要修复你的代码,直接在结果上调用 .Interface().(error),像这样:

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

正如 Cerise Limón 已经指出的,当你进行 类型断言 时,使用 "comma ok" 惯用法是一个好的做法,以避免不必要的 panic。

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

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

if err, ok := res[1].Interface().(error); ok && err != nil {
     // 这是一个非空的错误
}

英文:

The value returned from Call is a slice of reflect.Values, so there is no need to wrap the result in another reflect.ValueOf call like you do in your example code:

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

Doing that will change the value's underlying type from error to reflect.Value which is the reason why subsequently calling .Interface().(error) causes the program to panic.

So to fix your code just call .Interface().(error) directly on the result like so:

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

And, as already pointed out by Cerise Limón, when you do type assertion it's good practice to use the "comma ok" idiom to avoid unnecessary panics.

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

Or, a bit more concise alternative:

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

huangapple
  • 本文由 发表于 2017年5月4日 06:54:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/43771258.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定