Commit 6aabf31a authored by Rob Pike's avatar Rob Pike

a few tweaks triggered by tgs's comments

DELTA=46  (25 added, 1 deleted, 20 changed)
OCL=24342
CL=24354
parent 689c808c
...@@ -4,7 +4,7 @@ Let's Go ...@@ -4,7 +4,7 @@ Let's Go
Rob Pike Rob Pike
---- ----
(January 20, 2009) (February 4, 2009)
This document is a tutorial introduction to the basics of the Go systems programming This document is a tutorial introduction to the basics of the Go systems programming
...@@ -34,7 +34,7 @@ Let's start in the usual way: ...@@ -34,7 +34,7 @@ Let's start in the usual way:
--PROG progs/helloworld.go --PROG progs/helloworld.go
Every Go source file declares which package it's part of using a "package" statement. Every Go source file declares, using a "package" statement, which package it's part of.
The "main" package's "main" function is where the program starts running (after The "main" package's "main" function is where the program starts running (after
any initialization). any initialization).
...@@ -256,22 +256,24 @@ or the more idiomatic ...@@ -256,22 +256,24 @@ or the more idiomatic
Some types - maps, slices, and channels (see below) have reference semantics. Some types - maps, slices, and channels (see below) have reference semantics.
If you're holding a slice or a map and you modify its contents, other variables If you're holding a slice or a map and you modify its contents, other variables
referencing the same underlying data will see the modification. If you allocate referencing the same underlying data will see the modification. For these three
a reference object with "new()" you receive a pointer to an uninitialized ("nil") types you want to use the built-in function "make()":
reference. Instead, for these three types you want to use "make()":
m := make(map[string] int); m := make(map[string] int);
This statement initializes a new map ready to store entries. If you just declare This statement initializes a new map ready to store entries.
the map, as in If you just declare the map, as in
var m map[string] int; var m map[string] int;
it is a "nil" reference that cannot hold anything. To use the map, it creates a "nil" reference that cannot hold anything. To use the map,
you must first initialize the reference using "make()" or by assignment to an you must first initialize the reference using "make()" or by assignment to an
existing map. existing map.
Note that "new(T)" returns type "*T" while "make(T)" returns type "T". Note that "new(T)" returns type "*T" while "make(T)" returns type
"T". If you (mistakenly) allocate a reference object with "new()",
you receive a pointer to an uninitialized reference, equivalent to
declaring an uninitialized variable and taking its address.
An Interlude about Constants An Interlude about Constants
---- ----
...@@ -328,7 +330,8 @@ that is, by users of the package. In Go the rule about visibility of informati ...@@ -328,7 +330,8 @@ that is, by users of the package. In Go the rule about visibility of informati
simple: if a name (of a top-level type, function, method, constant, variable, or of simple: if a name (of a top-level type, function, method, constant, variable, or of
a structure field) is capitalized, users of the package may see it. Otherwise, the a structure field) is capitalized, users of the package may see it. Otherwise, the
name and hence the thing being named is visible only inside the package in which name and hence the thing being named is visible only inside the package in which
it is declared. In Go, the term for publicly visible names is ''exported''. it is declared. This is more than a convention; the rule is enforced by the compiler.
In Go, the term for publicly visible names is ''exported''.
In the case of "FD", all its fields are lower case and so invisible to users, but we In the case of "FD", all its fields are lower case and so invisible to users, but we
will soon give it some exported, upper-case methods. will soon give it some exported, upper-case methods.
...@@ -339,7 +342,8 @@ First, though, here is a factory to create them: ...@@ -339,7 +342,8 @@ First, though, here is a factory to create them:
This returns a pointer to a new "FD" structure with the file descriptor and name This returns a pointer to a new "FD" structure with the file descriptor and name
filled in. This code uses Go's notion of a ''composite literal'', analogous to filled in. This code uses Go's notion of a ''composite literal'', analogous to
the ones used to build maps and arrays, to construct the object. We could write the ones used to build maps and arrays, to construct a new heap-allocated
object. We could write
n := new(FD); n := new(FD);
n.fildes = fd; n.fildes = fd;
...@@ -390,9 +394,12 @@ There is no implicit "this" and the receiver variable must be used to access ...@@ -390,9 +394,12 @@ There is no implicit "this" and the receiver variable must be used to access
members of the structure. Methods are not declared within members of the structure. Methods are not declared within
the "struct" declaration itself. The "struct" declaration defines only data members. the "struct" declaration itself. The "struct" declaration defines only data members.
In fact, methods can be created for any type you name, such as an integer or In fact, methods can be created for any type you name, such as an integer or
array, not just for "structs". We'll see an an example with arrays later. array, not just for "structs". We'll see an example with arrays later.
These methods use the public variable "os.EINVAL" to return the ("*os.Error" The "String" method is so called because of printing convention we'll
describe later.
The methods use the public variable "os.EINVAL" to return the ("*os.Error"
version of the) Unix error code EINVAL. The "os" library defines a standard version of the) Unix error code EINVAL. The "os" library defines a standard
set of such error values. set of such error values.
...@@ -404,7 +411,7 @@ and run the program: ...@@ -404,7 +411,7 @@ and run the program:
% helloworld3 % helloworld3
hello, world hello, world
can't open file; errno=2 can't open file; err=No such file or directory
% %
Rotting cats Rotting cats
...@@ -504,8 +511,12 @@ useful for things like containers. ...@@ -504,8 +511,12 @@ useful for things like containers.
Sorting Sorting
---- ----
As another example of interfaces, consider this simple sort algorithm, Interfaces provide a simple form of polymorphism since they completely
taken from "progs/sort.go": separate the definition of what an object does from how it does it, allowing
distinct implementations to be represented at different times by the
same interface variable.
As an example, consider this simple sort algorithm taken from "progs/sort.go":
--PROG progs/sort.go /func.Sort/ /^}/ --PROG progs/sort.go /func.Sort/ /^}/
...@@ -628,7 +639,7 @@ Schematically, given a value "v", it does this: ...@@ -628,7 +639,7 @@ Schematically, given a value "v", it does this:
result = default_output(v) result = default_output(v)
} }
The code tests if the value stored in The code uses a ``type assertion'' ("v.(String)") to test if the value stored in
"v" satisfies the "String" interface; if it does, "s" "v" satisfies the "String" interface; if it does, "s"
will become an interface variable implementing the method and "ok" will will become an interface variable implementing the method and "ok" will
be "true". We then use the interface variable to call the method. be "true". We then use the interface variable to call the method.
...@@ -637,6 +648,14 @@ operations such as type conversion, map update, communications, and so on, ...@@ -637,6 +648,14 @@ operations such as type conversion, map update, communications, and so on,
although this is the only appearance in this tutorial.) although this is the only appearance in this tutorial.)
If the value does not satisfy the interface, "ok" will be false. If the value does not satisfy the interface, "ok" will be false.
In this snippet "String" is used as both a type name and a method name. This does
not create any ambiguity because methods only appear in association
with a variable ("s.String()"); a method name can never appear in a context
where a type name is legal and vice versa. Another way to say this is that the
method "String" is only available within the scope bound to a variable of type
"String". We double-use the name because it makes the interface type
self-describing ("String" (the interface) implements "String" (the method)).
One last wrinkle. To complete the suite, besides "Printf" etc. and "Sprintf" One last wrinkle. To complete the suite, besides "Printf" etc. and "Sprintf"
etc., there are also "Fprintf" etc. Unlike in C, "Fprintf"'s first argument is etc., there are also "Fprintf" etc. Unlike in C, "Fprintf"'s first argument is
not a file. Instead, it is a variable of type "io.Write", which is an not a file. Instead, it is a variable of type "io.Write", which is an
...@@ -646,6 +665,8 @@ interface type defined in the "io" library: ...@@ -646,6 +665,8 @@ interface type defined in the "io" library:
Write(p []byte) (n int, err *os.Error); Write(p []byte) (n int, err *os.Error);
} }
(This interface is another doubled name, this time for "Write"; there are also
"io.Read", "io.ReadWrite", and so on.)
Thus you can call "Fprintf" on any type that implements a standard "Write()" Thus you can call "Fprintf" on any type that implements a standard "Write()"
method, not just files but also network channels, buffers, rot13ers, whatever method, not just files but also network channels, buffers, rot13ers, whatever
you want. you want.
...@@ -686,7 +707,7 @@ Here is the first function in "progs/sieve.go": ...@@ -686,7 +707,7 @@ Here is the first function in "progs/sieve.go":
The "generate" function sends the sequence 2, 3, 4, 5, ... to its The "generate" function sends the sequence 2, 3, 4, 5, ... to its
argument channel, "ch", using the binary communications operator "<-". argument channel, "ch", using the binary communications operator "<-".
Channels block, so if there's no recipient for the the value on "ch", Channel operations block, so if there's no recipient for the value on "ch",
the send operation will wait until one becomes available. the send operation will wait until one becomes available.
The "filter" function has three arguments: an input channel, an output The "filter" function has three arguments: an input channel, an output
...@@ -734,8 +755,11 @@ This version does all the setup internally. It creates the output ...@@ -734,8 +755,11 @@ This version does all the setup internally. It creates the output
channel, launches a goroutine internally using a function literal, and channel, launches a goroutine internally using a function literal, and
returns the channel to the caller. It is a factory for concurrent returns the channel to the caller. It is a factory for concurrent
execution, starting the goroutine and returning its connection. execution, starting the goroutine and returning its connection.
The same
change can be made to "filter": The function literal notation (lines 6-10) allows us to construct an
anonymous function and invoke it on the spot.
The same change can be made to "filter":
--PROG progs/sieve1.go /func.filter/ /^}/ --PROG progs/sieve1.go /func.filter/ /^}/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment