LoginSignup
11
8

More than 5 years have passed since last update.

Go の channel に出てくる矢印の意味

Posted at

Go のライブラリを読んでいる時に、次のようなメソッドのシグネチャがあって、どういう意味か混乱した。混乱した理由は、channel の矢印の意味合いが理解できていなかったから。

func (client WorkspacesClient) CreateOrUpdate(resourceGroupName string, workspaceName string, parameters Workspace, cancel <-chan struct{}) (<-chan Workspace, <-chan error) {

パラメータにも、戻り値にも、channel があって、そこに矢印が付いている。これどういう意味?

Channel Direction

これは、チャネルの方向性の記述。

Channel

この絵の通りで、チャネルは、ベルトコンベアのようなもので、送る方と、受け取る方がある。製品を組み立てて、ベルトコンベアに載せる方と、受け取る方は例えば、それを箱詰めするとかそんなイメージ。

channel に置ける矢印の記法はそれを明確に表すためのもの。

<-chan は、送信のみのチャネルです

ic_send_only := make (<-chan int) 

chan<- は、受信のみのチャネルです。

ic_recv_only := make (chan<- int) 

なんとなく、ふわっとわかる気もするけど、よーわからんよ。しかも、パラメータと戻り値でもそれがあって、頭はパニックよ。試してみよう。

パラメータの Channel Direction

package main

import (
    "fmt"
    "time"
)

func receive(c chan<- string, message string) { // channel for send only
    c <- message
}

func send(c <-chan string) { // channel for recieve only
    fmt.Println(<-c)
}
func main() {
    c := make(chan string, 1)
    // c <- "hello then"
    receive(c, "hello")
    fmt.Println(<-c)

    c = make(chan string, 1)
    c <- "world"
    send(c)
}

実行結果

$ go run cmd/spike/main.go 
hello
world

ちなみに、試しにこんな感じに変えてみると、 fmt.Println(<-c) の部分でエラーになってコンパイルできなくなる。

func receive(c chan<- string, message string) { // channel for send
    fmt.Println(<-c)
    c <- message
}

よし、なんとか、パラメータの方は理解できたぞ。

戻り値の Channel Direction

じゃあ、戻り値はどうよ

func returnChannel() <-chan string {
    message := make(chan string, 1)
    message <- "hello world again"
    return message
}
func returnChannelReverse() chan<- string {
    message := make(chan string, 1)
    go func(msg chan string) {
        fmt.Println(<-msg)
    }(message)
    return message
}

func main() {
    b := returnChannel()
    fmt.Println(<-b)

    d := returnChannelReverse()
    d <- "hello world once upon a time"
    time.Sleep(100 * time.Millisecond)
}


戻り値の場合は、基本的に、関数の中で、チャネルを作って、返すけど、それが、送信専用か、受信専用かということになる。<-chan は、送信のみのチャネル(関数から見て)が戻り値だから、関数を呼んだ方は受信ができる。一方 chan<- は、受信のみのチャネル(関数から見て)が戻り値なので、関数を呼んだ方は、戻ってきたチャネルに対して送信ができる。という感じ。ちなみに、最後にスリープで待っているのは、ここでスリープを入れないと、 go routine がチャネルから受け付ける前にプログラムが終了するから。

うん。やっとスッキリした。

hello world again
hello world once upon a time

Reference

11
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
8