在 Go 中为通道输入别名工作很奇怪

Type alias for channel in Go works strange

我想通过通道组织两个函数之间的通信。被调用者只能向通道发送数据,而调用者将在 select 中等待。我想在被调用者签名中显示此限制。我想要的另一件事是使用通道的类型别名。例如,我不想使用 chan string,而是将 MsgChan 定义为 type MsgChan chan string。我遇到了这个问题——如果取消注释行 test1(make(Ch)):

,下面的代码将无法编译
package main

import "fmt"

type Ch chan int
type ChIn chan<- int

func test1(in ChIn) {
    fmt.Println(in)
}

func test2(in chan<- int) {
    fmt.Println(in)
}

func main() {
    //test1(make(Ch))
    test1(make(chan int))
    test2(make(Ch))
    test2(make(ChIn))
}

我不明白为什么我不能使用这种方法?

test1() 有一个 ChIn 类型的参数。这是一个命名类型。您想要传递类型为 Ch 的值,它是双向通道类型,也是命名类型。

因此,为了使其能够编译,Ch 的值应该可以分配给类型 ChIn。语言规范不允许这样做。

引用 Assignability(突出显示适用于此处的内容):

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.

如果您尝试传递未命名类型但具有相同基础类型的值,则可以使其工作,这可以通过使用类型转换来实现,例如:

test1((chan int)(make(Ch)))

但是上面的转换会破坏命名 Ch 类型的目的(因为你必须重复它的类型文字才能将它转换为未命名的类型,这样你就可以将它传递给 test1()).

你应该做的是不要隐藏类型是通道(不要在类型声明的类型字面量中包含chan),只为元素类型创建一个新类型频道,例如:

type Msg int

func test(in chan<- Msg) {
    fmt.Println(in)
}

func main() {
    test(make(chan Msg))
    test(make(chan<- Msg))

    ch := make(chan Msg)
    chIn := (chan<- Msg)(ch)
    test(chIn)
}

Go Playground 上试试。