diff --git a/doc/go_tutorial.html b/doc/go_tutorial.html new file mode 100644 index 0000000000..b801f8e369 --- /dev/null +++ b/doc/go_tutorial.html @@ -0,0 +1,1358 @@ +
+Rob Pike +
+
+
+This document is a tutorial introduction to the basics of the Go systems programming +language, intended for programmers familiar with C or C++. It is not a comprehensive +guide to the language; at the moment the document closest to that is the draft +specification: +
+
+ /doc/go_spec.html + ++To check out the compiler and tools and be ready to run Go programs, see +
+
+ /doc/go_setup.html + ++The presentation proceeds through a series of modest programs to illustrate +key features of the language. All the programs work (at time of writing) and are +checked in at +
+
+ /doc/progs + ++Program snippets are annotated with the line number in the original file; for +cleanliness, blank lines remain blank. +
+
+Let's start in the usual way: +
+
+01 package main +++03 import fmt "fmt" // Package implementing formatted I/O. +
+05 func main() { +06 fmt.Printf("Hello, world; or Καλημέρα κόσμε; or こんにちは 世界\n"); +07 } +
+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
+any initialization). It may also import other packages to use their facilities.
+This program imports the package fmt to gain access to
+our old, now capitalized and package-qualified friend, fmt.Printf.
+
+Function declarations are introduced with the func keyword.
+
+Notice that string constants can contain Unicode characters, encoded in UTF-8. +Go is defined to accept UTF-8 input. Strings are arrays of bytes, usually used +to store Unicode strings represented in UTF-8. +
+The comment convention is the same as in C++: +
+
+ /* ... */ + // ... + ++Later we'll have much more to say about printing. +
+
+Next up, here's a version of the Unix utility echo(1):
+
+
+01 package main +++03 import ( +04 "os"; +05 "flag"; +06 ) +
+08 var n_flag = flag.Bool("n", false, "don't print final newline") +
+10 const ( +11 kSpace = " "; +12 kNewline = "\n"; +13 ) +
+15 func main() { +16 flag.Parse(); // Scans the arg list and sets up flags +17 var s string = ""; +18 for i := 0; i < flag.NArg(); i++ { +19 if i > 0 { +20 s += kSpace +21 } +22 s += flag.Arg(i) +23 } +24 if !*n_flag { +25 s += kNewline +26 } +27 os.Stdout.WriteString(s); +28 } +
+This program is small but it's doing a number of new things. In the last example,
+we saw func introducing a function. The keywords var, const, and type
+(not used yet) also introduce declarations, as does import.
+Notice that we can group declarations of the same sort into
+parenthesized, semicolon-separated lists if we want, as on lines 3-6 and 10-13.
+But it's not necessary to do so; we could have said
+
+
+ const Space = " " + const Newline = "\n" + ++Semicolons aren't needed here; in fact, semicolons are unnecessary after any +top-level declaration, even though they are needed as separators within +a parenthesized list of declarations. +
+This program imports the "os" package to access its Stdout variable, of type
+*os.File. The import statement is actually a declaration: in its general form,
+as used in our ``hello world'' program,
+it names the identifier (fmt)
+that will be used to access members of the package imported from the file ("fmt"),
+found in the current directory or in a standard location.
+In this program, though, we've dropped the explicit name from the imports; by default,
+packages are imported using the name defined by the imported package,
+which by convention is of course the file name itself. Our ``hello world'' program
+could have said just import "fmt".
+
+You can specify your +own import names if you want but it's only necessary if you need to resolve +a naming conflict. +
+Given os.Stdout we can use its WriteString method to print the string.
+
+Having imported the flag package, line 8 creates a global variable to hold
+the value of echo's -n flag. The variable n_flag has type *bool, pointer
+to bool.
+
+In main.main, we parse the arguments (line 16) and then create a local
+string variable we will use to build the output.
+
+The declaration statement has the form +
+
+ var s string = ""; + ++This is the
var keyword, followed by the name of the variable, followed by
+its type, followed by an equals sign and an initial value for the variable.
++Go tries to be terse, and this declaration could be shortened. Since the +string constant is of type string, we don't have to tell the compiler that. +We could write +
+
+ var s = ""; + ++or we could go even shorter and write the idiom +
+
+ s := ""; + ++The
:= operator is used a lot in Go to represent an initializing declaration.
+(For those who know Limbo, its := construct is the same, but notice
+that Go has no colon after the name in a full var declaration.
+Also, for simplicity of parsing, := only works inside functions, not at
+the top level.)
+There's one in the for clause on the next line:
++
+18 for i := 0; i < flag.NArg(); i++ {
+
+
+The flag package has parsed the arguments and left the non-flag arguments
+in a list that can be iterated over in the obvious way.
+
+The Go for statement differs from that of C in a number of ways. First,
+it's the only looping construct; there is no while or do. Second,
+there are no parentheses on the clause, but the braces on the body
+are mandatory. The same applies to the if and switch statements.
+Later examples will show some other ways for can be written.
+
+The body of the loop builds up the string s by appending (using +=)
+the flags and separating spaces. After the loop, if the -n flag is not
+set, it appends a newline, and then writes the result.
+
+Notice that main.main is a niladic function with no return type.
+It's defined that way. Falling off the end of main.main means
+''success''; if you want to signal an erroneous return, call
+
+
+ os.Exit(1) + ++The
os package contains other essentials for getting
+started; for instance, os.Args is an array used by the
+flag package to access the command-line arguments.
++
+Go has some familiar types such as int and float, which represent
+values of the ''appropriate'' size for the machine. It also defines
+specifically-sized types such as int8, float64, and so on, plus
+unsigned integer types such as uint, uint32, etc. These are
+distinct types; even if int and int32 are both 32 bits in size,
+they are not the same type. There is also a byte synonym for
+uint8, which is the element type for strings.
+
+Speaking of string, that's a built-in type as well. Strings are
+immutable values -- they are not just arrays of byte values.
+Once you've built a string value, you can't change it, although
+of course you can change a string variable simply by
+reassigning it. This snippet from strings.go is legal code:
+
+
+07 s := "hello";
+08 if s[1] != 'e' { os.Exit(1) }
+09 s = "good bye";
+10 var p *string = &s;
+11 *p = "ciao";
+
+
+However the following statements are illegal because they would modify
+a string value:
+
+
+ s[0] = 'x'; + (*p)[1] = 'y'; + ++In C++ terms, Go strings are a bit like
const strings, while pointers
+to strings are analogous to const string references.
++Yes, there are pointers. However, Go simplifies their use a little; +read on. +
+Arrays are declared like this: +
+
+ var array_of_int [10]int; + ++Arrays, like strings, are values, but they are mutable. This differs +from C, in which
array_of_int would be usable as a pointer to int.
+In Go, since arrays are values, it's meaningful (and useful) to talk
+about pointers to arrays.
+
+The size of the array is part of its type; however, one can declare
+a slice variable, to which one can assign a pointer to
+any array
+with the same element type or - much more commonly - a slice
+expression of the form a[low : high], representing
+the subarray indexed by low through high-1.
+Slices look a lot like arrays but have
+no explicit size ([] vs. [10]) and they reference a segment of
+an underlying, often anonymous, regular array. Multiple slices
+can share data if they represent pieces of the same array;
+multiple arrays can never share data.
+
+Slices are actually much more common in Go programs than +regular arrays; they're more flexible, have reference semantics, +and are efficient. What they lack is the precise control of storage +layout of a regular array; if you want to have a hundred elements +of an array stored within your structure, you should use a regular +array. +
+When passing an array to a function, you almost always want +to declare the formal parameter to be a slice. When you call +the function, take the address of the array and Go will automatically +create (efficiently) a slice reference and pass that. +
+Using slices one can write this function (from sum.go):
+
+
+05 func sum(a []int) int { // returns an int
+06 s := 0;
+07 for i := 0; i < len(a); i++ {
+08 s += a[i]
+09 }
+10 return s
+11 }
+
++and invoke it like this: +
+
+15 s := sum(&[3]int{1,2,3}); // a slice of the array is passed to sum
+
+
+Note how the return type (int) is defined for sum() by stating it
+after the parameter list.
+The expression [3]int{1,2,3} -- a type followed by a brace-bounded expression
+-- is a constructor for a value, in this case an array of 3 ints. Putting an &
+in front gives us the address of a unique instance of the value. We pass the
+pointer to sum() by (automatically) promoting it to a slice.
+
+If you are creating a regular array but want the compiler to count the
+elements for you, use ... as the array size:
+
+
+ s := sum(&[...]int{1,2,3});
+
+
+In practice, though, unless you're meticulous about storage layout within a
+data structure, a slice itself - using empty brackets and no & - is all you need:
++
+ s := sum([]int{1,2,3});
+
+
+There are also maps, which you can initialize like this:
++
+ m := map[string] int {"one":1 , "two":2}
+
+
+The built-in function len(), which returns number of elements,
+makes its first appearance in sum. It works on strings, arrays,
+slices, and maps.
++
+
+Most types in Go are values. If you have an int or a struct
+or an array, assignment
+copies the contents of the object. To allocate something on the stack,
+just declare a variable. To allocate it on the heap, use new(), which
+returns a pointer to the allocated storage.
+
+
+ type T struct { a, b int }
+ var t *T = new(T);
+
+
+or the more idiomatic
++
+ t := new(T); + ++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 +referencing the same underlying data will see the modification. For these three +types you want to use the built-in function
make():
++
+ m := make(map[string] int); + ++This statement initializes a new map ready to store entries. +If you just declare the map, as in +
+
+ var m map[string] int; + ++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
+existing map.
+
+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.
+
+
+Although integers come in lots of sizes in Go, integer constants do not.
+There are no constants like 0ll or 0x0UL. Instead, integer
+constants are evaluated as ideal, large-precision values that
+can overflow only when they are assigned to an integer variable with
+too little precision to represent the value.
+
+
+ const hard_eight = (1 << 100) >> 97 // legal + ++There are nuances that deserve redirection to the legalese of the +language specification but here are some illustrative examples: +
+
+ var a uint64 = 0 // a has type uint64, value 0 + a := uint64(0) // equivalent; use a "conversion" + i := 0x1234 // i gets default type: int + var j int = 1e6 // legal - 1000000 is representable in an int + x := 1.5 // a float + i3div2 := 3/2 // integer division - result is 1 + f3div2 := 3./2. // floating point division - result is 1.5 + ++Conversions only work for simple cases such as converting
ints of one
+sign or size to another, and between ints and floats, plus a few other
+simple cases. There are no automatic numeric conversions of any kind in Go,
+other than that of making constants have concrete size and type when
+assigned to a variable.
++
+Next we'll look at a simple package for doing file I/O with the usual
+sort of open/close/read/write interface. Here's the start of file.go:
+
+
+01 package file +++03 import ( +04 "os"; +05 "syscall"; +06 ) +
+08 type File struct { +09 fd int; // file descriptor number +10 name string; // file name at Open time +11 } +
+The first line declares the name of the package -- file --
+and then we import two packages. The os package hides the differences
+between various operating systems to give a consistent view of files and
+so on; here we're only going to use its error handling utilities
+and reproduce the rudiments of its file I/O.
+
+The other item is the low-level, external syscall package, which provides
+a primitive interface to the underlying operating system's calls.
+
+Next is a type definition: the type keyword introduces a type declaration,
+in this case a data structure called File.
+To make things a little more interesting, our File includes the name of the file
+that the file descriptor refers to.
+
+Because File starts with a capital letter, the type is available outside the package,
+that is, by users of the package. In Go the rule about visibility of information is
+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
+name and hence the thing being named is visible only inside the package in which
+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 File, all its fields are lower case and so invisible to users, but we
+will soon give it some exported, upper-case methods.
+
+First, though, here is a factory to create them: +
+
+13 func newFile(fd int, name string) *File {
+14 if fd < 0 {
+15 return nil
+16 }
+17 return &File{fd, name}
+18 }
+
+
+This returns a pointer to a new File structure with the file descriptor and name
+filled in. This code uses Go's notion of a ''composite literal'', analogous to
+the ones used to build maps and arrays, to construct a new heap-allocated
+object. We could write
+
+
+ n := new(File); + n.fd = fd; + n.name = name; + return n + ++but for simple structures like
File it's easier to return the address of a nonce
+composite literal, as is done here on line 17.
+
+We can use the factory to construct some familiar, exported variables of type *File:
+
+
+20 var ( +21 Stdin = newFile(0, "/dev/stdin"); +22 Stdout = newFile(1, "/dev/stdout"); +23 Stderr = newFile(2, "/dev/stderr"); +24 ) ++
+The newFile function was not exported because it's internal. The proper,
+exported factory to use is Open:
+
+
+26 func Open(name string, mode int, perm int) (file *File, err os.Error) {
+27 r, e := syscall.Open(name, mode, perm);
+28 if e != 0 {
+29 err = os.Errno(e);
+30 }
+31 return newFile(r, name), err
+32 }
+
+
+There are a number of new things in these few lines. First, Open returns
+multiple values, an File and an error (more about errors in a moment).
+We declare the
+multi-value return as a parenthesized list of declarations; syntactically
+they look just like a second parameter list. The function
+syscall.Open
+also has a multi-value return, which we can grab with the multi-variable
+declaration on line 27; it declares r and e to hold the two values,
+both of type int64 (although you'd have to look at the syscall package
+to see that). Finally, line 28 returns two values: a pointer to the new File
+and the error. If syscall.Open fails, the file descriptor r will
+be negative and NewFile will return nil.
+
+About those errors: The os library includes a general notion of an error
+string, maintaining a unique set of errors throughout the program. It's a
+good idea to use its facility in your own interfaces, as we do here, for
+consistent error handling throughout Go code. In Open we use a
+conversion to os.Errno to translate Unix's integer errno value into
+an error value, which will be stored in a unique instance of type os.Error.
+
+Now that we can build Files, we can write methods for them. To declare
+a method of a type, we define a function to have an explicit receiver
+of that type, placed
+in parentheses before the function name. Here are some methods for *File,
+each of which declares a receiver variable file.
+
+
+34 func (file *File) Close() os.Error {
+35 if file == nil {
+36 return os.EINVAL
+37 }
+38 e := syscall.Close(file.fd);
+39 file.fd = -1; // so it can't be closed again
+40 if e != 0 {
+41 return os.Errno(e);
+42 }
+43 return nil
+44 }
+
+46 func (file *File) Read(b []byte) (ret int, err os.Error) {
+47 if file == nil {
+48 return -1, os.EINVAL
+49 }
+50 r, e := syscall.Read(file.fd, b);
+51 if e != 0 {
+52 err = os.Errno(e);
+53 }
+54 return int(r), err
+55 }
+
+57 func (file *File) Write(b []byte) (ret int, err os.Error) {
+58 if file == nil {
+59 return -1, os.EINVAL
+60 }
+61 r, e := syscall.Write(file.fd, b);
+62 if e != 0 {
+63 err = os.Errno(e);
+64 }
+65 return int(r), err
+66 }
+
+68 func (file *File) String() string {
+69 return file.name
+70 }
+
+
+There is no implicit this and the receiver variable must be used to access
+members of the structure. Methods are not declared within
+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
+array, not just for structs. We'll see an example with arrays later.
+
+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
+set of such error values.
+
+We can now use our new package: +
+
+01 package main +++03 import ( +04 "./file"; +05 "fmt"; +06 "os"; +07 ) +
+09 func main() { +10 hello := []byte{'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '\n'}; +11 file.Stdout.Write(hello); +12 file, err := file.Open("/does/not/exist", 0, 0); +13 if file == nil { +14 fmt.Printf("can't open file; err=%s\n", err.String()); +15 os.Exit(1); +16 } +17 } +
+The import of ''./file'' tells the compiler to use our own package rather than
+something from the directory of installed packages.
+
+Finally we can run the program: +
+
+ % helloworld3 + hello, world + can't open file; err=No such file or directory + % + ++
+Building on the file package, here's a simple version of the Unix utility cat(1),
+progs/cat.go:
+
+
+01 package main +++03 import ( +04 "./file"; +05 "flag"; +06 "fmt"; +07 "os"; +08 ) +
+10 func cat(f *file.File) { +11 const NBUF = 512; +12 var buf [NBUF]byte; +13 for { +14 switch nr, er := f.Read(&buf); true { +15 case nr < 0: +16 fmt.Fprintf(os.Stderr, "error reading from %s: %s\n", f.String(), er.String()); +17 os.Exit(1); +18 case nr == 0: // EOF +19 return; +20 case nr > 0: +21 if nw, ew := file.Stdout.Write(buf[0:nr]); nw != nr { +22 fmt.Fprintf(os.Stderr, "error writing from %s: %s\n", f.String(), ew.String()); +23 } +24 } +25 } +26 } +
+28 func main() { +29 flag.Parse(); // Scans the arg list and sets up flags +30 if flag.NArg() == 0 { +31 cat(file.Stdin); +32 } +33 for i := 0; i < flag.NArg(); i++ { +34 f, err := file.Open(flag.Arg(i), 0, 0); +35 if f == nil { +36 fmt.Fprintf(os.Stderr, "can't open %s: error %s\n", flag.Arg(i), err); +37 os.Exit(1); +38 } +39 cat(f); +40 f.Close(); +41 } +42 } +
+By now this should be easy to follow, but the switch statement introduces some
+new features. Like a for loop, an if or switch can include an
+initialization statement. The switch on line 14 uses one to create variables
+nr and er to hold the return values from f.Read(). (The if on line 21
+has the same idea.) The switch statement is general: it evaluates the cases
+from top to bottom looking for the first case that matches the value; the
+case expressions don't need to be constants or even integers, as long as
+they all have the same type.
+
+Since the switch value is just true, we could leave it off -- as is also
+the situation
+in a for statement, a missing value means true. In fact, such a switch
+is a form of if-else chain. While we're here, it should be mentioned that in
+switch statements each case has an implicit break.
+
+Line 21 calls Write() by slicing the incoming buffer, which is itself a slice.
+Slices provide the standard Go way to handle I/O buffers.
+
+Now let's make a variant of cat that optionally does rot13 on its input.
+It's easy to do by just processing the bytes, but instead we will exploit
+Go's notion of an interface.
+
+The cat() subroutine uses only two methods of f: Read() and String(),
+so let's start by defining an interface that has exactly those two methods.
+Here is code from progs/cat_rot13.go:
+
+
+22 type reader interface {
+23 Read(b []byte) (ret int, err os.Error);
+24 String() string;
+25 }
+
+
+Any type that implements the two methods of reader -- regardless of whatever
+other methods the type may also contain -- is said to implement the
+interface. Since file.File implements these methods, it implements the
+reader interface. We could tweak the cat subroutine to accept a reader
+instead of a *file.File and it would work just fine, but let's embellish a little
+first by writing a second type that implements reader, one that wraps an
+existing reader and does rot13 on the data. To do this, we just define
+the type and implement the methods and with no other bookkeeping,
+we have a second implementation of the reader interface.
+
+
+27 type rotate13 struct {
+28 source reader;
+29 }
+
+31 func newRotate13(source reader) *rotate13 {
+32 return &rotate13{source}
+33 }
+
+35 func (r13 *rotate13) Read(b []byte) (ret int, err os.Error) {
+36 r, e := r13.source.Read(b);
+37 for i := 0; i < r; i++ {
+38 b[i] = rot13(b[i])
+39 }
+40 return r, e
+41 }
+
+43 func (r13 *rotate13) String() string {
+44 return r13.source.String()
+45 }
+46 // end of rotate13 implementation
+
+
+(The rot13 function called on line 38 is trivial and not worth reproducing.)
+
+To use the new feature, we define a flag: +
+
+10 var rot13_flag = flag.Bool("rot13", false, "rot13 the input")
+
+
+and use it from within a mostly unchanged cat() function:
+
+
+48 func cat(r reader) {
+49 const NBUF = 512;
+50 var buf [NBUF]byte;
+
+52 if *rot13_flag {
+53 r = newRotate13(r)
+54 }
+55 for {
+56 switch nr, er := r.Read(&buf); {
+57 case nr < 0:
+58 fmt.Fprintf(os.Stderr, "error reading from %s: %s\n", r.String(), er.String());
+59 os.Exit(1);
+60 case nr == 0: // EOF
+61 return;
+62 case nr > 0:
+63 nw, ew := file.Stdout.Write(buf[0:nr]);
+64 if nw != nr {
+65 fmt.Fprintf(os.Stderr, "error writing from %s: %s\n", r.String(), ew.String());
+66 }
+67 }
+68 }
+69 }
+
+
+(We could also do the wrapping in main and leave cat() mostly alone, except
+for changing the type of the argument; consider that an exercise.)
+Lines 52 through 55 set it all up: If the rot13 flag is true, wrap the reader
+we received into a rotate13 and proceed. Note that the interface variables
+are values, not pointers: the argument is of type reader, not *reader,
+even though under the covers it holds a pointer to a struct.
+
+Here it is in action: +
+
+ % echo abcdefghijklmnopqrstuvwxyz | ./cat + abcdefghijklmnopqrstuvwxyz + % echo abcdefghijklmnopqrstuvwxyz | ./cat --rot13 + nopqrstuvwxyzabcdefghijklm + % ++
+Fans of dependency injection may take cheer from how easily interfaces +allow us to substitute the implementation of a file descriptor. +
+Interfaces are a distinct feature of Go. An interface is implemented by a
+type if the type implements all the methods declared in the interface.
+This means
+that a type may implement an arbitrary number of different interfaces.
+There is no type hierarchy; things can be much more ad hoc,
+as we saw with rot13. The type file.File implements reader; it could also
+implement a writer, or any other interface built from its methods that
+fits the current situation. Consider the empty interface
+
+
+ type Empty interface {}
+
++Every type implements the empty interface, which makes it +useful for things like containers. +
+
+Interfaces provide a simple form of polymorphism since they completely +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:
+
+
+09 func Sort(data SortInterface) {
+10 for i := 1; i < data.Len(); i++ {
+11 for j := i; j > 0 && data.Less(j, j-1); j-- {
+12 data.Swap(j, j-1);
+13 }
+14 }
+15 }
+
+
+The code needs only three methods, which we wrap into SortInterface:
+
+
+03 type SortInterface interface {
+04 Len() int;
+05 Less(i, j int) bool;
+06 Swap(i, j int);
+07 }
+
+
+We can apply Sort to any type that implements Len, Less, and Swap.
+The sort package includes the necessary methods to allow sorting of
+arrays of integers, strings, etc.; here's the code for arrays of int
+
+
+29 type IntArray []int +++31 func (p IntArray) Len() int { return len(p); } +32 func (p IntArray) Less(i, j int) bool { return p[i] < p[j]; } +33 func (p IntArray) Swap(i, j int) { p[i], p[j] = p[j], p[i]; } +
+
+36 type FloatArray []float +
+38 func (p FloatArray) Len() int { return len(p); } +39 func (p FloatArray) Less(i, j int) bool { return p[i] < p[j]; } +40 func (p FloatArray) Swap(i, j int) { p[i], p[j] = p[j], p[i]; } +
+
+43 type StringArray []string +
+45 func (p StringArray) Len() int { return len(p); } +46 func (p StringArray) Less(i, j int) bool { return p[i] < p[j]; } +47 func (p StringArray) Swap(i, j int) { p[i], p[j] = p[j], p[i]; } +
+
+50 // Convenience wrappers for common cases +
+52 func SortInts(a []int) { Sort(IntArray(a)); } +53 func SortFloats(a []float) { Sort(FloatArray(a)); } +54 func SortStrings(a []string) { Sort(StringArray(a)); } +
+
+57 func IntsAreSorted(a []int) bool { return IsSorted(IntArray(a)); } +58 func FloatsAreSorted(a []float) bool { return IsSorted(FloatArray(a)); } +59 func StringsAreSorted(a []string) bool { return IsSorted(StringArray(a)); } +
+Here we see methods defined for non-struct types. You can define methods
+for any type you define and name in your package.
+
+And now a routine to test it out, from progs/sortmain.go. This
+uses a function in the sort package, omitted here for brevity,
+to test that the result is sorted.
+
+
+08 func ints() {
+09 data := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586};
+10 a := sort.IntArray(data);
+11 sort.Sort(a);
+12 if !sort.IsSorted(a) {
+13 panic()
+14 }
+15 }
+
++If we have a new type we want to be able to sort, all we need to do is +to implement the three methods for that type, like this: +
+
+26 type day struct {
+27 num int;
+28 short_name string;
+29 long_name string;
+30 }
+
+32 type dayArray struct {
+33 data []*day;
+34 }
+
+36 func (p *dayArray) Len() int { return len(p.data); }
+37 func (p *dayArray) Less(i, j int) bool { return p.data[i].num < p.data[j].num; }
+38 func (p *dayArray) Swap(i, j int) { p.data[i], p.data[j] = p.data[j], p.data[i]; }
+
++
+
+The examples of formatted printing so far have been modest. In this section +we'll talk about how formatted I/O can be done well in Go. +
+We've seen simple uses of the package fmt, which
+implements Printf, Fprintf, and so on.
+Within the fmt package, Printf is declared with this signature:
+
+
+ Printf(format string, v ...) (n int, errno os.Error) + ++That
... represents the variadic argument list that in C would
+be handled using the stdarg.h macros, but in Go is passed using
+an empty interface variable (interface {}) that is then unpacked
+using the reflection library. It's off topic here but the use of
+reflection helps explain some of the nice properties of Go's Printf,
+due to the ability of Printf to discover the type of its arguments
+dynamically.
+
+For example, in C each format must correspond to the type of its
+argument. It's easier in many cases in Go. Instead of %llud you
+can just say %d; Printf knows the size and signedness of the
+integer and can do the right thing for you. The snippet
+
+
+06 var u64 uint64 = 1<<64-1;
+07 fmt.Printf("%d %d\n", u64, int64(u64));
+
++prints +
+
+ 18446744073709551615 -1 + ++In fact, if you're lazy the format
%v will print, in a simple
+appropriate style, any value, even an array or structure. The output of
++
+10 type T struct { a int; b string };
+11 t := T{77, "Sunset Strip"};
+12 a := []int{1, 2, 3, 4};
+13 fmt.Printf("%v %v %v\n", u64, t, a);
+
++is +
+
+ 18446744073709551615 {77 Sunset Strip} [1 2 3 4]
+
+
+You can drop the formatting altogether if you use Print or Println
+instead of Printf. Those routines do fully automatic formatting.
+The Print function just prints its elements out using the equivalent
+of %v while Println automatically inserts spaces between arguments
+and adds a newline. The output of each of these two lines is identical
+to that of the Printf call above.
++
+14 fmt.Print(u64, " ", t, " ", a, "\n"); +15 fmt.Println(u64, t, a); ++
+If you have your own type you'd like Printf or Print to format,
+just give it a String() method that returns a string. The print
+routines will examine the value to inquire whether it implements
+the method and if so, use it rather than some other formatting.
+Here's a simple example.
+
+
+05 type testType struct { a int; b string }
+
+07 func (t *testType) String() string {
+08 return fmt.Sprint(t.a) + " " + t.b
+09 }
+
+11 func main() {
+12 t := &testType{77, "Sunset Strip"};
+13 fmt.Println(t)
+14 }
+
+
+Since *T has a String() method, the
+default formatter for that type will use it and produce the output
+
+
+ 77 Sunset Strip + ++Observe that the
String() method calls Sprint (the obvious Go
+variant that returns a string) to do its formatting; special formatters
+can use the fmt library recursively.
+
+Another feature of Printf is that the format %T will print a string
+representation of the type of a value, which can be handy when debugging
+polymorphic code.
+
+It's possible to write full custom print formats with flags and precisions +and such, but that's getting a little off the main thread so we'll leave it +as an exploration exercise. +
+You might ask, though, how Printf can tell whether a type implements
+the String() method. Actually what it does is ask if the value can
+be converted to an interface variable that implements the method.
+Schematically, given a value v, it does this:
+
+
+
+ type Stringer interface {
+ String() string
+ }
+
+ s, ok := v.(Stringer); // Test whether v implements "String()"
+ if ok {
+ result = s.String()
+ } else {
+ result = default_output(v)
+ }
+
+
+The code uses a ``type assertion'' (v.(Stringer)) to test if the value stored in
+v satisfies the Stringer interface; if it does, s
+will become an interface variable implementing the method and ok will
+be true. We then use the interface variable to call the method.
+(The ''comma, ok'' pattern is a Go idiom used to test the success of
+operations such as type conversion, map update, communications, and so on,
+although this is the only appearance in this tutorial.)
+If the value does not satisfy the interface, ok will be false.
+
+In this snippet the name Stringer follows the convention that we add [e]r
+to interfaces describing simple method sets like this.
+
+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
+not a file. Instead, it is a variable of type io.Writer, which is an
+interface type defined in the io library:
+
+
+ type Writer interface {
+ Write(p []byte) (n int, err os.Error);
+ }
+
+
+(This interface is another conventional name, this time for Write; there are also
+io.Reader, io.ReadWriter, and so on.)
+Thus you can call Fprintf on any type that implements a standard Write()
+method, not just files but also network channels, buffers, rot13ers, whatever
+you want.
++
+Now we come to processes and communication -- concurrent programming. +It's a big subject so to be brief we assume some familiarity with the topic. +
+A classic program in the style is the prime sieve of Eratosthenes. +It works by taking a stream of all the natural numbers and introducing +a sequence of filters, one for each prime, to winnow the multiples of +that prime. At each step we have a sequence of filters of the primes +so far, and the next number to pop out is the next prime, which triggers +the creation of the next filter in the chain. +
+Here's a flow diagram; each box represents a filter element whose +creation is triggered by the first number that flowed from the +elements before it. +
+
+
+
+
+
+
+To create a stream of integers, we use a Go channel, which,
+borrowing from CSP's descendants, represents a communications
+channel that can connect two concurrent computations.
+In Go, channel variables are references to a run-time object that
+coordinates the communication; as with maps and slices, use
+make to create a new channel.
+
+Here is the first function in progs/sieve.go:
+
+
+05 // Send the sequence 2, 3, 4, ... to channel 'ch'.
+06 func generate(ch chan int) {
+07 for i := 2; ; i++ {
+08 ch <- i // Send 'i' to channel 'ch'.
+09 }
+10 }
+
+
+The generate function sends the sequence 2, 3, 4, 5, ... to its
+argument channel, ch, using the binary communications operator <-.
+Channel operations block, so if there's no recipient for the value on ch,
+the send operation will wait until one becomes available.
+
+The filter function has three arguments: an input channel, an output
+channel, and a prime number. It copies values from the input to the
+output, discarding anything divisible by the prime. The unary communications
+operator <- (receive) retrieves the next value on the channel.
+
+
+12 // Copy the values from channel 'in' to channel 'out',
+13 // removing those divisible by 'prime'.
+14 func filter(in, out chan int, prime int) {
+15 for {
+16 i := <-in; // Receive value of new variable 'i' from 'in'.
+17 if i % prime != 0 {
+18 out <- i // Send 'i' to channel 'out'.
+19 }
+20 }
+21 }
+
+
+The generator and filters execute concurrently. Go has
+its own model of process/threads/light-weight processes/coroutines,
+so to avoid notational confusion we'll call concurrently executing
+computations in Go goroutines. To start a goroutine,
+invoke the function, prefixing the call with the keyword go;
+this starts the function running in parallel with the current
+computation but in the same address space:
+
+
+ go sum(huge_array); // calculate sum in the background + ++If you want to know when the calculation is done, pass a channel +on which it can report back: +
+
+ ch := make(chan int); + go sum(huge_array, ch); + // ... do something else for a while + result := <-ch; // wait for, and retrieve, result + ++Back to our prime sieve. Here's how the sieve pipeline is stitched +together: +
+
+24 func main() {
+25 ch := make(chan int); // Create a new channel.
+26 go generate(ch); // Start generate() as a goroutine.
+27 for {
+28 prime := <-ch;
+29 fmt.Println(prime);
+30 ch1 := make(chan int);
+31 go filter(ch, ch1, prime);
+32 ch = ch1
+33 }
+34 }
+
+
+Line 25 creates the initial channel to pass to generate, which it
+then starts up. As each prime pops out of the channel, a new filter
+is added to the pipeline and its output becomes the new value
+of ch.
+
+The sieve program can be tweaked to use a pattern common
+in this style of programming. Here is a variant version
+of generate, from progs/sieve1.go:
+
+
+06 func generate() chan int {
+07 ch := make(chan int);
+08 go func(){
+09 for i := 2; ; i++ {
+10 ch <- i
+11 }
+12 }();
+13 return ch;
+14 }
+
++This version does all the setup internally. It creates the output +channel, launches a goroutine internally using a function literal, and +returns the channel to the caller. It is a factory for concurrent +execution, starting the goroutine and returning its connection. +
+The function literal notation (lines 8-12) allows us to construct an
+anonymous function and invoke it on the spot. Notice that the local
+variable ch is available to the function literal and lives on even
+after generate returns.
+
+The same change can be made to filter:
+
+
+17 func filter(in chan int, prime int) chan int {
+18 out := make(chan int);
+19 go func() {
+20 for {
+21 if i := <-in; i % prime != 0 {
+22 out <- i
+23 }
+24 }
+25 }();
+26 return out;
+27 }
+
+
+The sieve function's main loop becomes simpler and clearer as a
+result, and while we're at it let's turn it into a factory too:
+
+
+29 func sieve() chan int {
+30 out := make(chan int);
+31 go func() {
+32 ch := generate();
+33 for {
+34 prime := <-ch;
+35 out <- prime;
+36 ch = filter(ch, prime);
+37 }
+38 }();
+39 return out;
+40 }
+
+
+Now main's interface to the prime sieve is a channel of primes:
+
+
+42 func main() {
+43 primes := sieve();
+44 for {
+45 fmt.Println(<-primes);
+46 }
+47 }
+
++
+With channels, it's possible to serve multiple independent client goroutines without
+writing an actual multiplexer. The trick is to send the server a channel in the message,
+which it will then use to reply to the original sender.
+A realistic client-server program is a lot of code, so here is a very simple substitute
+to illustrate the idea. It starts by defining a request type, which embeds a channel
+that will be used for the reply.
+
+
+05 type request struct {
+06 a, b int;
+07 replyc chan int;
+08 }
+
++The server will be trivial: it will do simple binary operations on integers. Here's the +code that invokes the operation and responds to the request: +
+
+10 type binOp func(a, b int) int +++12 func run(op binOp, req *request) { +13 reply := op(req.a, req.b); +14 req.replyc <- reply; +15 } +
+Line 10 defines the name binOp to be a function taking two integers and
+returning a third.
+
+The server routine loops forever, receiving requests and, to avoid blocking due to
+a long-running operation, starting a goroutine to do the actual work.
+
+
+17 func server(op binOp, service chan *request) {
+18 for {
+19 req := <-service;
+20 go run(op, req); // don't wait for it
+21 }
+22 }
+
++We construct a server in a familiar way, starting it up and returning a channel to +connect to it: +
+
+24 func startServer(op binOp) chan *request {
+25 req := make(chan *request);
+26 go server(op, req);
+27 return req;
+28 }
+
++Here's a simple test. It starts a server with an addition operator, and sends out +lots of requests but doesn't wait for the reply. Only after all the requests are sent +does it check the results. +
+
+30 func main() {
+31 adder := startServer(func(a, b int) int { return a + b });
+32 const N = 100;
+33 var reqs [N]request;
+34 for i := 0; i < N; i++ {
+35 req := &reqs[i];
+36 req.a = i;
+37 req.b = i + N;
+38 req.replyc = make(chan int);
+39 adder <- req;
+40 }
+41 for i := N-1; i >= 0; i-- { // doesn't matter what order
+42 if <-reqs[i].replyc != N + 2*i {
+43 fmt.Println("fail at", i);
+44 }
+45 }
+46 fmt.Println("done");
+47 }
+
+
+One annoyance with this program is that it doesn't exit cleanly; when main returns
+there are a number of lingering goroutines blocked on communication. To solve this,
+we can provide a second, quit channel to the server:
+
+
+28 func startServer(op binOp) (service chan *request, quit chan bool) {
+29 service = make(chan *request);
+30 quit = make(chan bool);
+31 go server(op, service, quit);
+32 return service, quit;
+33 }
+
+
+It passes the quit channel to the server function, which uses it like this:
+
+
+17 func server(op binOp, service chan *request, quit chan bool) {
+18 for {
+19 select {
+20 case req := <-service:
+21 go run(op, req); // don't wait for it
+22 case <-quit:
+23 return;
+24 }
+25 }
+26 }
+
+
+Inside server, a select statement chooses which of the multiple communications
+listed by its cases can proceed. If all are blocked, it waits until one can proceed; if
+multiple can proceed, it chooses one at random. In this instance, the select allows
+the server to honor requests until it receives a quit message, at which point it
+returns, terminating its execution.
+
+
+All that's left is to strobe the quit channel
+at the end of main:
+
+
+36 adder, quit := startServer(func(a, b int) int { return a + b });
+
+...
++51 quit <- true; ++
+There's a lot more to Go programming and concurrent programming in general but this +quick tour should give you some of the basics. + +