You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: content/advent-2019/directional-channels.md
+28-5Lines changed: 28 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -36,26 +36,49 @@ var receiveOnlyChan <-chan string // can read from, but cannot write to or close
36
36
varsendOnlyChanchan<-string// cannot read from, but can write to and close()
37
37
```
38
38
39
+
A good way to remember how this works is that, in declarations, the arrow indicates how the channel is allowd to be used:
40
+
```
41
+
<-chan // data only comes out
42
+
chan<- // data only goes in
43
+
```
44
+
39
45
At first glance, this might seem pretty useless --how useful is a new channel if it can't work in both directions?-- but there's another important line in the spec in the very same paragraph:
40
46
41
47
> A channel may be constrained only to send or only to receive by **assignment** or **explicit conversion**.
42
48
43
-
This means channels can start out bidirectional, but magically _become_ directional simply by assigning a regular channel to a variable of a constrained type (or passing it into a function with a constrained channel argument, which accomplishes the same thing). This is very useful for creating receive-only channels that no one can close but you.
49
+
This means channels can start out bidirectional, but magically _become_ directional simply by assigning a regular channel to a variable of a constrained type. This is very useful for creating receive-only channels that no one can close but you.
44
50
45
51
## Receive-only Channels
46
52
47
53
```go
48
54
varbiDirectionalchanstring
49
55
varreadOnly<-chanstring
50
-
functakesReadonly(c <-chanstring){}
51
56
52
57
biDirectional = make(chanstring)
53
58
54
59
takesReadonly(biDirectional)
55
60
readOnly = biDirectional
56
61
```
57
62
58
-
`readOnly` now shares the same underlying channel, as `biDirectional`, but it cannot be written to *or* closed. Most crucially, this distinction is part of its *type*, which means these restrictions can be enforced at *compile time*.
63
+
`readOnly` now shares the same underlying channel, as `biDirectional`, but it cannot be written to *or* closed. This can also be done on the way into our out of a function, simply by specifying a direction in the argument or return type:
64
+
65
+
```go
66
+
functakesReadonly(c <-chanstring){
67
+
// c is now receive-only inside the function and anywhere else it might go from here
68
+
}
69
+
70
+
funcreturnsReadOnly() <-chanstring{
71
+
c:=make(chanstring)
72
+
gofunc(){
73
+
// some concurrent work with c
74
+
}()
75
+
return c
76
+
}
77
+
readOnly:=returnsReadOnly()
78
+
79
+
```
80
+
81
+
This is a pretty nifty trick, and works a bit differently to conversions in the rest of the language, but, most crucially, the change in direction is reflected in the *type*, which means these restrictions can be enforced at *compile time*.
This is useful not only to control who can write to or close your channel, but also in terms of descriptiveness and Intentionality. One of the nice things about strongly-typed languages like Go is that they can be tremendously descriptive just through their API. Take the following function as an example:
76
99
77
100
```go
78
-
funcSliceIterChan(s []int) <-chanint
101
+
funcSliceIterChan(s []int) <-chanint {}
79
102
```
80
103
81
104
Even without the documentation or implementation, this code unambiguously states that it returns a channel that the consumer is supposed to read from, either forever, or until it's closed (which documentation can help clarify). This lends itself very well to a **for-range** over the provided channel.
@@ -87,7 +110,7 @@ for i := range SliceIterChan(someSlice) {
87
110
fmt.Println("channel closed!")
88
111
```
89
112
90
-
Diving into the implementation, the function creates a bidirectional channel for its own use, and then all it needs to do to ensure that it has full control over writing to and closing the channel is to return it, whereupon it will be converted into a receive-only channel automatically.
113
+
Diving into the implementation, the function creates a bidirectional channel for its own use, and then all it needs to do to ensure that it has full control over writing to and closing the channel is to return it, whereupon it will be converted into a read-only channel automatically.
91
114
92
115
```go
93
116
// SliceIterChan returns each element of a slice on a channel for concurrent
0 commit comments