From: Rob Pike Date: Fri, 16 Oct 2009 18:13:40 +0000 (-0700) Subject: embedding part 1. X-Git-Tag: weekly.2009-11-06~277 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=8840726edc7297e27c2ddfb3aa4e459b81a06859;p=gostls13.git embedding part 1. R=rsc DELTA=128 (104 added, 0 deleted, 24 changed) OCL=35835 CL=35839 --- diff --git a/doc/effective_go.html b/doc/effective_go.html index 21aa4cf82a..fc65d155d2 100644 --- a/doc/effective_go.html +++ b/doc/effective_go.html @@ -1,6 +1,6 @@ - +

Introduction

@@ -34,7 +34,7 @@ should read first.

Examples

-The Go package sources +The Go package sources are intended to serve not only as the core library but also as examples of how to use the language. @@ -1551,7 +1551,7 @@ type Handler interface {

For brevity, let's ignore POSTs and assume HTTP requests are always GETs; that simplification does not affect the way the handlers are -made. Here's a trivial but complete implementation of a handler to +set up. Here's a trivial but complete implementation of a handler to count the number of times the page is visited.

@@ -1568,7 +1568,7 @@ func (ctr *Counter) ServeHTTP(c *http.Conn, req *http.Request) {

(Keeping with our theme, note how Fprintf can print to an HTTP connection.) -For reference, here's how to set up such a server. +For reference, here's how to attach such a server to a node on the URL tree.

 import "http"
 ...
@@ -1595,17 +1595,17 @@ has been visited?  Tie a channel to the web page.
 
 // A channel that sends a notification on each visit.
 // (Probably want the channel to be buffered.)
-type Chan chan int
+type Chan chan *http.Request
 
 func (ch Chan) ServeHTTP(c *http.Conn, req *http.Request) {
-	ch <- 1;
+	ch <- req;
 	fmt.Fprint(c, "notification sent");
 }
 

Finally, let's say we wanted to present on /args the arguments used when invoking the server binary. -It's easy to write a function to print the arguments: +It's easy to write a function to print the arguments.

 func ArgServer() {
@@ -1617,8 +1617,8 @@ func ArgServer() {
 

How do we turn that into an HTTP server? We could make ArgServer a method of some type whose value we ignore, but there's a cleaner way. -Since we can write a method for (almost) any type, we can write a method -for a function. +Since we can define a method for any type except pointers and interfaces, +we can write a method for a function. The http package contains this code:

@@ -1641,8 +1641,8 @@ calls f.  That may seem odd but it's no different from, say,
 the receiver being a channel and the method sending on the channel.
 

-To make ArgServer into an HTTP server, we first give it the right -signature. +To make ArgServer into an HTTP server, we first modify it +to have the right signature.

 // Argument server.
@@ -1653,30 +1653,134 @@ func ArgServer(c *http.Conn, req *http.Request) {
 }
 

-ArgServer has same signature as HandlerFunc, -so the function can be converted to that type to access its methods, -just as we converted Sequence to []int earlier. -The code to set it up is short: +ArgServer now has same signature as HandlerFunc, +so it can be converted to that type to access its methods, +just as we converted Sequence to IntArray +to access IntArray.Sort. +The code to set it up is concise:

 http.Handle("/args", http.HandlerFunc(ArgServer));
 

When someone visits the page /args, -the handler installed at that page has type -HandlerFunc and value ArgServer. +the handler installed at that page has value ArgServer +and type HandlerFunc. The HTTP server will invoke the method ServeHTTP -of that type, with that receiver, which will in turn call +of that type, with ArgServer as the receiver, which will in turn call ArgServer (via the invocation f(c, req) -inside HandlerFunc.ServeHTTP) and the arguments -will be displayed. +inside HandlerFunc.ServeHTTP). +The arguments will then be displayed.

-In summary, we have made an HTTP server from a struct, an integer, +In this section we have made an HTTP server from a struct, an integer, a channel, and a function, all because interfaces are just sets of methods, which can be defined for (almost) any type.

+

Embedding

+ +

+Go does not provide the typical, type-driven notion of subclassing, +but it does have the ability to “borrow” pieces of an +implementation by embedding types within a struct or +interface. +

+

+Interface embedding is very simple. +We've mentioned the io.Reader and io.Writer interfaces before; +here are their definitions. +

+
+type Reader interface {
+	Read(p []byte) (n int, err os.Error);
+}
+
+type Writer interface {
+	Write(p []byte) (n int, err os.Error);
+}
+
+

+The io package also exports several other interfaces +that specify objects that can implement several such methods. +For instance, there is io.ReadWriter, an interface +containing both Read and Write. +We could specify io.ReadWriter by listing the +two methods explicitly, but it's easier and more evocative +to embed the two interfaces to form the new one, like this: +

+
+// ReadWrite is the interface that groups the basic Read and Write methods.
+type ReadWriter interface {
+	Reader;
+	Writer;
+}
+
+

+This says just what it looks like: A ReadWriter can do +what a Reader does and what a Writer +does; it is a union of the embedded interfaces (which must be disjoint +sets of methods). +Only interfaces can be embedded within interfaces. +

+The same basic idea applies to structs, but with more far-reaching +implications. The bufio package has two struct types, +bufio.Reader and bufio.Writer, each of +which of course implements the analogous interfaces from package +io. +And bufio also implements a buffered reader/writer, +which it does by combining a reader and a writer into one struct +using embedding: it lists the types within the struct +but does not give them field names. +

+
+// ReadWriter stores pointers to a Reader and a Writer.
+// It implements io.ReadWriter.
+type ReadWriter struct {
+	*Reader;
+	*Writer;
+}
+
+

+This struct could be written as +

+
+type ReadWriter struct {
+	reader *Reader;
+	writer *Writer;
+}
+
+

+but then to promote the methods of the fields and to +satisfy the io interfaces, we would also need +to provide forwarding methods, like this: +

+
+func (rw *ReadWriter) Read(p []byte) (n int, err os.Error) {
+	return rw.reader.Read(p)
+}
+
+

+By embedding the structs directly, we avoid this bookkeeping. +The methods of embedded types come along for free, which means that bufio.ReadWriter +not only has the methods of bufio.Reader and bufio.Writer, +it also satisfies all three interfaces: +io.Reader, +io.Writer, and +io.ReadWriter. +

+

+There's one important way in which embedding differs from subclassing. When we embed a type, +the methods of that type become methods of the out type +Read method of a bufio.ReadWriter is +invoked, it has the exactly the same effect as the forwarding method written out above; +the receiver is the reader field of the ReadWriter, not the +ReadWriter itself. +

+ + +

Errors

@@ -1735,7 +1839,7 @@ field for recoverable failures.

 for try := 0; try < 2; try++ {
-	file, err := os.Open(filename, os.O_RDONLY, 0);
+	file, err = os.Open(filename, os.O_RDONLY, 0);
 	if err == nil {
 		return
 	}