送信、受信専用のチャンネルの「生成」

secondarykey 2019/07/22 17:06

Shizuoika.go Vol.14 お疲れ様でした。

先日はShizuoka.go Vol.14に参加していただきありがとうございました。

その中で少し話題になった送受信専用チャンネルの生成について少し調べてみました。 問題としては「生成できる意味があるか?」という話題です。

そもそも私自身は送信、受信専用の宣言があること自体を忘れていた始末、、、 少し触ってみましょう。

「宣言」としての使用方法

まずは使ってみます。

package main

import (
    "fmt"
    "time"
)

func receive(r <-chan int) {
    for {
        num := <-r 
        fmt.Println("Receive : ", num)
        time.Sleep(time.Second)
    }   
}

func send(s chan<- int) {
    for i := 1; i <= 10; i++ {
        s <- i
    }   
}

func main() {
    c := make(chan int)
    var s chan<- int = c 
    var r <-chan int = c 

    go receive(r)

    send(s)

    for len(c) > 0 { 
        time.Sleep(100)
    }
    close(c)                                                                    
}

待受が雑ですみません。

双方向のチャンネルを生成して、それを受信、送信専用のチャネルに代入しています。 コードを変更してみればわかりますが、send(),receive()とも変数をわざわざ宣言しなくても、cを渡すことも可能です。※まぁキャストしてないで代入できるのでわかりそうなもんですが。

このコードは関数内で送受信の逆の用途で使わせることはないので意図があり有用だと思います。

生成するコードを書いてみる

上記コードのrとsを生成してみます。

r := make(<-chan int)
s := make(chan<- int)
go receive(r)
send(s)

例えばこんな感じで書いて、実行をかけると・・・

もちろんですが受信がないので、デッドロックします。

生成とは関係ないですが、例えば

s = r

と代入してみると

cannot use r (type <-chan int) as type chan<- int in assignment

と出ますし、

s = r.(chan<- int)

と書いても

invalid type assertion: r.(chan<- int) (non-interface type <-chan int on left)

となります。もちろん逆も不可能です。 そもそも送受信が同一のチャンネルでないと意味がない為、元のチャンネルをキャストして使用する。となるはずで、生成に意味がないことにかなり実感がわきます。

生成に関する議論

かなり以前のものですが、GoのIssueにありました。

このスレッドの限りでは「書き方で問題が起こることは他にもある」という感じですね。 別段問題が起こってないのでいいのでは?って感じでOKなのかな?

いまのトレンドとして、どうしても気になるなら、型の宣言の仕方をチェックする静的解析を行ってチェックをかけるとかが現実的なのかな。。。ってそもそもmake自体に意味をなすコードが書けない気がする。。。

Gopherに関して

にて出力させていただきました。 オリジナルのGopherくんは、

によってデザインされました。

vertical_align_top