content: expand discussion of interfaces
Change-Id: Ib1463519bac971b5b19952ca366de8bad58c25d7
Reviewed-on: https://go-review.googlesource.com/18425
Reviewed-by: Rob Pike <r@golang.org>
diff --git a/content/methods.article b/content/methods.article
index 9319062..ec99b7e 100644
--- a/content/methods.article
+++ b/content/methods.article
@@ -142,29 +142,131 @@
* Interfaces
-An interface type is defined by a set of methods.
+An _interface_type_ is defined as a set of method signatures.
A value of interface type can hold any value that implements those methods.
*Note:* There is an error in the example code on line 22.
-`Vertex` (the value type) doesn't satisfy `Abser` because
+`Vertex` (the value type) doesn't implement `Abser` because
the `Abs` method is defined only on `*Vertex` (the pointer type).
.play methods/interfaces.go
-* Interfaces are satisfied implicitly
+* Interfaces are implemented implicitly
-A type implements an interface by implementing the methods.
-There is no explicit declaration of intent; no "implements" keyword.
+A type implements an interface by implementing its methods.
+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.
-
-It also encourages the definition of precise interfaces, because you don't have to find every implementation and tag it with the new interface name.
-
-[[https://golang.org/pkg/io/][Package io]] defines `Reader` and `Writer`; you don't have to.
+Implicit interfaces decouple the definition of an interface from its
+implementation, which could then appear in any package without prearrangement.
.play methods/interfaces-are-satisfied-implicitly.go
+* Interface values
+
+Under the covers, interface values can be thought of as a tuple of a value and a
+concrete type:
+
+ (value, type)
+
+An interface value holds a value of a specific underlying concrete type.
+
+Calling a method on an interface value executes the method of the same name on
+its underlying type.
+
+.play methods/interface-values.go
+
+* Interface values with nil underlying values
+
+If the concrete value inside the interface itself is nil,
+the method will be called with a nil receiver.
+
+In some languages this would trigger a null pointer exception,
+but in Go it is common to write methods that gracefully handle being called
+with a nil receiver (as with the method `M` in this example.)
+
+Note that an interface value that holds a nil concrete value is itself non-nil.
+
+.play methods/interface-values-with-nil.go
+
+* Nil interface values
+
+A nil interface value holds neither value nor concrete type.
+
+Calling a method on a nil interface is a run-time error because there is no
+type inside the interface tuple to indicate which _concrete_ method to call.
+
+.play methods/nil-interface-values.go
+
+* The empty interface
+
+The interface type that specifies zero methods is known as the _empty_interface_:
+
+ interface{}
+
+An empty interface may hold values of any type.
+(Every type implements at least zero methods.)
+
+Empty interfaces are used by code that handles values of unknown type.
+For example, `fmt.Print` takes any number of arguments of type `interface{}`.
+
+.play methods/empty-interface.go
+
+* Type assertions
+
+A _type_assertion_ provides access to an interface value's underlying concrete value.
+
+ t := i.(T)
+
+This statement asserts that the interface value `i` holds the concrete type `T`
+and assigns the underlying `T` value to the variable `t`.
+
+If `i` does not hold a `T`, the statement will trigger a panic.
+
+To _test_ whether an interface value holds a specific type,
+a type assertion can return two values: the underlying value
+and a boolean value that reports whether the assertion succeeded.
+
+ t, ok := i.(T)
+
+If `i` holds a `T`, then `t` will be the underlying value and `ok` will be true.
+
+If not, `ok` will be false and `t` will be the zero value of type `T`,
+and no panic occurs.
+
+Note the similarity between this syntax and that of reading from a map.
+
+.play methods/type-assertions.go
+
+* Type switches
+
+A _type_switch_ is a construct that permits several type assertions in series.
+
+A type switch is like a regular switch statement, but the cases in a type
+switch specify types (not values), and those values are compared against
+the type of the value held by the given interface value.
+
+ switch v := i.(type) {
+ case T:
+ // here v has type T
+ case S:
+ // here v has type S
+ default:
+ // no match; here v has the same type as i
+ }
+
+The declaration in a type switch has the same syntax as a type assertion `i.(T)`,
+but the specific type `T` is replaced with the keyword `type`.
+
+This switch statement tests whether the interface value `i`
+holds a value of type `T` or `S`.
+In each of the `T` and `S` cases, the variable `v` will be of type
+`T` or `S` respectively and hold the value held by `i`.
+In the default case (where there is no match), the variable `v` is
+of the same interface type and value as `i`.
+
+.play methods/type-switches.go
+
* Stringers
One of the most ubiquitous interfaces is [[//golang.org/pkg/fmt/#Stringer][`Stringer`]] defined by the [[//golang.org/pkg/fmt/][`fmt`]] package.
@@ -277,56 +379,6 @@
.play methods/exercise-rot-reader.go
-* Web servers
-
-[[https://golang.org/pkg/net/http/][Package http]] serves HTTP requests using any value that implements `http.Handler`:
-
- package http
-
- type Handler interface {
- ServeHTTP(w ResponseWriter, r *Request)
- }
-
-In this example, the type `Hello` implements `http.Handler`.
-
-Visit [[http://localhost:4000/][http://localhost:4000/]] to see the greeting.
-
-#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: [[https://golang.org/dl/][Install Go]].
-
-.play methods/web-servers.go
-
-* Exercise: HTTP Handlers
-
-Implement the following types and define ServeHTTP methods on them. Register them to handle specific paths in your web server.
-
- type String string
-
- type Struct struct {
- Greeting string
- Punct string
- Who string
- }
-
-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!"})
-
-After starting your server, you will be able to visit
-[[http://localhost:4000/string][http://localhost:4000/string]] and
-[[http://localhost:4000/struct][http://localhost:4000/struct]].
-
-For more details about the `http.Handle` function, see
-[[https://golang.org/pkg/net/http/#Handle][the documentation]].
-
-#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: [[https://golang.org/dl/][Install Go]].
-
-.play methods/exercise-http-handlers.go
-
* Images
[[https://golang.org/pkg/image/#Image][Package image]] defines the `Image` interface:
diff --git a/content/methods/empty-interface.go b/content/methods/empty-interface.go
new file mode 100644
index 0000000..10e9861
--- /dev/null
+++ b/content/methods/empty-interface.go
@@ -0,0 +1,20 @@
+// +build OMIT
+
+package main
+
+import "fmt"
+
+func main() {
+ var i interface{}
+ describe(i)
+
+ i = 42
+ describe(i)
+
+ i = "hello"
+ describe(i)
+}
+
+func describe(i interface{}) {
+ fmt.Printf("(%v, %T)\n", i, i)
+}
diff --git a/content/methods/exercise-http-handlers.go b/content/methods/exercise-http-handlers.go
deleted file mode 100644
index a824531..0000000
--- a/content/methods/exercise-http-handlers.go
+++ /dev/null
@@ -1,13 +0,0 @@
-// +build OMIT
-
-package main
-
-import (
- "log"
- "net/http"
-)
-
-func main() {
- // your http.Handle calls here
- log.Fatal(http.ListenAndServe("localhost:4000", nil))
-}
diff --git a/content/methods/interface-values-with-nil.go b/content/methods/interface-values-with-nil.go
new file mode 100644
index 0000000..60d69d5
--- /dev/null
+++ b/content/methods/interface-values-with-nil.go
@@ -0,0 +1,38 @@
+// build +OMIT
+
+package main
+
+import "fmt"
+
+type I interface {
+ M()
+}
+
+type T struct {
+ S string
+}
+
+func (t *T) M() {
+ if t == nil {
+ fmt.Println("<nil>")
+ return
+ }
+ fmt.Println(t.S)
+}
+
+func main() {
+ var i I
+
+ var t *T
+ i = t
+ describe(i)
+ i.M()
+
+ i = &T{"hello"}
+ describe(i)
+ i.M()
+}
+
+func describe(i I) {
+ fmt.Printf("(%v, %T)\n", i, i)
+}
diff --git a/content/methods/interface-values.go b/content/methods/interface-values.go
new file mode 100644
index 0000000..52d2b3d
--- /dev/null
+++ b/content/methods/interface-values.go
@@ -0,0 +1,42 @@
+// build +OMIT
+
+package main
+
+import (
+ "fmt"
+ "math"
+)
+
+type I interface {
+ M()
+}
+
+type T struct {
+ S string
+}
+
+func (t *T) M() {
+ fmt.Println(t.S)
+}
+
+type F float64
+
+func (f F) M() {
+ fmt.Println(f)
+}
+
+func main() {
+ var i I
+
+ i = &T{"Hello"}
+ describe(i)
+ i.M()
+
+ i = F(math.Pi)
+ describe(i)
+ i.M()
+}
+
+func describe(i I) {
+ fmt.Printf("(%v, %T)\n", i, i)
+}
diff --git a/content/methods/interfaces-are-satisfied-implicitly.go b/content/methods/interfaces-are-satisfied-implicitly.go
index d7f2589..4bc2232 100644
--- a/content/methods/interfaces-are-satisfied-implicitly.go
+++ b/content/methods/interfaces-are-satisfied-implicitly.go
@@ -2,29 +2,23 @@
package main
-import (
- "fmt"
- "os"
-)
+import "fmt"
-type Reader interface {
- Read(b []byte) (n int, err error)
+type I interface {
+ M()
}
-type Writer interface {
- Write(b []byte) (n int, err error)
+type T struct {
+ S string
}
-type ReadWriter interface {
- Reader
- Writer
+// This method means type T implements the interface I,
+// but we don't need to explicitly declare that it does so.
+func (t T) M() {
+ fmt.Println(t.S)
}
func main() {
- var w Writer
-
- // os.Stdout implements Writer
- w = os.Stdout
-
- fmt.Fprintf(w, "hello, writer\n")
+ var i I = &T{"hello"}
+ i.M()
}
diff --git a/content/methods/nil-interface-values.go b/content/methods/nil-interface-values.go
new file mode 100644
index 0000000..e15bdc9
--- /dev/null
+++ b/content/methods/nil-interface-values.go
@@ -0,0 +1,19 @@
+// build +OMIT
+
+package main
+
+import "fmt"
+
+type I interface {
+ M()
+}
+
+func main() {
+ var i I
+ describe(i)
+ i.M()
+}
+
+func describe(i I) {
+ fmt.Printf("(%v, %T)\n", i, i)
+}
diff --git a/content/methods/type-assertions.go b/content/methods/type-assertions.go
new file mode 100644
index 0000000..fcd5685
--- /dev/null
+++ b/content/methods/type-assertions.go
@@ -0,0 +1,21 @@
+// build +OMIT
+
+package main
+
+import "fmt"
+
+func main() {
+ var i interface{} = "hello"
+
+ s := i.(string)
+ fmt.Println(s)
+
+ s, ok := i.(string)
+ fmt.Println(s, ok)
+
+ f, ok := i.(float64)
+ fmt.Println(f, ok)
+
+ f = i.(float64) // panic
+ fmt.Println(f)
+}
diff --git a/content/methods/type-switches.go b/content/methods/type-switches.go
new file mode 100644
index 0000000..12d879d
--- /dev/null
+++ b/content/methods/type-switches.go
@@ -0,0 +1,22 @@
+// +build OMIT
+
+package main
+
+import "fmt"
+
+func do(i interface{}) {
+ switch v := i.(type) {
+ case int:
+ fmt.Printf("Twice %v is %v\n", v, v*2)
+ case string:
+ fmt.Printf("%q is %v bytes long\n", v, len(v))
+ default:
+ fmt.Printf("I don't know about type %T!\n", v)
+ }
+}
+
+func main() {
+ do(21)
+ do("hello")
+ do(true)
+}
diff --git a/content/methods/web-servers.go b/content/methods/web-servers.go
deleted file mode 100644
index eff055a..0000000
--- a/content/methods/web-servers.go
+++ /dev/null
@@ -1,25 +0,0 @@
-// +build OMIT
-
-package main
-
-import (
- "fmt"
- "log"
- "net/http"
-)
-
-type Hello struct{}
-
-func (h Hello) ServeHTTP(
- w http.ResponseWriter,
- r *http.Request) {
- fmt.Fprint(w, "Hello!")
-}
-
-func main() {
- var h Hello
- err := http.ListenAndServe("localhost:4000", h)
- if err != nil {
- log.Fatal(err)
- }
-}