From 80f18fc933cf3f3e829c5455a1023d69f7b86e52 Mon Sep 17 00:00:00 2001 From: Ondřej Surý Date: Tue, 13 Sep 2011 13:11:55 +0200 Subject: Imported Upstream version 60 --- doc/go_tutorial.html | 1455 -------------------------------------------------- 1 file changed, 1455 deletions(-) delete mode 100644 doc/go_tutorial.html (limited to 'doc/go_tutorial.html') diff --git a/doc/go_tutorial.html b/doc/go_tutorial.html deleted file mode 100644 index 822f9626e..000000000 --- a/doc/go_tutorial.html +++ /dev/null @@ -1,1455 +0,0 @@ - -

Introduction

-

-This document is a tutorial introduction to the basics of the Go 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 -language specification. -After you've read this tutorial, you should look at -Effective Go, -which digs deeper into how the language is used and -talks about the style and idioms of programming in Go. -Also, slides from a 3-day course about Go are available. -They provide some background and a lot of examples: -Day 1, -Day 2, -Day 3. -

-The presentation here 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 into the repository in the directory /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: -

-

package main
-
-import fmt "fmt"  // Package implementing formatted I/O.
-
-func main() {
-    fmt.Printf("Hello, world; or Καλημέρα κόσμε; or こんにちは 世界\n")
-}
-
-

-Every Go source file declares, using a package statement, which package it's part of. -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. -

-Functions are introduced with the func keyword. -The main package's main function is where the program starts running (after -any initialization). -

-String constants can contain Unicode characters, encoded in UTF-8. -(In fact, Go source files are defined to be encoded in UTF-8.) -

-The comment convention is the same as in C++: -

-

-/* ... */
-// ...
-
-

-Later we'll have much more to say about printing. -

-

Semicolons

-

-You might have noticed that our program has no semicolons. In Go -code, the only place you typically see semicolons is separating the -clauses of for loops and the like; they are not necessary after -every statement. -

-In fact, what happens is that the formal language uses semicolons, -much as in C or Java, but they are inserted automatically -at the end of every line that looks like the end of a statement. You -don't need to type them yourself. -

-For details about how this is done you can see the language -specification, but in practice all you need to know is that you -never need to put a semicolon at the end of a line. (You can put -them in if you want to write multiple statements per line.) As an -extra help, you can also leave out a semicolon immediately before -a closing brace. -

-This approach makes for clean-looking, semicolon-free code. The -one surprise is that it's important to put the opening -brace of a construct such as an if statement on the same line as -the if; if you don't, there are situations that may not compile -or may give the wrong result. The language forces the brace style -to some extent. -

-

Compiling

-

-Go is a compiled language. At the moment there are two compilers. -Gccgo is a Go compiler that uses the GCC back end. There is also a -suite of compilers with different (and odd) names for each architecture: -6g for the 64-bit x86, 8g for the 32-bit x86, and more. These -compilers run significantly faster but generate less efficient code -than gccgo. At the time of writing (late 2009), they also have -a more robust run-time system although gccgo is catching up. -

-Here's how to compile and run our program. With 6g, say, -

-

-$ 6g helloworld.go  # compile; object goes into helloworld.6
-$ 6l helloworld.6   # link; output goes into 6.out
-$ 6.out
-Hello, world; or Καλημέρα κόσμε; or こんにちは 世界
-$
-
-

-With gccgo it looks a little more traditional. -

-

-$ gccgo helloworld.go
-$ a.out
-Hello, world; or Καλημέρα κόσμε; or こんにちは 世界
-$
-
-

-

Echo

-

-Next up, here's a version of the Unix utility echo(1): -

-

package main
-
-import (
-    "os"
-    "flag"  // command line option parser
-)
-
-var omitNewline = flag.Bool("n", false, "don't print final newline")
-
-const (
-    Space = " "
-    Newline = "\n"
-)
-
-func main() {
-    flag.Parse()   // Scans the arg list and sets up flags
-    var s string = ""
-    for i := 0; i < flag.NArg(); i++ {
-        if i > 0 {
-            s += Space
-        }
-        s += flag.Arg(i)
-    }
-    if !*omitNewline {
-        s += Newline
-    }
-    os.Stdout.WriteString(s)
-}
-
-

-This program is small but it's doing a number of new things. In the last example, -we saw func introduce 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 lists, one item per line, as in the import and const clauses here. -But it's not necessary to do so; we could have said -

-

-const Space = " "
-const Newline = "\n"
-
-

