duanjiebian6712 2017-06-17 09:07
浏览 33
已采纳

为什么我不能在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
}

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)

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

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

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

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

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

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

(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.)

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.

Note 2: Regarding the rules of Assignability:

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?

  • 写回答

3条回答 默认 最新

  • dongyi1215 2017-06-17 16:23
    关注

    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.

    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.

    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.

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

    • 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).

    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?
    

    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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

悬赏问题

  • ¥15 ansys fluent计算闪退
  • ¥15 有关wireshark抓包的问题
  • ¥15 Ubuntu20.04无法连接GitHub
  • ¥15 需要写计算过程,不要写代码,求解答,数据都在图上
  • ¥15 向数据表用newid方式插入GUID问题
  • ¥15 multisim电路设计
  • ¥20 用keil,写代码解决两个问题,用库函数
  • ¥50 ID中开关量采样信号通道、以及程序流程的设计
  • ¥15 U-Mamba/nnunetv2固定随机数种子
  • ¥30 C++行情软件的tick数据如何高效的合成K线