如何返回指定返回值的子类型(在这种情况下是interface{})?

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

How can I return a subtype of the specified return value (in this case interface{})?

问题

我有一个接口,定义了一个参数的类型为 func(interface{}, proto.Message) interface{},我试图将类型为 func reduceMsg(a interface{}, b proto.Message) []*PersistentData 的内容传递给它。这导致了以下编译器错误:

无法将 reduceMsg(类型为 func(a interface{}, b proto.Message) []*PersistentData)用作类型为 func(interface{}, proto.Message) interface{} 的参数

这个错误的原因是什么,我该如何解决?似乎返回比 interface{} 更具体的类型应该是合法的。下面是一个简单的完整示例,说明了这个问题:

package main

import "fmt"

func main() {
    var t func() interface{} = func() []string { return []string{} }
    fmt.Println(t)
}
英文:

I have an interface that defines one parameter to have type func(interface{}, proto.Message) interface{} and I'm trying to pass something of type func reduceMsg(a interface{}, b proto.Message) []*PersistentData to it. This results in the following compiler error:

Cannot use reduceMsg (type func(a interface{}, b proto.Message) []*PersistentData as type func(interface{}, proto.Message) interface{}

What is the reason for this error, and how can I work around it? It seems like returning a more specific type than interface{} should be legal. Here's a simple complete example that illustrates the issue:

package main

import "fmt"

func main() {
	var t func() interface{} = func() []string { return []string{} }
	fmt.Println(t)
}

答案1

得分: 2

对象的类型是整个函数签名。如果签名不匹配,那么它们就不是相同的类型,不能以这种方式进行赋值。

任何东西都可以赋值给空接口,因为所有类型都满足该接口,但在你的问题中,两种类型都不是空接口,你只是有一个返回空接口的函数。

并不是因为函数的一部分可以赋值给另一个函数,就意味着它们是相同的类型。类型是整个函数签名。我认为这背后的逻辑与不能将int赋值给int8是相同的。如果你愿意,你可以进行类型转换,但对于Go来说,它们是不同的类型,你需要处理必要的转换才能进行赋值。

你可以将第二个函数的签名更改为返回空接口,像这样:

func(interface{}, proto.Message) interface{}

func reduceMsg(a interface{}, b proto.Message) interface{} {
    var a []*PersistentData
    // 在这里进行一些操作
    return a
}

这样,函数签名就相同了,因此被视为相同的类型,并且你返回的是[]*PersistentData。当然,在使用它之前,你需要进行类型断言,因为程序将把它视为interface{}类型,因为这是函数返回的类型。

英文:

The type of the object is the whole function signature. If the signature don't match, then it's not the same type and can't be assigned that way.

Anything can be assigned to the empty interface, because all types satisfy the interface, but in your problem neither type is the empty interface, you just have a function that returns an empty interface.

Not because a part of the function can be assigned to another it makes it the same. The type is the whole function signature. I think it's the same logic behind not being able to assign an int to an int8. You can cast them if you want, but for go, they are separate types and you need to deal with making the necessary conversions to be able to assign them.

What you can do is change your second function signature to return an empty interface like this:

func(interface{}, proto.Message) interface{}

func reduceMsg(a interface{}, b proto.Message) interface{} {
    var a []*PersistentData
    // do something here
    return a
}

This way the function signature is the same, so it's consider the same type and you are returning an []*PersistentData. Of course you will need to do a type assertion before using it as such because the program will treat it as an {}interface because that is the type that the function returned.

答案2

得分: 1

参考规范中提到:

在赋值语句中,每个值必须能够赋值给其被赋值的操作数的类型,以下是特殊情况:

  • 任何有类型的值都可以赋值给空白标识符。
  • 如果将无类型常量赋值给接口类型的变量或空白标识符,则该常量首先会转换为其默认类型。
  • 如果将无类型布尔值赋值给接口类型的变量或空白标识符,则该值首先会转换为布尔类型。

可赋值性

值 x 能够赋值给类型为 T 的变量("x 能够赋值给 T")的情况如下:

  • x 的类型与 T 相同。
  • x 的类型 V 和 T 具有相同的基础类型,并且 V 或 T 中至少有一个不是命名类型。
  • T 是接口类型,并且 x 实现了 T。
  • x 是双向通道值,T 是通道类型,x 的类型 V 和 T 具有相同的元素类型,并且 V 或 T 中至少有一个不是命名类型。
  • x 是预声明的标识符 nil,T 是指针、函数、切片、映射、通道或接口类型。
  • x 是可由类型 T 的值表示的无类型常量。

一般来说,Go 不允许你隐式地将一个类型的值转换为另一个类型,除非你可以将具体类型的对象用作它们实现的接口。

在这个特定的情况下,由于你的函数实际上并没有返回一个 interface{},编译器需要额外的工作来将返回值包装成一个 interface{} 并返回;如果你真的想要实现你所尝试的功能,你可以显式地自己这样做:

type Foo struct {
    X int
}
func create(x int) Foo {
    return Foo{X: x}
}
func main() {
    var f func(int) interface{} = func(x int) interface{} {
        return create(x)
    }
}

这基本上是(显式地)执行你希望运行时隐式执行的包装操作。

英文:

Referencing the spec,

>In assignments, each value must be assignable to the type of the operand to which it is assigned, with the following special cases:
>
> * Any typed value may be assigned to the blank identifier.
> * If an untyped constant is assigned to a variable of interface type or the blank identifier, the constant is first converted to its default type.
> * If an untyped boolean value is assigned to a variable of interface type or the blank identifier, it is first converted to type bool.

Assignability

> A value x is assignable to a variable of type T ("x is assignable to T") in any of these cases:
>
> * x's type is identical to T.
> * x's type V and T have identical underlying types and at least one of V or T is not a named type.
> * T is an interface type and x implements T.
> * x is a bidirectional channel value, T is a channel type, x's type V and T have identical element types, and at least one of V or T is not a named type.
> * x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type.
> * x is an untyped constant representable by a value of type T.

In general, Go doesn't allow you to implicitly convert values from one type to another, with the exception of being able to use concrete-typed objects as though they were interfaces (that they implement).

In this particular case, since your function doesn't actually return an interface{}, the compiler would have to do some extra work to wrap up the return value as an interface{} and return it; if you really want to accomplish what you're trying you can do this explicitly yourself:

type Foo struct {
    X int
}
func create(x int) Foo {
    return Foo{X: x}
}
func main() {
    var f func(int) interface{} = func(x int) interface{} {
        return create(x)
    }
}

which is basically doing (explicitly) the wrapping operation that you want the runtime to do implicitly.

huangapple
  • 本文由 发表于 2017年4月26日 08:29:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/43623049.html
匿名

发表评论

匿名网友

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

确定