Skip to content
This repository has been archived by the owner on Nov 22, 2018. It is now read-only.

Commit

Permalink
go-tour: add Stringer, Reader discussions and exercises; other tweaks
Browse files Browse the repository at this point in the history
LGTM=campoy
R=campoy
CC=golang-codereviews
https://codereview.appspot.com/118170043
  • Loading branch information
adg committed Jul 30, 2014
1 parent e1dd6be commit 393eaa4
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 25 deletions.
117 changes: 94 additions & 23 deletions content/methods.article
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ The _method_receiver_ appears in its own argument list between the `func` keywor

* Methods continued

In fact, you can define a method on _any_ type you define in your package, not just structs.
You can declare a method on _any_ type that is declared in your package, not just struct types.

You cannot define a method on a type from another package, or on a basic type.
However, you cannot define a method on a type from another package (including built in types).

.play methods/methods-continued.go

Expand All @@ -42,18 +42,16 @@ An interface type is defined by a set of methods.

A value of interface type can hold any value that implements those methods.

*Note:* The code on the left fails to compile.

`Vertex` doesn't satisfy `Abser` because
the `Abs` method is defined only on `*Vertex`, not `Vertex`.
*Note:* There is an error in the example code on line 22.
`Vertex` (the value type) doesn't satisfy `Abser` because
the `Abs` method is defined only on `*Vertex` (the pointer type).

.play methods/interfaces.go

* Interfaces are satisfied implicitly

A type implements an interface by implementing the methods.

_There_is_no_explicit_declaration_of_intent._
There is no explicit declaration of intent; no "implements" keyword.

Implicit interfaces decouple implementation packages from the packages that define the interfaces: neither depends on the other.

Expand All @@ -63,15 +61,51 @@ It also encourages the definition of precise interfaces, because you don't have

.play methods/interfaces-are-satisfied-implicitly.go

* Stringers

