2017-06-17 34 views
0

我想了解Go的类型转换规则。假设我们有以下接口:为什么我无法在Go中替换另一种类型的切片?

type woofer interface { 
    woof() 
} 

type runner interface { 
    run() 
} 

type woofRunner interface { 
    woofer 
    runner 
} 

,并满足我们有一个dog类型的接口:

type dog struct{} 

func (*dog) run() {} 
func (*dog) woof() {} 

这两个功能使用的接口:

func allWoof(ws []woofer) {} 

func oneWoof(w woofer) {} 

要使用这些方法我可以写下:

dogs := make([]woofRunner, 10) 
oneWoof(dogs[0]) 
allWoof(dogs) 

第一个功能oneWoof()按预期工作;一个*dog实现所有oneWoof需求,这是一个woof函数。

不过,对于第二个功能allWoof,围棋将无法编译尝试调用,报告如下:

不能用狗(型[] woofRunner)类型[]低音单元在争论allWoof

使用类型转换也是不可能的;写[]woofer(dogs)也将失败:

不能将狗(型[] woofRunner)键入[]低音单元

[]woofRunner每一个成员都拥有所有必要的功能,以满足[]woofer,所以这是为什么禁止转换?

(我不知道这是否是相同的情况下,在围棋FAQ并在堆栈溢出,使人们问转换类型Tinterface{}各种问题解释切片/阵列中的每个指针指向。我知道一个解决方案是在循环和并转换:即直接转换为另一种类型使用这些指针应为同样的原因,经过dog[0]为“oneWoof`是可能的)

注1可能项目逐一。我的问题是为什么这是必要的,是否有更好的解决方案。

注2:就Assignability规则:

甲值x可分配给类型的T A变量[时] T是一个接口类型以及x实现T.

难道我们不能说切片/数组的类型是否可以分配给另一种类型,那么这些类型的数组也可以赋值?

+1

https://golang.org/ref/spec#Assignability因此,赋值不符合语言标准可分配性规则。 – zerkms

+0

原因:因为这就是类型转换的工作原理。不,没有更好的解决方案。 – Flimzy

+2

请参阅https://golang.org/doc/faq#convert_slice_of_interface。经验法则:检查有效围棋,常见问题解答,围棋之旅和语言规范。答案就在那里。 – Volker

回答

2

除了去拒绝切片转换沿着这里其他的答案解决这些差异的关系,它想通过是非常有用的,为什么去拒绝这样做,即使在内存中表示将两者之间的相同类型。

在你的例子中,提供一片woofRunners s作为[]woofer类型的参数是要求片的元素类型为covariant treatment。当阅读从切片,确实,因为woofRunnerwoofer,你知道每一个存在于[]woofRunner元素将满足读者寻找[]woofer

但是,在Go中,切片是引用类型。在将切片作为参数传递给函数时,将复制切片,但在调用的函数体中使用的副本继续引用相同的后备阵列(在超出其容量之前不需要重新分配)。数组的可变视图(更一般地说,将项目插入到集合中)需要处理元素类型逆变换。也就是说,当要求功能参数意图为插入到覆盖woofRunner类型的元素时,可以提供[]woofer

的问题是功能是否苛刻的

  • 从中读取片参数(读woofer S,A []woofRunner只是作为[]woofer为好),
  • 写入它(为写作woofRunner s,一个[]woofer就像[]woofRunner),
  • 或两者(两者都不是另一个可接受的替代品)。

考虑一下,如果去没有接受协变时尚切片中的参数会发生什么,有人走过来,改变allWoof如下:

// Another type satisfying `woofRunner`: 
type wolf struct{} 
func (*wolf) run() {} 
func (*wolf) woof() {} 

func allWoof(ws []woofer) { 
    if len(ws) > 0 { 
    ws[0] = &wolf{} 
    } 
} 

dogs := []*dog{&dog{}, &dog{}} 
allWoof(dogs) // Doesn't compile, but what if it did? 

即使转到愿意治疗[]*dog[]woofer,我们会在*dog这里列出*wolf。一些语言通过对试图插入或覆盖数组的运行时类型检查来防御这样的事故,但由于Go甚至使我们无法实现这一目标,所以它不需要这些额外的检查。

+0

精彩的回答。谢谢! –

0

您必须转换的接口组合在一个循环:

var woofers []woofer 
for _, w := range dogs { 
    woofers = append(woofers, w) 
} 
+0

谢谢,但请参阅我的编辑,我在其中澄清我问的问题的类型 –

2

Go reference

两片类型是相同的,如果它们具有相同的元素类型。

两种接口类型是相同的,如果他们有相同的一组具有相同名称和相同的功能类型(...)方法。

所以woofRunner不等同于woofer,这会导致我们[]woofRunner不等同于[]woofer

相关问题