diff --git a/content/methods.article b/content/methods.article index 148e2d6..59d988b 100644 --- a/content/methods.article +++ b/content/methods.article @@ -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 @@ -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. @@ -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 @@ -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`: @@ -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 @@ -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 @@ -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! diff --git a/content/methods/exercise-reader.go b/content/methods/exercise-reader.go new file mode 100644 index 0000000..7428ae9 --- /dev/null +++ b/content/methods/exercise-reader.go @@ -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{}) +} diff --git a/content/methods/exercise-rot-reader.go b/content/methods/exercise-rot-reader.go index edf63ba..ff22119 100644 --- a/content/methods/exercise-rot-reader.go +++ b/content/methods/exercise-rot-reader.go @@ -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) } diff --git a/content/methods/exercise-stringer.go b/content/methods/exercise-stringer.go new file mode 100644 index 0000000..ef656b4 --- /dev/null +++ b/content/methods/exercise-stringer.go @@ -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) + } +} diff --git a/content/methods/reader.go b/content/methods/reader.go new file mode 100644 index 0000000..f22a1f2 --- /dev/null +++ b/content/methods/reader.go @@ -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 + } + } +} diff --git a/content/methods/stringer.go b/content/methods/stringer.go new file mode 100644 index 0000000..9fb98b5 --- /dev/null +++ b/content/methods/stringer.go @@ -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) +} diff --git a/reader/validate.go b/reader/validate.go new file mode 100644 index 0000000..21e2f45 --- /dev/null +++ b/reader/validate.go @@ -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!") +}