结论:chan<- T 是只发送 channel,<-chan T 是只接收 channel,在函数签名上把 channel 的使用方向固化进类型系统,调用方传错方向(往只收 channel 发送)直接编译错误。双向 channel 可隐式转为任一单向,反之不行。

展开:价值在于把设计意图写成契约:生产者函数签名 func produce(out chan<- int) 明确"我只往里塞",消费者 func consume(in <-chan int) 明确"我只往外取",读签名即知数据流向,也防止消费者误关闭、误发送。惯例搭配:1)close 只能由发送方做,单向类型从编译期保证接收方不会 close;2)标准库范例 time.After 返回 <-chan Time,调用方只能收;3)做扇入扇出流水线时,每个 stage 收 <-chan<-chan(内部持有的写端在 goroutine 退出时 close),链式组合清晰安全。易错点:把 <-chan 用在需要判断关闭又需要回写确认的场景会发现自己缺权限——设计时应拆开成两个 channel(如 done 信号单独传)。

func gen(nums ...int) <-chan int {
    out := make(chan int)
    go func() {
        defer close(out)
        for _, n := range nums { out <- n }
    }()
    return out // 双向隐式转只收
}

追问方向:select 中混合收发单向 channel 的写法、nil channel 在 select 中"永久阻塞"特性能用来做什么(动态禁用分支)。