One of the most ubiquitous interfaces is [[//golang.org/pkg/fmt/#Stringer][`Stringer`]] defined by the [[//golang.org/pkg/fmt/][`fmt`]] package.

type Stringer struct {
String() string
}

A `Stringer` is a type that can describe itself as a string. The `fmt` package
(and many others) look for this interface to print values.

.play methods/stringer.go

* Exercise: Stringers

Make the `IPAddr` type implement `fmt.Stringer` to print the address as
a dotted quad.

For instance, `IPAddr{1,`2,`3,`4}` should print as `"1.2.3.4"`.

.play methods/exercise-stringer.go

* Errors

An error is anything that can describe itself as an error string. The idea is captured by the predefined, built-in interface type, `error`, with its single method, `Error`, returning a string:
Go programs express error state with `error` values.

The `error` type is a built-in interface simliar to `fmt.Stringer`:

type error interface {
Error() string
}

The `fmt` package's various print routines automatically know to call the method when asked to print an `error`.
(As with `fmt.Stringer`, the `fmt` package looks for the `error` interface when
printing values.)

Functions often return an `error` value, and calling code should handle errors
by testing whether the error equals `nil`.

i, err := strconv.Atoi("42")
if err != nil {
fmt.Printf("couldn't convert number: %v\n", err)
}
fmt.Println("Converted integer:", i)

A nil `error` denotes success; a non-nil `error` denotes failure.

.play methods/errors.go

Expand All @@ -97,6 +131,47 @@ Change your `Sqrt` function to return an `ErrNegativeSqrt` value when given a ne

.play methods/exercise-errors.go

* Readers

The `io` package specifies the `io.Reader` interface,
which represents the read end of a stream of data.

The Go standard library contains [[http://golang.org/search?q=Read#Global][many implementations]] of these interfaces, including files, network connections, compressors, ciphers, and others.

The `io.Reader` interface has a `Read` method:

func (T) Read(b []byte) (n int, err error)

`Read` populates the given byte slice with data and returns the number of bytes
populated and an error value. It returns an `io.EOF` error when the stream
ends.

The example code creates a
[[//golang.org/pkg/strings/#Reader][`strings.Reader`]].
and consumes its output 8 bytes at a time.

.play methods/reader.go

* Exercise: Readers

Implement a `Reader` type that emits an infinite stream of the ASCII character
`'A'`.

.play methods/exercise-reader.go

* Exercise: rot13Reader

A common pattern is an [[http://golang.org/pkg/io/#Reader][io.Reader]] that wraps another `io.Reader`, modifying the stream in some way.

For example, the [[http://golang.org/pkg/compress/gzip/#NewReader][gzip.NewReader]] function takes an `io.Reader` (a stream of compressed data) and returns a `*gzip.Reader` that also implements `io.Reader` (a stream of the decompressed data).

Implement a `rot13Reader` that implements `io.Reader` and reads from an `io.Reader`, modifying the stream by applying the [[http://en.wikipedia.org/wiki/ROT13][rot13]] substitution cipher to all alphabetical characters.

The `rot13Reader` type is provided for you.
Make it an `io.Reader` by implementing its `Read` method.

.play methods/exercise-rot-reader.go

* Web servers

[[http://golang.org/pkg/net/http/][Package http]] serves HTTP requests using any value that implements `http.Handler`:
Expand Down Expand Up @@ -134,6 +209,10 @@ For example, you should be able to register handlers using:
http.Handle("/string", String("I'm a frayed knot."))
http.Handle("/struct", &Struct{"Hello", ":", "Gophers!"})

#appengine: *Note:* This example won't run through the web-based tour user
#appengine: interface. To try writing web servers you may want to
#appengine: [[http://golang.org/doc/install/][Install Go]].

.play methods/exercise-http-handlers.go

* Images
Expand All @@ -148,9 +227,13 @@ For example, you should be able to register handlers using:
At(x, y int) color.Color
}

*Note*: the `Rectangle` return value of the `Bounds` method is actually an
[[http://golang.org/pkg/image/#Rectangle][`image.Rectangle`]], as the
declaration is inside package `image`.

(See [[http://golang.org/pkg/image/#Image][the documentation]] for all the details.)

Also, `color.Color` and `color.Model` are interfaces, but we'll ignore that by using the predefined implementations `color.RGBA` and `color.RGBAModel`. These interfaces and types are specified by the [[http://golang.org/pkg/image/color/][image/color package]]
The `color.Color` and `color.Model` types are also interfaces, but we'll ignore that by using the predefined implementations `color.RGBA` and `color.RGBAModel`. These interfaces and types are specified by the [[http://golang.org/pkg/image/color/][image/color package]]

.play methods/images.go

Expand All @@ -168,18 +251,6 @@ Define your own `Image` type, implement [[http://golang.org/pkg/image/#Image][th

.play methods/exercise-images.go

* Exercise: Rot13 Reader

A common pattern is an [[http://golang.org/pkg/io/#Reader][io.Reader]] that wraps another `io.Reader`, modifying the stream in some way.

For example, the [[http://golang.org/pkg/compress/gzip/#NewReader][gzip.NewReader]] function takes an `io.Reader` (a stream of gzipped data) and returns a `*gzip.Reader` that also implements `io.Reader` (a stream of the decompressed data).

Implement a `rot13Reader` that implements `io.Reader` and reads from an `io.Reader`, modifying the stream by applying the [[http://en.wikipedia.org/wiki/ROT13][ROT13]] substitution cipher to all alphabetical characters.

The `rot13Reader` type is provided for you. Make it an `io.Reader` by implementing its `Read` method.

.play methods/exercise-rot-reader.go

* Congratulations!

You finished this lesson!
Expand Down
11 changes: 11 additions & 0 deletions content/methods/exercise-reader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package main

import "code.google.com/p/go-tour/reader"

type MyReader struct{}

// TODO: Add a Read(byte) (int, error) method to MyReader.

func main() {
reader.Validate(MyReader{})
}
3 changes: 1 addition & 2 deletions content/methods/exercise-rot-reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ type rot13Reader struct {
}

func main() {
s := strings.NewReader(
"Lbh penpxrq gur pbqr!")
s := strings.NewReader("Lbh penpxrq gur pbqr!")
r := rot13Reader{s}
io.Copy(os.Stdout, &r)
}
17 changes: 17 additions & 0 deletions content/methods/exercise-stringer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package main

import "fmt"

type IPAddr [4]byte

// TODO: Add a "String() string" method to IPAddr.

func main() {
addrs := map[string]IPAddr{
"loopback": {127, 0, 0, 1},
"googleDNS": {8, 8, 8, 8},
}
for n, a := range addrs {
fmt.Printf("%v: %v\n", n, a)
}
}
21 changes: 21 additions & 0 deletions content/methods/reader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package main

import (
"fmt"
"io"
"strings"
)

func main() {
r := strings.NewReader("Hello, Reader!")

b := make([]byte, 8)
for {
n, err := r.Read(b)
fmt.Printf("n = %v err = %v b = %v\n", n, err, b)
fmt.Printf("b[:n] = %q\n", b[:n])
if err == io.EOF {
break
}
}
}
18 changes: 18 additions & 0 deletions content/methods/stringer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package main

import "fmt"

type Person struct {
Name string
Age int
}

func (p Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}

func main() {
a := Person{"Arthur Dent", 42}
z := Person{"Zaphod Beeblebrox", 9001}
fmt.Println(a, z)
}
31 changes: 31 additions & 0 deletions reader/validate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package reader

import (
"fmt"
"io"
"os"
)

func Validate(r io.Reader) {
b := make([]byte, 1024)
i, o := 0, 0
for ; i < 1<<20 && o < 1<<20; i++ { // test 1mb
n, err := r.Read(b)
for i, v := range b[:n] {
if v != 'A' {
fmt.Fprintf(os.Stderr, "got byte %x at offset %v, want 'A'\n", v, o+i)
return
}
}
o += n
if err != nil {
fmt.Fprintf(os.Stderr, "read error: %v\n", err)
return
}
}
if o == 0 {
fmt.Fprintf(os.Stderr, "read zero bytes after %d Read calls\n", i)
return
}
fmt.Println("OK!")
}

0 comments on commit 393eaa4

Please sign in to comment.