2017-04-26 93 views
0

我有一个接口,它定义了一个参数,其类型为func(interface{}, proto.Message) interface{},我试图将类型func reduceMsg(a interface{}, b proto.Message) []*PersistentData传递给它。这会导致以下编译器错误:如何返回指定返回值的子类型(在本例中为interface {})?

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

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

package main 

import "fmt" 

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

https://golang.org/doc/faq#covariant_types – JimB

+0

这似乎是一种语言中的缺陷。特别是鉴于以下内容完全合法:'var t interface {} = [] string {}'。 – jonderry

+1

您的示例与解释的问题实际上没有关系:可分配性规则(您的示例代码)和接口实现规则(您的实际问题)是不同的。 – zerkms

回答

1

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.

可转让

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.

一般来说,围棋不允许隐式从一种类型转换值到另一个,是能够使用混凝土 - 异常类型化对象就好像它们是接口(它们实现的那样)。

在这种特殊情况下,由于你的函数实际上并没有返回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) 
    } 
} 

这基本上是做(明确)所需的运行时隐式地做包装操作。

2

该对象的类型是整个函数签名。如果签名不匹配,则它不是同一类型,并且不能这样分配。

任何东西都可以分配给空接口,因为所有类型都满足接口,但是在你的问题中既没有类型是空接口,你只是有一个返回空接口的函数。

不是因为功能的一部分可以分配给另一个它使它相同。该类型是整个函数签名。我认为这是不能将int分配给int8的相同逻辑。如果你愿意,你可以将它们施放,但是对于它们来说,它们是不同的类型,你需要处理进行必要的转换才能分配它们。

你可以做的是改变你的第二个函数签名返回这样一个空的接口:

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

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

这样的函数签名是一样的,所以这是考虑同一类型的,你是返回一个[]*PersistentData 。当然,在使用它之前,你需要做一个类型断言,因为程序将它视为{}interface,因为这是函数返回的类型。

+1

“我认为这是不能将int分配给int8的相同逻辑”---我不同意;您不能将'int'分配给'int8',因为这可能会导致数据丢失。但协方差是一个完全不同的故事:协变类型兼容:'[] string'可分配给'interface {}'。 Co/Contra-variance支持是一种更高级别的操作,而不仅仅是类型的可分配性。 – zerkms

+1

我知道这是一个不同的概念,他们可能有以这种方式设计语言的动机,但不允许的原因是一样的。如果是不同的类型,你就不能做任务。而函数类型是由整个函数签名定义的。 https://golang.org/ref/spec#Function_types – Topo