-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 12 creates a global variable to hold -the value of echo's -n flag. The variable omitNewline has type *bool, pointer -to bool. -

-In main.main, we parse the arguments (line 20) 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. -There's one in the for clause on the next line: -

-

    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 arguments and separating spaces. After the loop, if the -n flag is not -set, the program appends a newline. Finally, it 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 a slice used by the -flag package to access the command-line arguments. -

-

An Interlude about Types

-

-Go has some familiar types such as int and uint (unsigned int), which represent -values of the ''appropriate'' size for the machine. It also defines -explicitly-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. -

-Floating-point types are always sized: float32 and float64, -plus complex64 (two float32s) and complex128 -(two float64s). Complex numbers are outside the -scope of this tutorial. -

-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: -

-

    s := "hello"
-    if s[1] != 'e' { os.Exit(1) }
-    s = "good bye"
-    var p *string = &s
-    *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 arrayOfInt [10]int
-
-

-Arrays, like strings, are values, but they are mutable. This differs -from C, in which arrayOfInt 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 hold a reference to any array, of any size, -with the same element type. -A slice -expression has the form a[low : high], representing -the internal array indexed from low through high-1; the resulting -slice is indexed from 0 through high-low-1. -In short, slices look a lot like arrays but with -no explicit size ([] vs. [10]) and they reference a segment of -an underlying, usually anonymous, regular array. Multiple slices -can share data if they represent pieces of the same array; -multiple arrays can never share data. -

-Slices are 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. To create one, use a compound value constructor—an -expression formed -from a type followed by a brace-bounded expression like this: -

-

-[3]int{1,2,3}
-
-

-In this case the constructor builds an array of 3 ints. -

-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, slice the array to create -(efficiently) a slice reference and pass that. -By default, the lower and upper bounds of a slice match the -ends of the existing object, so the concise notation [:] -will slice the whole array. -

-Using slices one can write this function (from sum.go): -

-

func sum(a []int) int { // returns an int
-    s := 0
-    for i := 0; i < len(a); i++ {
-        s += a[i]
-    }
-    return s
-}
-
-

-Note how the return type (int) is defined for sum by stating it -after the parameter list. -

-To call the function, we slice the array. This intricate call (we'll show -a simpler way in a moment) constructs -an array and slices it: -

-

-s := sum([3]int{1,2,3}[:])
-
-

-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}[:])
-
-

-That's fussier than necessary, though. -In practice, unless you're meticulous about storage layout within a -data structure, a slice itself—using empty brackets with no size—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, maps, and channels. -

-By the way, another thing that works on strings, arrays, slices, maps -and channels is the range clause on for loops. Instead of writing -

-

-for i := 0; i < len(a); i++ { ... }
-
-

-to loop over the elements of a slice (or map or ...) , we could write -

-

-for i, v := range a { ... }
-
-

-This assigns i to the index and v to the value of the successive -elements of the target of the range. See -Effective Go -for more examples of its use. -

-

-

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 a new variable, use the built-in function 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 from 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 rather than make, -you receive a pointer to a nil 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 large-precision values that -can overflow only when they are assigned to an integer variable with -too little precision to represent the value. -

-

-const hardEight = (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; uses a "conversion"
-i := 0x1234       // i gets default type: int
-var j int = 1e6   // legal - 1000000 is representable in an int
-x := 1.5          // a float64, the default type for floating constants
-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 integers and floating-point numbers, -plus a couple of other instances outside the scope of a tutorial. -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 an -open/close/read/write interface. Here's the start of file.go: -

-

package file
-
-import (
-    "os"
-    "syscall"
-)
-
-type File struct {
-    fd   int    // file descriptor number
-    name string // file name at Open time
-}
-
-

-The first few lines declare the name of the -package—file—and then 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 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 or variable, or of -a structure field or method) 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 a File: -

-

func newFile(fd int, name string) *File {
-    if fd < 0 {
-        return nil
-    }
-    return &File{fd, name}
-}
-
-

-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 -composite literal, as is done here on line 21. -

-We can use the factory to construct some familiar, exported variables of type *File: -

-

var (
-    Stdin  = newFile(syscall.Stdin, "/dev/stdin")
-    Stdout = newFile(syscall.Stdout, "/dev/stdout")
-    Stderr = newFile(syscall.Stderr, "/dev/stderr")
-)
-
-
-

-The newFile function was not exported because it's internal. The proper, -exported factory to use is OpenFile (we'll explain that name in a moment): -

-

