diff --git a/doc/go_tutorial.html b/doc/go_tutorial.html new file mode 100644 index 00000000000..b801f8e3690 --- /dev/null +++ b/doc/go_tutorial.html @@ -0,0 +1,1358 @@ +

Let's Go

+

+Rob Pike +

+


+(March 18, 2009) +

+

+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. +

+

Hello, World

+

+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. +

+

Echo

+

+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. +

+

An Interlude about Types

+

+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. +

+

+

An Interlude about Allocation

+

+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. +

+

An Interlude about Constants

+

+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. +

+

An I/O Package

+

+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
+    % 
+
+
+

Rotting cats

+

+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. +

+

Sorting

+

+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]; } +

+

+

+

Printing

+

+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. +

+

Prime numbers

+

+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    }
+
+

+

Multiplexing

+

+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. + + +