go.talks: bestpractices: adding compelling example for error handling.

R=adg, r, dan.kortschak, nightlyone
CC=golang-dev
https://golang.org/cl/12636043
diff --git a/2013/bestpractices.slide b/2013/bestpractices.slide
index 3ee24bc..dec2ea5 100644
--- a/2013/bestpractices.slide
+++ b/2013/bestpractices.slide
@@ -24,13 +24,13 @@
 
 * Some code
 
-.code bestpractices/shortercode1.go /GOPHER/,/DUMP/
+.code bestpractices/shortercode1.go /type Gopher/,/^}/
 
-.code bestpractices/shortercode1.go /DUMP/,/MAIN/
+.code bestpractices/shortercode1.go /WriteTo/,/^}/
 
 * Avoid nesting by handling errors first
 
-.code bestpractices/shortercode2.go /DUMP/,/MAIN/
+.code bestpractices/shortercode2.go /WriteTo/,/^}/
 
 Less nesting means less cognitive load on the reader
 
@@ -38,19 +38,37 @@
 
 Deploy one-off utility types for simpler code
 
-.code bestpractices/shortercode3.go /BINWRITER/,/DUMP/
+.code bestpractices/shortercode3.go /binWriter/,/^}/
 
-.code bestpractices/shortercode3.go /DUMP/,/MAIN/
+.code bestpractices/shortercode3.go /Write writes/,/^}/
+
+* Avoid repetition when possible
+
+Using `binWriter`
+
+.code bestpractices/shortercode3.go /WriteTo/,/^}/
 
 * Type switch to handle special cases
 
-.code bestpractices/shortercode4.go /WRITE/,/DUMP/
+.code bestpractices/shortercode4.go /func .* Write/,/^}/
 
-.code bestpractices/shortercode4.go /DUMP/,/MAIN/
+.code bestpractices/shortercode4.go /WriteTo/,/^}/
 
 * Type switch with short variable declaration
 
-.code bestpractices/shortercode5.go /WRITE/,/DUMP/
+.code bestpractices/shortercode5.go /func .* Write/,/^}/
+
+* Writing everything or nothing
+
+.code bestpractices/shortercode6.go /binWriter/,/^}/
+
+.code bestpractices/shortercode6.go /Write writes/,/^}/
+
+* Writing everything or nothing
+
+.code bestpractices/shortercode6.go /Flush/,/^}/
+
+.code bestpractices/shortercode6.go /func .* WriteTo/,/^}/
 
 * Function adapters
 
@@ -154,19 +172,19 @@
 
 Let's use the Gopher type from before
 
-.code bestpractices/shortercode1.go /GOPHER/,/DUMP/
+.code bestpractices/shortercode1.go /type Gopher/,/^}/
 
 We could define this method
 
-.code bestpractices/shortercode1.go /DumpToFile/,/DumpToFile/
+.code bestpractices/shortercode1.go /WriteToFile/
 
 But using a concrete type makes this code difficult to test, so we use an interface.
 
-.code bestpractices/shortercode1.go /DumpToReadWriter/,/DumpToReadWriter/
+.code bestpractices/shortercode1.go /WriteToReadWriter/
 
 And, since we're using an interface, we should ask only for the methods we need.
 
-.code bestpractices/shortercode1.go /DumpToWriter/,/DumpToWriter/
+.code bestpractices/shortercode1.go /WriteToWriter/
 
 * Keep independent packages independent
 
diff --git a/2013/bestpractices/shortercode1.go b/2013/bestpractices/shortercode1.go
index 51dffb3..cfcbc74 100644
--- a/2013/bestpractices/shortercode1.go
+++ b/2013/bestpractices/shortercode1.go
@@ -1,61 +1,61 @@
-// +build ignore,OMIT
+// +build OMIT
 
 package main
 
 import (
 	"encoding/binary"
-	"image/color"
 	"io"
 	"log"
 	"os"
 )
 
-// GOPHER OMIT
 type Gopher struct {
 	Name     string
-	Age      int32
-	FurColor color.Color
+	AgeYears int
 }
 
