为了账号安全,请及时绑定邮箱和手机立即绑定

为什么通道的通道方向变化不能兼容?

为什么通道的通道方向变化不能兼容?

Go
慕工程0101907 2021-07-07 16:57:51
我喜欢编程提供尽可能严格的接口,以避免错误的使用,因为它是明确的和自我记录的。所以,当用户应该单向使用时,我喜欢提供定向通道,但当然,在内部我有一个双向通道副本。分配以下作品:var internal chan intvar external <-chan intexternal = internal但是现在我想向用户提供一个<-chan chan<- int类型(在函数的返回中),但以下方法不起作用:var internal chan chan intvar external <-chan chan<- intexternal = internal // this fails我有两个问题:究竟为什么这不起作用?所以,我可以声明一个<-chan chan<-类型的变量,但是......不能在任何实际意义上使用这样的类型?(因为即使有定向通道,它们也是用于编排双向通道的 AFAIK,并且由于不可能进行分配,因此不能以这种方式使用)
查看完整描述

3 回答

?
慕容森

TA贡献1853条经验 获得超18个赞

这不起作用的原因

规范说明了有关通道可分配性的内容:


[通道] 值x可分配给类型变量T(“x 可分配给 T”)[当]x是双向通道值,T是通道类型,x的类型V并T具有相同的元素类型,并且至少有一个V或T不是命名类型。


这正是您所经历的:


chan (chan int)以<- chan (chan int)作品

chan (chan int)对<- chan (chan<- int)不

原因是元素类型(chan关键字之后的)不相等。


我可以申报但不能使用吗?

您可以使用它,但不能以您想要的方式使用。不可能按照您的方式分配变量,但通过更正元素类型,您确实可以使用它:


var internal chan chan<- int

var external <-chan chan<- int

external = internal

如果你只有你的chan chan int类型,你需要复制你的值(播放示例):


var internal chan chan int

var internalCopy chan chan<- int


go func() { for e := range internal { internalCopy <- e } }()


var external <-chan chan<- int

external = internalCopy


查看完整回答
反对 回复 2021-07-12
?
慕运维8079593

TA贡献1876条经验 获得超5个赞

这种情况有点类似于在具有用户级泛型的语言中遇到的协变和逆变问题。在 Go 中使用泛型类型的内部等价物(它们被称为“复合类型”)时,您也可以遇到它。例如:


type A struct{}


// All `B`s are convertible to `A`s

type B A

甚至:


type A interface{}

// B embeds A

type B interface{ A }


var b B

// This works without problem

var a A = A(b)

但请考虑以下情况:


var bs []B

var as []A = ([]A)(bs)

在这里,编译失败并显示错误cannot use bs (type []B) as type []A in assignment。尽管 anyB可以转换为等效的A,但这不适用于[]Band []A(或chan Band chan A、 or map[string]Band map[string]A、 or func(a A)andfunc(b B)或在其定义中使用As 和Bs 的任何其他泛型类型)。尽管类型可以相互转换,但它们并不相同,这些generics在 Go 中的工作方式来自规范:


每个类型 T 都有一个底层类型:如果 T 是预先声明的类型或类型文字,则对应的底层类型是 T 本身。否则,T 的基础类型是 T 在其类型声明中引用的类型的基础类型。


type T1 string

type T2 T1

type T3 []T1

type T4 T3

字符串、T1 和 T2 的基础类型是字符串。[]T1、T3 和 T4 的基础类型是 []T1。


请注意,这里的基础类型[]T1是[]T1,而不是[]string。这意味着[]T2will的基础类型是[]T2,not []stringor []T1,这使得它们之间的转换变得不可能。


基本上,您正在尝试执行以下操作:


var internal chan Type1

var external <-chan Type2

external = internal

这是失败Type1和Type2两种不同的类型,至于类型系统而言。


协变和逆变是非常困难的问题,因为维基百科文章的长度或在 Java 或 C# 中解开有界泛型层所花费的任何时间都会告诉您。这是泛型如此难以实现并引发如此多争论的原因之一。


您可以通过在只读和读/写通道之间的别名上更深一层来获得所需的行为,就像您在第一个示例中对internal/external通道所做的一样:


package main


import "fmt"


// This has the correct signature you wanted

func ExportedFunction(c <-chan (chan<- int)) {

    // Sends 1 to the channel it receives

    (<-c)<- 1

}


func main() {

    // Note that this is a READ/WRITE channel of WRITE-ONLY channels

    // so that the types are correct

    internal := make(chan (chan<- int))

    var external <-chan (chan<- int)

    // This works because the type of elements in the channel is the same

    external = internal


    // This channel is internal, so it is READ/WRITE

    internal2 := make(chan int)


    // This is typically called externally

    go ExportedFunction(external)


    fmt.Println("Sending channel...")

    // The type of internal makes the receiving end of internal/external

    // see a WRITE-ONLY channel

    internal <- internal2

    fmt.Println("We received:")

    fmt.Println(<-internal2)

}

同样的事情在操场上。


基本上与您的第一个示例相同,除了您必须在“读/写”与“仅读(或写)”别名中更深入一层。


查看完整回答
反对 回复 2021-07-12
  • 3 回答
  • 0 关注
  • 193 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信