除了Go拒绝根据此处其他答案中解决的这些方差关系转换切片之外,思考一下 为什么 Go拒绝这样做是有用的,即使两种类型的内存中表示形式相同。
在您的示例中,提供
woofRunnerss 的切片作为类型的参数
[]woofer要求对切片的元素类型进行 协变
处理。实际上,当从切片中
读取内容时 ,由于a
woofRunner是a
woofer,因此您知道a中存在的每个元素
[]woofRunner都会满足读者的需求
[]woofer。
但是,在Go中,切片是参考类型。当将切片作为参数传递给函数时,将复制切片,但是在调用的函数主体中使用的副本将继续引用相同的后备数组(
append超出其容量之前必须进行重新分配)。数组的可变视图(通常是将项目插入集合)需要对元素类型进行
逆向 处理。也就是说,当需要 插入 或 覆盖
type元素的目的是要求函数参数时
woofRunner,提供a是可以接受的
[]woofer。
问题是函数是否要求使用slice参数
- 从中读取(对于
woofer
s来说,a[]woofRunner
和a一样好[]woofer
), - 向它写(对于写
woofRunner
s,a[]woofer
和a一样好[]woofRunner
), - 或两者兼有(两者都不可以替代)。
考虑一下,如果Go确实接受协变方式的slice参数,并且有人跟随并
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?即使Go愿意将a
[]*dog视为a
[]woofer,我们也会
*wolf在
*dog此处的数组中加上a
。某些语言通过对尝试的数组插入或覆盖进行运行时类型检查来防止此类事故发生,但是由于Go阻止了我们做到这一点,因此不需要这些额外的检查。



