为什么我不能在Go中用一种类型的切片替代另一种类型? [英] Why can't I substitute a slice of one type for another in Go?

查看:76
本文介绍了为什么我不能在Go中用一种类型的切片替代另一种类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图了解Go的类型转换规则。假设我们有以下接口:

I'm trying to understand Go's type conversion rules. Say we have these interfaces:

type woofer interface {
  woof()
}

type runner interface {
  run()
}

type woofRunner interface {
  woofer
  runner
}

为了满足这些接口,我们有一个 dog 类型:

and to satisfy the interfaces we have a dog type:

type dog struct{}

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

这两个函数正在使用以下接口:

These two functions are using the interfaces:

func allWoof(ws []woofer) {}

func oneWoof(w woofer) {}

要使用这些方法,我可以编写以下内容:

To use these methods I can write the following:

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

第一个函数 oneWoof()可以正常工作; * dog 满足了所有 oneWoof 的需求,这就是 woof 功能。

The first function oneWoof() works as expected; a *dog implements all oneWoof needs, which is a woof function.

但是对于第二个函数 allWoof ,Go不会编译尝试的调用,并报告以下内容:

However for the second function allWoof, Go won't compile the attempted invocation, reporting the following:


不能在allWoof的参数中使用狗(类型[] woofRunner)作为类型[] woofer

cannot use dogs (type []woofRunner) as type []woofer in argument to allWoof

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

Using a type conversion is also impossible; writing []woofer(dogs) fails as well:


无法转换狗(键入[] woofRunner)键入[] woofer

cannot convert dogs (type []woofRunner) to type []woofer

[] woofRunner 具有满足 []低音扬声器的所有必要功能,那么为什么禁止这种转换?

Every member of []woofRunner has all the necessary functions to satisfy a []woofer, so why is this conversion prohibited?

(我不确定在Go 常见问题解答和各种问题中所解释的情况是否相同在堆栈溢出中,人们要求将类型 T 转换为 interface {} 。slice / array中的每个指针都是指向可以直接转换为另一种类型的类型。使用这些指针应该是可能的,其原因与将 dog [0] 传递给 oneWoof的原因相同。)

(I'm not sure if this is the same case explained in the Go FAQ and in various questions on Stack Overflow in which people ask about converting type T to interface{}. Every pointer in the slice/array is pointing to a type that is directly convertible to another type. Using these pointers should be possible for the same reason that passing dog[0] to 'oneWoof` is possible.)

注释1 :我知道一种解决方案是遍历并逐项转换项目。我的问题是,为什么这是必要的,以及是否有更好的解决方案。

Note 1: I know one solution is to loop over and and convert the items one by one. My question here is why that's necessary and whether there is a better solution.

注2 :关于可分配性


一个值x可以分配给类型T的变量[当] T是接口类型并且x实现T时。

A value x is assignable to a variable of type T [when] T is an interface type and x implements T.

我们不能说如果切片/数组的类型可以分配给另一种类型,那么这些类型的数组也可以分配?

Can't we say if the type of the slice/array is assignable to another type, then arrays of those types are also assignable?

推荐答案

对于Go拒绝根据此处其他答案中解决的这些方差关系转换切片的问题,仔细思考为什么 Go拒绝这样做是很有用的,即使两种类型的内存中表示形式相同。

In addition to Go's refusal to convert slices along these variance relationships addressed in other answers here, it's useful to think through why Go refuses to do so, even when the in-memory representation would be the same between the two types.

在您的示例中,提供了一部分 woofRunners s作为类型为的参数[] woofer 要求 协变处理。实际上,当从片中读取时,由于 woofRunner 低音扬声器,您知道 [] woofRunner 中存在的每个元素都会满足寻找 [] woofer 。

In your example, supplying a slice of woofRunnerss as a parameter of type []woofer is asking for covariant treatment of the slice's element type. When reading from the slice, indeed, since a woofRunner is a woofer, you know that every element present in a []woofRunner will satisfy a reader looking for []woofer.

但是,在Go中,切片是引用类型。将切片作为参数传递给函数时,将复制切片,但是在调用的函数主体中使用的副本将继续引用相同的后备数组(在追加超出其能力)。数组的可变视图(通常是将项目插入集合中)需要对元素类型进行 contravariant 处理。也就是说,当需要功能参数以插入覆盖类型为 woofRunner ,提供 []低音扬声器是可以接受的。

However, in Go, a slice is a reference type. When passing a slice as an argument to a function, the slice is copied, but the copy used in the invoked function body continues to refer to the same backing array (absent reallocation necessary before appending beyond its capacity). The mutable view of an array—more generally, inserting an item into a collection—requires contravariant treatment of the element type. That is, when it comes to demanding a function parameter with the intention of inserting into or overwriting an element of type woofRunner, it's acceptable to supply a []woofer.

问题是该函数是否需要slice参数对于

The question is whether the function is demanding the slice parameter for


  • 从中读取(对于读取低音扬声器 s,则为 [] woofRunner [] woofer )一样好,

  • 向其写入(对于编写 woofRunner s, []低音扬声器 []一样好woofRunner ),

  • 或两者(都不能替代)。

  • reading from it (for reading woofers, a []woofRunner is just as good as a []woofer),
  • writing to it (for writing woofRunners, a []woofer is just as good as a []woofRunner),
  • or both (neither is an acceptable substitute for the other).

考虑一下,如果Go确实接受协变方式的切片参数,然后有人来更改 allWoof 会发生以下情况:

Consider what would happen if Go did accept slice parameters in covariant fashion, and someone came along and changed allWoof as follows:

// 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愿意对待 [] *狗作为 []低音扬声器,我们最终会在我们的<$数组中使用 * wolf c $ c> * dog 在这里。某些语言通过对尝试的数组插入或覆盖进行运行时类型检查来防止此类事故发生,但是由于Go阻止了我们进行此类检查,因此不需要这些额外的检查。

Even if Go was willing to treat a []*dog as a []woofer, we would wind up with a *wolf in our array of *dog here. Some languages defend against such an accident with run-time type checks on the attempted array insertion or overwrite, but because Go precludes us from even making it this far, it doesn't need these additional checks.

这篇关于为什么我不能在Go中用一种类型的切片替代另一种类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