Go中的通道类型别名的工作方式很奇怪。

huangapple go评论80阅读模式
英文:

Type alias for channel in Go works strange

问题

我想通过通道来组织两个函数之间的通信。被调用者只能向通道发送数据,而调用者将在select中等待它。我希望在被调用者的签名中显示这个限制。另一件我想要的事情是使用通道的类型别名。例如,我想使用MsgChan来代替chan string,并将其定义为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))
}

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

英文:

I want to organize communication between two functions via channels. The callee can only send data to the channel while the caller will wait for it in the select. And I want to show this restriction in the callee signature. Another thing that I want is working with type aliases for channels. For example, instead of having chan string I want to work with MsgChan defined as type MsgChan chan string. And I faced with the problem - the code below won't compile if uncomment line test1(make(Ch)):

package main

import &quot;fmt&quot;

type Ch chan int
type ChIn chan&lt;- int

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

func test2(in chan&lt;- int) {
	fmt.Println(in)
}

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

I don't understand why I can't use such approach?

答案1

得分: 3

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

所以为了使其编译通过,Ch的值应该可以赋值给ChIn类型。但是语言规范不允许这样做。

引用可赋值性(这里突出显示适用的部分):

> 在以下任何情况下,值x可以赋值给类型为T变量("x可以赋值给T"):
>
> - x的类型与T完全相同。
> - x的类型VT具有相同的底层类型,并且VT中至少有一个不是命名类型。
> - T是一个接口类型,并且x实现T
> - x是一个双向通道值,T是一个通道类型,x的类型VT具有相同的元素类型,并且VT中至少有一个不是命名类型。
> - x是预声明的标识符nil,并且T是指针、函数、切片、映射、通道或接口类型。
> - x是一个未命名的常量,可以用类型T的值表示。

如果你尝试传递一个未命名类型但具有相同底层类型的值,它可以工作,可以通过使用类型转换来实现,例如:

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

但是上述转换会破坏具有命名Ch类型的目的(因为你必须重复其类型字面量,以便将其转换为未命名类型,以便将其传递给test1())。

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

type Msg int

func test(in chan&lt;- Msg) {
    fmt.Println(in)
}

func main() {
    test(make(chan Msg))
    test(make(chan&lt;- Msg))

    ch := make(chan Msg)
    chIn := (chan&lt;- Msg)(ch)
    test(chIn)
}

Go Playground上试一试。

英文:

test1() has one parameter of type ChIn. This is a named type. You want to pass a value of type Ch which is a bidirectional channel type, and is also a named type.

So in order for this to compile, a value of Ch should be assignable to type ChIn. This is not allowed by the language specification.

Quoting Assignability (highlighted the one that applies here):

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

You can make it work if you try to pass a value of unnamed type but having the same underlying type, which can be achieved by using a type conversion, e.g.:

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

But the above conversion would defeat the purpose of having the named Ch type (as you have to repeat its type literal in order to convert it to an unnamed type just so you can pass it to test1()).

What you should do is don't hide that the type is a channel (don't include chan in the type literal of the type declaration), only create a new type for the element type of the channel, e.g.:

type Msg int

func test(in chan&lt;- Msg) {
    fmt.Println(in)
}

func main() {
    test(make(chan Msg))
    test(make(chan&lt;- Msg))

    ch := make(chan Msg)
    chIn := (chan&lt;- Msg)(ch)
    test(chIn)
}

Try it on the Go Playground.

huangapple
  • 本文由 发表于 2016年4月24日 16:00:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/36820663.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定