io.Reader, io.Writer, and io.ReadWriter are some of the most powerful, yet surprisingly subtle, tools in the Go standard library. In this talk, we'll explore two concrete examples leveraging these building blocks to construct expressive and performant abstractions. We'll also cover details of package bufio, the LimitReader, and other techniques to rain kittens on our code and dial it up to 11.
8. Basics - Composition
var r io.Reader
r = strings.NewReader("1234567890")
r = io.LimitReader(r, 5)
io.Copy(os.Stdout, r)
Setup:
Output:
9. Basics - Composition
var r io.Reader
r = strings.NewReader("1234567890")
r = io.LimitReader(r, 5)
io.Copy(gzip.NewWriter(os.Stdout), r)
Setup:
Output:
18. io.Copy
Solution 2
Problems:
• Strips out the chunking data
• Can’t validate the MD5
Better solution with httputil.ChunkedReader
responseBody clientWriterChunkedReader
22. Example 2 - Multiplexing
Run subcommands, multiplex the output, give each a
unique prefix.
echo “HellonWorld!”
make all
git status
[echo] Hello,
[make] make: *** No rule to make target.
[echo] World!
[git] On branch master
[git] Your branch is up-to-date
[git] nothing to commit
26. Example 2 - Multiplexing
Splitting streams into sections with bufio.Scanner
input := ...
// Iterate over each line
scanner := bufio.NewScanner(input)
scanner.SplitFunc(bufio.ScanLines)
for scanner.Scan() {
// Write the prefix into the output
fmt.Fprintf(output, "[%s] ", prefix)
// Copy the line
output.Write(scanner.Bytes())
// Re-add a newline (scanner removes it)
fmt.Fprint(output, "n")
}
27. Example 2 - Multiplexing
Splitting streams into sections with bufio.Scanner
input := ...
// Iterate over each line
scanner := bufio.NewScanner(input)
scanner.SplitFunc(bufio.ScanLines)
for scanner.Scan() {
// Write the prefix into the output
fmt.Fprintf(output, "[%s] ", prefix)
// Copy the line
output.Write(scanner.Bytes())
// Re-add a newline (scanner removes it)
fmt.Fprint(output, "n")
}
28. Example 2 - Multiplexing
Splitting streams into sections with bufio.Scanner
input := ...
// Iterate over each line
scanner := bufio.NewScanner(input)
scanner.SplitFunc(bufio.ScanLines)
for scanner.Scan() {
// Write the prefix into the output
fmt.Fprintf(output, "[%s] ", prefix)
// Copy the line
output.Write(scanner.Bytes())
// Re-add a newline (scanner removes it)
fmt.Fprint(output, "n")
}
29. Example 2 - Multiplexing
Splitting streams into sections with bufio.Scanner
input := ...
// Iterate over each line
scanner := bufio.NewScanner(input)
scanner.SplitFunc(bufio.ScanLines)
for scanner.Scan() {
// Write the prefix into the output
fmt.Fprintf(output, "[%s] ", prefix)
// Copy the line
output.Write(scanner.Bytes())
// Re-add a newline (scanner removes it)
fmt.Fprint(output, "n")
}
30. Example 2 - Multiplexing
Splitting streams into sections with bufio.Scanner
input := ...
// Iterate over each line
scanner := bufio.NewScanner(input)
scanner.SplitFunc(bufio.ScanLines)
for scanner.Scan() {
// Write the prefix into the output
fmt.Fprintf(output, "[%s] ", prefix)
// Copy the line
output.Write(scanner.Bytes())
// Re-add a newline (scanner removes it)
fmt.Fprint(output, "n")
}
31. Example 2 - Multiplexing
Splitting streams into sections with bufio.Scanner
input := ...
// Iterate over each line
scanner := bufio.NewScanner(input)
scanner.SplitFunc(bufio.ScanLines)
for scanner.Scan() {
// Write the prefix into the output
fmt.Fprintf(output, "[%s] ", prefix)
// Copy the line
output.Write(scanner.Bytes())
// Re-add a newline (scanner removes it)
fmt.Fprint(output, "n")
}
32. Example 2 - Multiplexing
Splitting streams into sections with bufio.Scanner
input := ...
// Iterate over each line
scanner := bufio.NewScanner(input)
scanner.SplitFunc(bufio.ScanLines)
for scanner.Scan() {
// Write the prefix into the output
fmt.Fprintf(output, "[%s] ", prefix)
// Copy the line
output.Write(scanner.Bytes())
// Re-add a newline (scanner removes it)
fmt.Fprint(output, "n")
}
33. Example 2 - Multiplexing
What is input?
• bufio.Scanner expects a io.Reader
• exec.Cmd expects a io.Writer
34. goroutine
Example 2 - Multiplexing
pipeWriter bufio.Scannerio.Pipe output
io.Pipe to the rescue