-// DUMP OMIT
-func (g *Gopher) DumpBinary(w io.Writer) error {
-	err := binary.Write(w, binary.LittleEndian, int32(len(g.Name)))
+// Example of bad code, missing early return. OMIT
+func (g *Gopher) WriteTo(w io.Writer) (size int64, err error) {
+	err = binary.Write(w, binary.LittleEndian, int32(len(g.Name)))
 	if err == nil {
-		_, err := w.Write([]byte(g.Name))
+		size += 4
+		var n int
+		n, err = w.Write([]byte(g.Name))
+		size += int64(n)
 		if err == nil {
-			err := binary.Write(w, binary.LittleEndian, g.Age)
+			err = binary.Write(w, binary.LittleEndian, int64(g.AgeYears))
 			if err == nil {
-				return binary.Write(w, binary.LittleEndian, g.FurColor)
+				size += 4
 			}
-			return err
+			return
 		}
-		return err
+		return
 	}
-	return err
+	return
 }
 
-// MAIN OMIT
 func main() {
-	w := os.Stdout
 	g := &Gopher{
 		Name:     "Gophertiti",
-		Age:      3383,
-		FurColor: color.RGBA{B: 255},
+		AgeYears: 3382,
 	}
 
-	if err := g.DumpBinary(w); err != nil {
-		log.Fatal("DumpBinary: %v", err)
+	if _, err := g.WriteTo(os.Stdout); err != nil {
+		log.Printf("DumpBinary: %v\n", err)
 	}
 }
 
-func (g *Gopher) DumpToFile(f *os.File) error {
-	return nil
+// Example of bad API, it's better to use an interface.
+func (g *Gopher) WriteToFile(f *os.File) (int64, error) {
+	return 0, nil
 }
 
-func (g *Gopher) DumpToReadWriter(rw io.ReadWriter) error {
-	return nil
+// Example of bad API, it's better to use a narrower interface.
+func (g *Gopher) WriteToReadWriter(rw io.ReadWriter) (int64, error) {
+	return 0, nil
 }
 
-func (g *Gopher) DumpToWriter(f io.Writer) error {
-	return nil
+// Example of better API.
+func (g *Gopher) WriteToWriter(f io.Writer) (int64, error) {
+	return 0, nil
 }
diff --git a/2013/bestpractices/shortercode2.go b/2013/bestpractices/shortercode2.go
index 082457c..4971dbe 100644
--- a/2013/bestpractices/shortercode2.go
+++ b/2013/bestpractices/shortercode2.go
@@ -1,49 +1,44 @@
-// +build ignore,OMIT
+// +build OMIT
 
 package main
 
 import (
 	"encoding/binary"
-	"image/color"
 	"io"
 	"log"
 	"os"
 )
 
-// GOPHER OMIT
 type Gopher struct {
 	Name     string
-	Age      int32
-	FurColor color.Color
+	AgeYears int
 }
 
-// DUMP OMIT
-func (g *Gopher) DumpBinary(w io.Writer) error {
-	err := binary.Write(w, binary.LittleEndian, int32(len(g.Name)))
+func (g *Gopher) WriteTo(w io.Writer) (size int64, err error) {
+	err = binary.Write(w, binary.LittleEndian, int32(len(g.Name)))
 	if err != nil {
-		return err
+		return
 	}
-	_, err = w.Write([]byte(g.Name))
+	size += 4
+	n, err := w.Write([]byte(g.Name))
+	size += int64(n)
 	if err != nil {
-		return err
+		return
 	}
-	err = binary.Write(w, binary.LittleEndian, g.Age)
-	if err != nil {
-		return err
+	err = binary.Write(w, binary.LittleEndian, int64(g.AgeYears))
+	if err == nil {
+		size += 4
 	}
-	return binary.Write(w, binary.LittleEndian, g.FurColor)
+	return
 }
 
-// MAIN OMIT
 func main() {
-	w := os.Stdout
 	g := &Gopher{
 		Name:     "Gophertiti",
-		Age:      3383,
-		FurColor: color.RGBA{B: 255},
+		AgeYears: 3382,
 	}
 
-	if err := g.DumpBinary(w); err != nil {
-		log.Fatal("DumpBinary: %v", err)
+	if _, err := g.WriteTo(os.Stdout); err != nil {
+		log.Printf("DumpBinary: %v\n", err)
 	}
 }
diff --git a/2013/bestpractices/shortercode3.go b/2013/bestpractices/shortercode3.go
index 47d10a7..47eca96 100644
--- a/2013/bestpractices/shortercode3.go
+++ b/2013/bestpractices/shortercode3.go
@@ -1,57 +1,50 @@
-// +build ignore,OMIT
+// +build OMIT
 
 package main
 
 import (
 	"encoding/binary"
-	"image/color"
 	"io"
 	"log"
 	"os"
 )
 
-// GOPHER OMIT
 type Gopher struct {
 	Name     string
-	Age      int32
-	FurColor color.Color
+	AgeYears int
 }
 
-// BINWRITER OMIT
 type binWriter struct {
-	w   io.Writer
-	err error
+	w    io.Writer
+	size int64
+	err  error
 }
 
-// WRITE OMIT
-// Write writes a value into its writer using little endian.
+// Write writes a value to the provided writer in little endian form.
 func (w *binWriter) Write(v interface{}) {
 	if w.err != nil {
 		return
 	}
-	w.err = binary.Write(w.w, binary.LittleEndian, v)
+	if w.err = binary.Write(w.w, binary.LittleEndian, v); w.err == nil {
+		w.size += int64(binary.Size(v))
+	}
 }
 
-// DUMP OMIT
-func (g *Gopher) DumpBinary(w io.Writer) error {
+func (g *Gopher) WriteTo(w io.Writer) (int64, error) {
 	bw := &binWriter{w: w}
 	bw.Write(int32(len(g.Name)))
 	bw.Write([]byte(g.Name))
-	bw.Write(g.Age)
-	bw.Write(g.FurColor)
-	return bw.err
+	bw.Write(int64(g.AgeYears))
+	return bw.size, bw.err
 }
 
-// MAIN OMIT
 func main() {
-	w := os.Stdout
 	g := &Gopher{
 		Name:     "Gophertiti",
-		Age:      3383,
-		FurColor: color.RGBA{B: 255},
+		AgeYears: 3382,
 	}
 
-	if err := g.DumpBinary(w); err != nil {
-		log.Fatal("DumpBinary: %v", err)
+	if _, err := g.WriteTo(os.Stdout); err != nil {
+		log.Printf("DumpBinary: %v\n", err)
 	}
 }
diff --git a/2013/bestpractices/shortercode4.go b/2013/bestpractices/shortercode4.go
index fd7f6cc..a026463 100644
--- a/2013/bestpractices/shortercode4.go
+++ b/2013/bestpractices/shortercode4.go
@@ -1,62 +1,59 @@
-// +build ignore,OMIT
+// +build OMIT
 
 package main
 
 import (
 	"encoding/binary"
-	"image/color"
 	"io"
 	"log"
 	"os"
 )
 
-// GOPHER OMIT
 type Gopher struct {
 	Name     string
-	Age      int32
-	FurColor color.Color
+	AgeYears int
 }
 
 type binWriter struct {
-	w   io.Writer
-	err error
+	w    io.Writer
+	size int64
+	err  error
 }
 
-// WRITE OMIT
-// Write writes a value into its writer using little endian.
+// Write writes a value to the provided writer in little endian form.
 func (w *binWriter) Write(v interface{}) {
 	if w.err != nil {
 		return
 	}
-	switch v.(type) {
+	switch v.(type) { // HL
 	case string:
 		s := v.(string)
 		w.Write(int32(len(s)))
 		w.Write([]byte(s))
+	case int:
+		i := v.(int)
+		w.Write(int64(i))
 	default:
-		w.err = binary.Write(w.w, binary.LittleEndian, v)
+		if w.err = binary.Write(w.w, binary.LittleEndian, v); w.err == nil {
+			w.size += int64(binary.Size(v))
+		}
 	}
 }
 
-// DUMP OMIT
-func (g *Gopher) DumpBinary(w io.Writer) error {
+func (g *Gopher) WriteTo(w io.Writer) (int64, error) {
 	bw := &binWriter{w: w}
-	bw.Write(g.Name)
-	bw.Write(g.Age)
-	bw.Write(g.FurColor)
-	return bw.err
+	bw.Write(g.Name) // HL
+	bw.Write(g.AgeYears)
+	return bw.size, bw.err
 }
 
-// MAIN OMIT
 func main() {
-	w := os.Stdout
 	g := &Gopher{
 		Name:     "Gophertiti",
-		Age:      3383,
-		FurColor: color.RGBA{B: 255},
+		AgeYears: 3382,
 	}
 
-	if err := g.DumpBinary(w); err != nil {
-		log.Fatal("DumpBinary: %v", err)
+	if _, err := g.WriteTo(os.Stdout); err != nil {
+		log.Printf("DumpBinary: %v\n", err)
 	}
 }
diff --git a/2013/bestpractices/shortercode5.go b/2013/bestpractices/shortercode5.go
index 39a280d..e9234bf 100644
--- a/2013/bestpractices/shortercode5.go
+++ b/2013/bestpractices/shortercode5.go
@@ -1,61 +1,57 @@
-// +build ignore,OMIT
+// +build OMIT
 
 package main
 
 import (
 	"encoding/binary"
-	"image/color"
 	"io"
 	"log"
 	"os"
 )
 
-// GOPHER OMIT
 type Gopher struct {
 	Name     string
-	Age      int32
-	FurColor color.Color
+	AgeYears int
 }
 
 type binWriter struct {
-	w   io.Writer
-	err error
+	w    io.Writer
+	size int64
+	err  error
 }
 
-// WRITE OMIT
-// Write write the given value into the writer using little endian.
+// Write writes a value to the provided writer in little endian form.
 func (w *binWriter) Write(v interface{}) {
 	if w.err != nil {
 		return
 	}
-	switch v := v.(type) {
+	switch x := v.(type) { // HL
 	case string:
-		w.Write(int32(len(v)))
-		w.Write([]byte(v))
+		w.Write(int32(len(x)))
+		w.Write([]byte(x))
+	case int:
+		w.Write(int64(x))
 	default:
-		w.err = binary.Write(w.w, binary.LittleEndian, v)
+		if w.err = binary.Write(w.w, binary.LittleEndian, v); w.err == nil {
+			w.size += int64(binary.Size(v))
+		}
 	}
 }
 
-// DUMP OMIT
-func (g *Gopher) DumpBinary(w io.Writer) error {
+func (g *Gopher) WriteTo(w io.Writer) (int64, error) {
 	bw := &binWriter{w: w}
 	bw.Write(g.Name)
-	bw.Write(g.Age)
-	bw.Write(g.FurColor)
-	return bw.err
+	bw.Write(g.AgeYears)
+	return bw.size, bw.err
 }
 
-// MAIN OMIT
 func main() {
-	w := os.Stdout
 	g := &Gopher{
 		Name:     "Gophertiti",
-		Age:      3383,
-		FurColor: color.RGBA{B: 255},
+		AgeYears: 3382,
 	}
 
-	if err := g.DumpBinary(w); err != nil {
-		log.Fatal("DumpBinary: %v", err)
+	if _, err := g.WriteTo(os.Stdout); err != nil {
+		log.Printf("DumpBinary: %v\n", err)
 	}
 }
diff --git a/2013/bestpractices/shortercode6.go b/2013/bestpractices/shortercode6.go
new file mode 100644
index 0000000..0c6a79c
--- /dev/null
+++ b/2013/bestpractices/shortercode6.go
@@ -0,0 +1,66 @@
+// +build OMIT
+
+package main
+
+import (
+	"bytes"
+	"encoding/binary"
+	"io"
+	"log"
+	"os"
+)
+
+type Gopher struct {
+	Name     string
+	AgeYears int
+}
+
+type binWriter struct {
+	w   io.Writer
+	buf bytes.Buffer // HL
+	err error
+}
+
+// Write writes a value to the provided writer in little endian form.
+func (w *binWriter) Write(v interface{}) {
+	if w.err != nil {
+		return
+	}
+	switch x := v.(type) {
+	case string:
+		w.Write(int32(len(x)))
+		w.Write([]byte(x))
+	case int:
+		w.Write(int64(x))
+	default:
+		w.err = binary.Write(&w.buf, binary.LittleEndian, v) // HL
+	}
+}
+
+// Flush writes any pending values into the writer if no error has occurred.
+// If an error has occurred, earlier or with a write by Flush, the error is
+// returned.
+func (w *binWriter) Flush() (int64, error) {
+	if w.err != nil {
+		return 0, w.err
+	}
+	return w.buf.WriteTo(w.w)
+}
+
+func (g *Gopher) WriteTo(w io.Writer) (int64, error) {
+	bw := &binWriter{w: w}
+	bw.Write(g.Name)
+	bw.Write(g.AgeYears)
+	return bw.Flush() // HL
+}
+
+func main() {
+	g := &Gopher{
+		Name:     "Gophertiti",
+		AgeYears: 3382,
+	}
+
+	if _, err := g.WriteTo(os.Stdout); err != nil {
+		log.Printf("DumpBinary: %v\n", err)
+	}
+}