Go中声明只读通道变量必须使用<-chan T类型,如var ch <-chan int,它仅允许接收操作(<-ch或for range ch),禁止发送和关闭;该类型只能由双向通道显式赋值转换获得,不可直接make(<-chan T)。

Go 中如何声明只读通道变量
Go 语言里不能“声明一个变量后让它变成只读通道”,而是通过类型系统在编译期限制写入操作——只读通道类型是 chan<- T,它只能被发送(send),不能接收(receive)。
chan<- T 和 <-chan T 的区别必须分清
这是最容易混淆的点:箭头方向决定可操作性,不是“谁在左边谁是只读”。chan<- T 是“只能发”的通道,<-chan T 是“只能收”的通道。两者不可互赋值,哪怕底层是同一个 chan T。
-
chan<- int:可以向它ch <- 42,但不能<-ch或range ch -
<-chan int:可以<-ch或range ch,但不能ch <- 42 - 函数参数中用
chan<- T表示“我只往里塞数据”,用<-chan T表示“我只从它读数据”,这是 Go 接口设计的常见模式
如何从普通通道获得只读通道变量
你无法直接声明一个独立的、未初始化的 chan<- T 变量并让它指向某个通道,因为只读/只写类型是“视图类型”——它们必须由一个双向通道(chan T)转换而来,且转换是单向隐式或显式发生的。
- 函数返回时自动转换:
func newProducer() chan<- string { return make(chan string) }—— 返回值类型是只写,但make创建的是双向通道,Go 允许隐式转换 - 显式类型转换不被允许:
ch := make(chan int); ro := (chan<- int)(ch)是非法语法;正确方式是用函数签名或参数传递触发转换 - 赋值给只读变量时,必须由双向通道“流出”:
c := make(chan int); var ro chan<- int = c✅;但var ro chan<- int; ro = c❌(编译错误:cannot assign chan int to chan<- int)
实际使用中最容易踩的坑
只读通道不是运行时保护,而是一道编译检查。一旦类型定为 chan<- T,你就彻底失去接收能力——哪怕底层通道还有数据也没法读。
立即学习“go语言免费学习笔记(深入)”;
- 误以为
chan<- T能range:不行,会报错invalid operation: range on send-only channel - 把只写通道传给需要接收的函数(比如标准库的
sync.WaitGroup配合for range消费逻辑),直接编译失败 - 在 select 语句中混用:
case v := <-ro(ro 是<-chan T)合法,但case ro <- v(ro 是chan<- T)才合法;写反了就报错 - 注意:通道关闭只能由发送方执行,所以
close(ch)要求ch类型至少是chan<- T,不能对<-chan T调用close
类型约束越早施加越安全,但别指望它能防止逻辑错误——比如往一个已关闭的 chan<- T 发送数据,依然会 panic。


