func OpenFile(name string, mode int, perm uint32) (file *File, err os.Error) {
-    r, e := syscall.Open(name, mode, perm)
-    if e != 0 {
-        err = os.Errno(e)
-    }
-    return newFile(r, name), err
-}
-
-

-There are a number of new things in these few lines. First, OpenFile returns -multiple values, a 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 31; it declares r and e to hold the two values, -both of type int (although you'd have to look at the syscall package -to see that). Finally, line 35 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. -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 translate Unix's integer errno value into the integer type -os.Errno, which implements os.Error. -

-Why OpenFile and not Open? To mimic Go's os package, which -our exercise is emulating. The os package takes the opportunity -to make the two commonest cases - open for read and create for -write - the simplest, just Open and Create. OpenFile is the -general case, analogous to the Unix system call Open. Here is -the implementation of our Open and Create; they're trivial -wrappers that eliminate common errors by capturing -the tricky standard arguments to open and, especially, to create a file: -

-

const (
-    O_RDONLY = syscall.O_RDONLY
-    O_RDWR   = syscall.O_RDWR
-    O_CREATE = syscall.O_CREAT
-    O_TRUNC  = syscall.O_TRUNC
-)
-
-func Open(name string) (file *File, err os.Error) {
-    return OpenFile(name, O_RDONLY, 0)
-}
-
-

-

func Create(name string) (file *File, err os.Error) {
-    return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
-}
-
-

-Back to our main story. -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. -

-

func (file *File) Close() os.Error {
-    if file == nil {
-        return os.EINVAL
-    }
-    e := syscall.Close(file.fd)
-    file.fd = -1 // so it can't be closed again
-    if e != 0 {
-        return os.Errno(e)
-    }
-    return nil
-}
-
-func (file *File) Read(b []byte) (ret int, err os.Error) {
-    if file == nil {
-        return -1, os.EINVAL
-    }
-    r, e := syscall.Read(file.fd, b)
-    if e != 0 {
-        err = os.Errno(e)
-    }
-    return int(r), err
-}
-
-func (file *File) Write(b []byte) (ret int, err os.Error) {
-    if file == nil {
-        return -1, os.EINVAL
-    }
-    r, e := syscall.Write(file.fd, b)
-    if e != 0 {
-        err = os.Errno(e)
-    }
-    return int(r), err
-}
-
-func (file *File) String() string {
-    return file.name
-}
-
-

-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 almost 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 a 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: -

-

package main
-
-import (
-    "./file"
-    "fmt"
-    "os"
-)
-
-func main() {
-    hello := []byte("hello, world\n")
-    file.Stdout.Write(hello)
-    f, err := file.Open("/does/not/exist")
-    if f == nil {
-        fmt.Printf("can't open file; err=%s\n",  err.String())
-        os.Exit(1)
-    }
-}
-
-

-The ''./'' in the import of ''./file'' tells the compiler -to use our own package rather than -something from the directory of installed packages. -(Also, ''file.go'' must be compiled before we can import the -package.) -

-Now we can compile and run the program. On Unix, this would be the result: -

-

-$ 6g file.go                       # compile file package
-$ 6g helloworld3.go                # compile main package
-$ 6l -o helloworld3 helloworld3.6  # link - no need to mention "file"
-$ 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: -

-

package main
-
-import (
-    "./file"
-    "flag"
-    "fmt"
-    "os"
-)
-
-func cat(f *file.File) {
-    const NBUF = 512
-    var buf [NBUF]byte
-    for {
-        switch nr, er := f.Read(buf[:]); true {
-        case nr < 0:
-            fmt.Fprintf(os.Stderr, "cat: error reading from %s: %s\n", f.String(), er.String())
-            os.Exit(1)
-        case nr == 0: // EOF
-            return
-        case nr > 0:
-            if nw, ew := file.Stdout.Write(buf[0:nr]); nw != nr {
-                fmt.Fprintf(os.Stderr, "cat: error writing from %s: %s\n", f.String(), ew.String())
-                os.Exit(1)
-            }
-        }
-    }
-}
-
-func main() {
-    flag.Parse() // Scans the arg list and sets up flags
-    if flag.NArg() == 0 {
-        cat(file.Stdin)
-    }
-    for i := 0; i < flag.NArg(); i++ {
-        f, err := file.Open(flag.Arg(i))
-        if f == nil {
-            fmt.Fprintf(os.Stderr, "cat: can't open %s: error %s\n", flag.Arg(i), err)
-            os.Exit(1)
-        }
-        cat(f)
-        f.Close()
-    }
-}
-
-

-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 statement in cat uses one to create variables -nr and er to hold the return values from the call to f.Read. (The if a few lines later -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. -

-The argument to file.Stdout.Write is created by slicing the array buf. -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: -

-

type reader interface {
-    Read(b []byte) (ret int, err os.Error)
-    String() string
-}
-
-

-Any type that has the two methods of reader—regardless of whatever -other methods the type may also have—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. -

-

type rotate13 struct {
-    source reader
-}
-
-func newRotate13(source reader) *rotate13 {
-    return &rotate13{source}
-}
-
-func (r13 *rotate13) Read(b []byte) (ret int, err os.Error) {
-    r, e := r13.source.Read(b)
-    for i := 0; i < r; i++ {
-        b[i] = rot13(b[i])
-    }
-    return r, e
-}
-
-func (r13 *rotate13) String() string {
-    return r13.source.String()
-}
-// end of rotate13 implementation
-
-

-(The rot13 function called in Read is trivial and not worth reproducing here.) -

-To use the new feature, we define a flag: -

-

var rot13Flag = flag.Bool("rot13", false, "rot13 the input")
-
-

-and use it from within a mostly unchanged cat function: -

-

func cat(r reader) {
-    const NBUF = 512
-    var buf [NBUF]byte
-
-    if *rot13Flag {
-        r = newRotate13(r)
-    }
-    for {
-        switch nr, er := r.Read(buf[:]); {
-        case nr < 0:
-            fmt.Fprintf(os.Stderr, "cat: error reading from %s: %s\n", r.String(), er.String())
-            os.Exit(1)
-        case nr == 0: // EOF
-            return
-        case nr > 0:
-            nw, ew := file.Stdout.Write(buf[0:nr])
-            if nw != nr {
-                fmt.Fprintf(os.Stderr, "cat: error writing from %s: %s\n", r.String(), ew.String())
-                os.Exit(1)
-            }
-        }
-    }
-}
-
-

-(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.) -The if at the top of cat sets 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 distinctive 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. 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: -

-

func Sort(data Interface) {
-    for i := 1; i < data.Len(); i++ {
-        for j := i; j > 0 && data.Less(j, j-1); j-- {
-            data.Swap(j, j-1)
-        }
-    }
-}
-
-

-The code needs only three methods, which we wrap into sort's Interface: -

-

type Interface interface {
-    Len() int
-    Less(i, j int) bool
-    Swap(i, j int)
-}
-
-

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

-

type IntSlice []int
-
-func (p IntSlice) Len() int            { return len(p) }
-func (p IntSlice) Less(i, j int) bool  { return p[i] < p[j] }
-func (p IntSlice) Swap(i, j int)       { p[i], p[j] = p[j], p[i] }
-
-

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

-

func ints() {
-    data := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
-    a := sort.IntSlice(data)
-    sort.Sort(a)
-    if !sort.IsSorted(a) {
-        panic("fail")
-    }
-}
-
-

-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: -

-

type day struct {
-    num        int
-    shortName  string
-    longName   string
-}
-
-type dayArray struct {
-    data []*day
-}
-
-func (p *dayArray) Len() int            { return len(p.data) }
-func (p *dayArray) Less(i, j int) bool  { return p.data[i].num < p.data[j].num }
-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 ...interface{}) (n int, errno os.Error)
-
-

-The token ... introduces a variable-length argument list that in C would -be handled using the stdarg.h macros. -In Go, variadic functions are passed a slice of the arguments of the -specified type. In Printf's case, the declaration says ...interface{} -so the actual type is a slice of empty interface values, []interface{}. -Printf can examine the arguments by iterating over the slice -and, for each element, using a type switch or the reflection library -to interpret the value. -It's off topic here but such run-time type analysis -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 -

-

    var u64 uint64 = 1<<64-1
-    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 -

-

    type T struct {
-        a int
-        b string
-    }
-    t := T{77, "Sunset Strip"}
-    a := []int{1, 2, 3, 4}
-    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 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. -

-

    fmt.Print(u64, " ", t, " ", a, "\n")
-    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. -

-

type testType struct {
-    a int
-    b string
-}
-
-func (t *testType) String() string {
-    return fmt.Sprint(t.a) + " " + t.b
-}
-
-func main() {
-    t := &testType{77, "Sunset Strip"}
-    fmt.Println(t)
-}
-
-

-Since *testType 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 = defaultOutput(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, 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 a prime sieve. -(The sieve of Eratosthenes is computationally more efficient than -the algorithm presented here, but we are more interested in concurrency than -algorithmics at the moment.) -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: -

-

// Send the sequence 2, 3, 4, ... to channel 'ch'.
-func generate(ch chan int) {
-    for i := 2; ; i++ {
-        ch <- i  // Send 'i' to channel 'ch'.
-    }
-}
-
-

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

-

// Copy the values from channel 'in' to channel 'out',
-// removing those divisible by 'prime'.
-func filter(in, out chan int, prime int) {
-    for {
-        i := <-in  // Receive value of new variable 'i' from 'in'.
-        if i % prime != 0 {
-            out <- i  // Send 'i' to channel 'out'.
-        }
-    }
-}
-
-

-The generator and filters execute concurrently. Go has -its own model of process/threads/light-weight processes/coroutines, -so to avoid notational confusion we 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(hugeArray) // 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(hugeArray, 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: -

-

func main() {
-    ch := make(chan int)  // Create a new channel.
-    go generate(ch)  // Start generate() as a goroutine.
-    for i := 0; i < 100; i++ { // Print the first hundred primes.
-        prime := <-ch
-        fmt.Println(prime)
-        ch1 := make(chan int)
-        go filter(ch, ch1, prime)
-        ch = ch1
-    }
-}
-
-

-The first line of main 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: -

-

func generate() chan int {
-    ch := make(chan int)
-    go func(){
-        for i := 2; ; i++ {
-            ch <- i
-        }
-    }()
-    return ch
-}
-
-

-This version does all the setup internally. It creates the output -channel, launches a goroutine running 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 used in the go statement 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: -

-

func filter(in chan int, prime int) chan int {
-    out := make(chan int)
-    go func() {
-        for {
-            if i := <-in; i % prime != 0 {
-                out <- i
-            }
-        }
-    }()
-    return out
-}
-
-

-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: -

-

func sieve() chan int {
-    out := make(chan int)
-    go func() {
-        ch := generate()
-        for {
-            prime := <-ch
-            out <- prime
-            ch = filter(ch, prime)
-        }
-    }()
-    return out
-}
-
-

-Now main's interface to the prime sieve is a channel of primes: -

-

func main() {
-    primes := sieve()
-    for i := 0; i < 100; i++ { // Print the first hundred primes.
-        fmt.Println(<-primes)
-    }
-}
-
-

-

Multiplexing

-

-With channels, it's possible to serve multiple independent client goroutines without -writing an explicit 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. -

-

type request struct {
-    a, b    int
-    replyc  chan int
-}
-
-

-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: -

-

type binOp func(a, b int) int
-
-func run(op binOp, req *request) {
-    reply := op(req.a, req.b)
-    req.replyc <- reply
-}
-
-

-The type declaration makes binOp represent 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. -

-

func server(op binOp, service chan *request) {
-    for {
-        req := <-service
-        go run(op, req)  // don't wait for it
-    }
-}
-
-

-We construct a server in a familiar way, starting it and returning a channel -connected to it: -

-

func startServer(op binOp) chan *request {
-    req := make(chan *request)
-    go server(op, req)
-    return req
-}
-
-

-Here's a simple test. It starts a server with an addition operator and sends out -N requests without waiting for the replies. Only after all the requests are sent -does it check the results. -

-

func main() {
-    adder := startServer(func(a, b int) int { return a + b })
-    const N = 100
-    var reqs [N]request
-    for i := 0; i < N; i++ {
-        req := &reqs[i]
-        req.a = i
-        req.b = i + N
-        req.replyc = make(chan int)
-        adder <- req
-    }
-    for i := N-1; i >= 0; i-- {   // doesn't matter what order
-        if <-reqs[i].replyc != N + 2*i {
-            fmt.Println("fail at", i)
-        }
-    }
-    fmt.Println("done")
-}
-
-

-One annoyance with this program is that it doesn't shut down the server 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: -

-

func startServer(op binOp) (service chan *request, quit chan bool) {
-    service = make(chan *request)
-    quit = make(chan bool)
-    go server(op, service, quit)
-    return service, quit
-}
-
-

-It passes the quit channel to the server function, which uses it like this: -

-

func server(op binOp, service chan *request, quit chan bool) {
-    for {
-        select {
-        case req := <-service:
-            go run(op, req)  // don't wait for it
-        case <-quit:
-            return
-        }
-    }
-}
-
-

-Inside server, the 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: -

-

    adder, quit := startServer(func(a, b int) int { return a + b })
-
-... -
    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. -- cgit v1.2.3