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

我喜欢编程提供尽可能严格的接口,以避免错误的使用,因为它是明确的和自我记录的。


所以,当用户应该单向使用时,我喜欢提供定向通道,但当然,在内部我有一个双向通道副本。


分配以下作品:


var internal chan int

var external <-chan int

external = internal

但是现在我想向用户提供一个<-chan chan<- int类型(在函数的返回中),但以下方法不起作用:


var internal chan chan int

var external <-chan chan<- int

external = internal // this fails

我有两个问题:


究竟为什么这不起作用?

所以,我可以声明一个<-chan chan<-类型的变量,但是......不能在任何实际意义上使用这样的类型?(因为即使有定向通道,它们也是用于编排双向通道的 AFAIK,并且由于不可能进行分配,因此不能以这种方式使用)


慕工程0101907
浏览 194回答 3
3回答

慕容森

这不起作用的原因规范说明了有关通道可分配性的内容:[通道] 值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<- intvar external <-chan chan<- intexternal = internal如果你只有你的chan chan int类型,你需要复制你的值(播放示例):var internal chan chan intvar internalCopy chan chan<- intgo func() { for e := range internal { internalCopy <- e } }()var external <-chan chan<- intexternal = internalCopy

慕运维8079593

这种情况有点类似于在具有用户级泛型的语言中遇到的协变和逆变问题。在 Go 中使用泛型类型的内部等价物(它们被称为“复合类型”)时,您也可以遇到它。例如:type A struct{}// All `B`s are convertible to `A`stype B A甚至:type A interface{}// B embeds Atype B interface{ A }var b B// This works without problemvar a A = A(b)但请考虑以下情况:var bs []Bvar 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 stringtype T2 T1type T3 []T1type T4 T3字符串、T1 和 T2 的基础类型是字符串。[]T1、T3 和 T4 的基础类型是 []T1。请注意,这里的基础类型[]T1是[]T1,而不是[]string。这意味着[]T2will的基础类型是[]T2,not []stringor []T1,这使得它们之间的转换变得不可能。基本上,您正在尝试执行以下操作:var internal chan Type1var external <-chan Type2external = internal这是失败Type1和Type2两种不同的类型,至于类型系统而言。协变和逆变是非常困难的问题,因为维基百科文章的长度或在 Java 或 C# 中解开有界泛型层所花费的任何时间都会告诉您。这是泛型如此难以实现并引发如此多争论的原因之一。您可以通过在只读和读/写通道之间的别名上更深一层来获得所需的行为,就像您在第一个示例中对internal/external通道所做的一样:package mainimport "fmt"// This has the correct signature you wantedfunc ExportedFunction(c <-chan (chan<- int)) {&nbsp; &nbsp; // Sends 1 to the channel it receives&nbsp; &nbsp; (<-c)<- 1}func main() {&nbsp; &nbsp; // Note that this is a READ/WRITE channel of WRITE-ONLY channels&nbsp; &nbsp; // so that the types are correct&nbsp; &nbsp; internal := make(chan (chan<- int))&nbsp; &nbsp; var external <-chan (chan<- int)&nbsp; &nbsp; // This works because the type of elements in the channel is the same&nbsp; &nbsp; external = internal&nbsp; &nbsp; // This channel is internal, so it is READ/WRITE&nbsp; &nbsp; internal2 := make(chan int)&nbsp; &nbsp; // This is typically called externally&nbsp; &nbsp; go ExportedFunction(external)&nbsp; &nbsp; fmt.Println("Sending channel...")&nbsp; &nbsp; // The type of internal makes the receiving end of internal/external&nbsp; &nbsp; // see a WRITE-ONLY channel&nbsp; &nbsp; internal <- internal2&nbsp; &nbsp; fmt.Println("We received:")&nbsp; &nbsp; fmt.Println(<-internal2)}同样的事情在操场上。基本上与您的第一个示例相同,除了您必须在“读/写”与“仅读(或写)”别名中更深入一层。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go