From 7201b0c27c45d06ccb1f2ac4f275200a863bae91 Mon Sep 17 00:00:00 2001
From: Rob Pike
-The first tutorial. An introductory text that touches upon several core -concepts: syntax, types, allocation, constants, I/O, sorting, printing, -goroutines, and channels. -
-A document that gives tips for writing clear, idiomatic Go code. -A must read for any new Go programmer. It augments the tutorial and +A must read for any new Go programmer. It augments the tour and the language specification, both of which should be read first.
@@ -226,7 +219,7 @@ Go libraries.This document gives tips for writing clear, idiomatic Go code. -It augments the language specification -and the tutorial, both of which you +It augments the language specification, +the Tour of Go, +and How to Write Go Code, +all of which you should read first.
@@ -1454,7 +1456,7 @@ fmt.Println(fmt.Sprint("Hello ", 23))
As mentioned in
-the tutorial, fmt.Fprint
+the Tour, fmt.Fprint
and friends take as a first argument any object
that implements the io.Writer
interface; the variables os.Stdout
and os.Stderr
are familiar instances.
diff --git a/doc/effective_go.tmpl b/doc/effective_go.tmpl
index 5763cacdab..446b0525e3 100644
--- a/doc/effective_go.tmpl
+++ b/doc/effective_go.tmpl
@@ -27,8 +27,10 @@ will be easy for other Go programmers to understand.
This document gives tips for writing clear, idiomatic Go code. -It augments the language specification -and the tutorial, both of which you +It augments the language specification, +the Tour of Go, +and How to Write Go Code, +all of which you should read first.
@@ -1450,7 +1452,7 @@ fmt.Println(fmt.Sprint("Hello ", 23))
As mentioned in
-the tutorial, fmt.Fprint
+the Tour, fmt.Fprint
and friends take as a first argument any object
that implements the io.Writer
interface; the variables os.Stdout
and os.Stderr
are familiar instances.
diff --git a/doc/go_for_cpp_programmers.html b/doc/go_for_cpp_programmers.html
index 8e215190fa..7a9f616079 100644
--- a/doc/go_for_cpp_programmers.html
+++ b/doc/go_for_cpp_programmers.html
@@ -11,8 +11,9 @@ to nothing about the similarities.
For a more general introduction to Go, see the -Go tutorial and -Effective Go. +Go Tour, +How to Write Go Code +and Effective Go.
For a detailed description of the Go language, see the diff --git a/doc/go_tutorial.html b/doc/go_tutorial.html deleted file mode 100644 index 589262363f..0000000000 --- a/doc/go_tutorial.html +++ /dev/null @@ -1,1452 +0,0 @@ - - - - -
-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. -An interactive introduction to Go is available, called -A Tour of Go. -
-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/
.
-
-
-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. -
-
-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.
-
-
-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 ããã«ã¡ã¯ ä¸ç -$ --
-
-Next up, here's a version of the Unix utility echo(1)
:
-
-
package main - -import ( - "flag" // command line option parser - "os" -) - -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.
-
-After importing the flag
package, we use a var
declaration
-to create and initialize a global variable, called omitNewline
,
-to hold the value of echo's -n
flag.
-The variable has type *bool
, pointer to bool
.
-
-In main.main
, we parse the arguments (the call to flag.Parse
) and then create a local
-string variable with which 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.
-
-
-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 code (we'll show -a simpler way in a moment) constructs -an array and slices it: -
-
-x := [3]int{1,2,3} -s := sum(x[:]) --
-If you are creating a regular array but want the compiler to count the
-elements for you, use ...
as the array size:
-
-
-x := [...]int{1,2,3} -s := sum(x[:]) --
-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.
-
-
-
-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.
-
-
-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.
-
-
-Next we'll look at a simple package for doing Unix 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.
-The syscall
package is very system-dependent, and the way it's
-used here works only on Unix-like systems,
-but the general ideas explored here apply broadly.
-(A Windows version is available in
-file_windows.go
.)
-
-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 in the return
statement from newFile
.
-
-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 error) { - r, err := syscall.Open(name, mode, perm) - 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 the first line; 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, OpenFile
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 Go language includes a general notion of an error:
-a pre-defined type error
with properties (described below)
-that make it a good basis for representing and handling errors.
-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 is an implementation of 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 error) { - return OpenFile(name, O_RDONLY, 0) -}-
-
func Create(name string) (file *File, err 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() error { - if file == nil { - return os.ErrInvalid - } - err := syscall.Close(file.fd) - file.fd = -1 // so it can't be closed again - return err -} - -func (file *File) Read(b []byte) (ret int, err error) { - if file == nil { - return -1, os.ErrInvalid - } - r, err := syscall.Read(file.fd, b) - return int(r), err -} - -func (file *File) Write(b []byte) (ret int, err error) { - if file == nil { - return -1, os.ErrInvalid - } - r, err := syscall.Write(file.fd, b) - 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.ErrInvalid
to return the (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.Error()) - 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 -$ --
-
-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, er) - 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, ew) - 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 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 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() -}-
-(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, er) - 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, ew) - 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. -
-
-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] }-
-
-
-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 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.
-
-A related interface is that defined by the error
builtin type, which is just
-
-
-type error interface { - Error() string -} --
-Other than the method name (Error
vs. String
), this looks like
-a Stringer
; the different name guarantees that types that implement Stringer
-don't accidentally satisfy the error
interface.
-Naturally, Printf
and its relatives recognize the error
interface,
-just as they do Stringer
,
-so it's trivial to print an error as a string.
-
-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 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.
-
-
-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) - } -}-
-
-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 - } -}-
-There's a new feature in the signature of server
: the type of the
-service
channel specifies the direction of communication.
-A channel of plain chan
type can be used both for sending and receiving.
-However, the type used when declaring a channel can be decorated with an arrow to
-indicate that the channel can be used only to send (chan<-
) or to
-receive (<-chan
) data.
-The arrow points towards or away from the chan
to indicate whether data flows into or out of
-the channel.
-In the server
function, service <-chan *request
is a "receive only" channel
-that the function can use only to read new requests.
-
-We instantiate 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 -}-
-The returned channel is send only, even though the channel was created bidirectionally.
-The read end is passed to server
, while the send end is returned
-to the caller of startServer
, so the two halves of the channel
-are distinguished, just as we did in startServer
.
-
-Bidirectional channels can be assigned to unidirectional channels but not the
-other way around, so if you annotate your channel directions when you declare
-them, such as in function signatures, the type system can help you set up and
-use channels correctly.
-Note that it's pointless to make
unidirectional channels, since you can't
-use them to communicate. Their purpose is served by variables assigned from bidirectional channels
-to distinguish the input and output halves.
-
-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. diff --git a/doc/go_tutorial.tmpl b/doc/go_tutorial.tmpl deleted file mode 100644 index 33189189e4..0000000000 --- a/doc/go_tutorial.tmpl +++ /dev/null @@ -1,1040 +0,0 @@ - -{{donotedit}} - -
-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. -An interactive introduction to Go is available, called -A Tour of Go. -
-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/
.
-
-
-Let's start in the usual way: -
-{{code "progs/helloworld.go" `/package/` "$"}} -
-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. -
-
-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.
-
-
-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 ããã«ã¡ã¯ ä¸ç -$ --
-
-Next up, here's a version of the Unix utility echo(1)
:
-
-{{code "progs/echo.go" `/package/` "$"}} -
-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.
-
-After importing the flag
package, we use a var
declaration
-to create and initialize a global variable, called omitNewline
,
-to hold the value of echo's -n
flag.
-The variable has type *bool
, pointer to bool
.
-
-In main.main
, we parse the arguments (the call to flag.Parse
) and then create a local
-string variable with which 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:
-
-{{code "progs/echo.go" `/for/`}} -
-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.
-
-
-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:
-
-{{code "progs/strings.go" `/hello/` `/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
):
-
-{{code "progs/sum.go" `/sum/` `/^}/`}} -
-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 code (we'll show -a simpler way in a moment) constructs -an array and slices it: -
-
-x := [3]int{1,2,3} -s := sum(x[:]) --
-If you are creating a regular array but want the compiler to count the
-elements for you, use ...
as the array size:
-
-
-x := [...]int{1,2,3} -s := sum(x[:]) --
-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.
-
-
-
-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.
-
-
-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.
-
-
-Next we'll look at a simple package for doing Unix file I/O with an
-open/close/read/write interface.
-Here's the start of file.go
:
-
-{{code "progs/file.go" `/package/` `/^}/`}} -
-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.
-The syscall
package is very system-dependent, and the way it's
-used here works only on Unix-like systems,
-but the general ideas explored here apply broadly.
-(A Windows version is available in
-file_windows.go
.)
-
-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
:
-
-{{code "progs/file.go" `/newFile/` `/^}/`}} -
-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 in the return
statement from newFile
.
-
-We can use the factory to construct some familiar, exported variables of type *File
:
-
-{{code "progs/file.go" `/var/` `/^\)/`}} -
-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):
-
-{{code "progs/file.go" `/func.OpenFile/` `/^}/`}} -
-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 the first line; 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, OpenFile
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 Go language includes a general notion of an error:
-a pre-defined type error
with properties (described below)
-that make it a good basis for representing and handling errors.
-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 is an implementation of 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:
-
-{{code "progs/file.go" `/^const/` `/^}/`}} -
-{{code "progs/file.go" `/func.Create/` `/^}/`}} -
-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
.
-
-{{code "progs/file.go" `/Close/` "$"}} -
-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.ErrInvalid
to return the (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: -
-{{code "progs/helloworld3.go" `/package/` "$"}} -
-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 -$ --
-
-Building on the file
package, here's a simple version of the Unix utility cat(1)
,
-progs/cat.go
:
-
-{{code "progs/cat.go" `/package/` "$"}} -
-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
:
-
-{{code "progs/cat_rot13.go" `/type.reader/` `/^}/`}} -
-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.
-
-{{code "progs/cat_rot13.go" `/type.rotate13/` `/end.of.rotate13/`}} -
-(The rot13
function called in Read
is trivial and not worth reproducing here.)
-
-To use the new feature, we define a flag: -
-{{code "progs/cat_rot13.go" `/rot13Flag/`}} -
-and use it from within a mostly unchanged cat
function:
-
-{{code "progs/cat_rot13.go" `/func.cat/` `/^}/`}} -
-(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. -
-
-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
:
-
-{{code "progs/sort.go" `/func.Sort/` `/^}/`}} -
-The code needs only three methods, which we wrap into sort's Interface
:
-
-{{code "progs/sort.go" `/interface/` `/^}/`}} -
-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
-
-{{code "progs/sort.go" `/type.*IntSlice/` `/Swap/`}} -
-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.
-
-{{code "progs/sortmain.go" `/func.ints/` `/^}/`}} -
-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: -
-{{code "progs/sortmain.go" `/type.day/` `/Swap/`}} -
-
-
-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 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
-
-{{code "progs/print.go" 10 11}} -
-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
-
-{{code "progs/print.go" 14 20}} -
-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.
-
-{{code "progs/print.go" 21 22}} -
-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.
-
-{{code "progs/print_string.go" 9 "$"}} -
-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.
-
-A related interface is that defined by the error
builtin type, which is just
-
-
-type error interface { - Error() string -} --
-Other than the method name (Error
vs. String
), this looks like
-a Stringer
; the different name guarantees that types that implement Stringer
-don't accidentally satisfy the error
interface.
-Naturally, Printf
and its relatives recognize the error
interface,
-just as they do Stringer
,
-so it's trivial to print an error as a string.
-
-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 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.
-
-
-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
:
-
-{{code "progs/sieve.go" `/Send/` `/^}/`}} -
-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.
-
-{{code "progs/sieve.go" `/Copy.the/` `/^}/`}} -
-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: -
-{{code "progs/sieve.go" `/func.main/` `/^}/`}} -
-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
:
-
-{{code "progs/sieve1.go" `/func.generate/` `/^}/`}} -
-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
:
-
-{{code "progs/sieve1.go" `/func.filter/` `/^}/`}} -
-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:
-
-{{code "progs/sieve1.go" `/func.sieve/` `/^}/`}} -
-Now main
's interface to the prime sieve is a channel of primes:
-
-{{code "progs/sieve1.go" `/func.main/` `/^}/`}} -
-
-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.
-
-{{code "progs/server.go" `/type.request/` `/^}/`}} -
-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: -
-{{code "progs/server.go" `/type.binOp/` `/^}/`}} -
-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.
-
-{{code "progs/server.go" `/func.server/` `/^}/`}} -
-There's a new feature in the signature of server
: the type of the
-service
channel specifies the direction of communication.
-A channel of plain chan
type can be used both for sending and receiving.
-However, the type used when declaring a channel can be decorated with an arrow to
-indicate that the channel can be used only to send (chan<-
) or to
-receive (<-chan
) data.
-The arrow points towards or away from the chan
to indicate whether data flows into or out of
-the channel.
-In the server
function, service <-chan *request
is a "receive only" channel
-that the function can use only to read new requests.
-
-We instantiate a server in a familiar way, starting it and returning a channel -connected to it: -
-{{code "progs/server.go" `/func.startServer/` `/^}/`}} -
-The returned channel is send only, even though the channel was created bidirectionally.
-The read end is passed to server
, while the send end is returned
-to the caller of startServer
, so the two halves of the channel
-are distinguished, just as we did in startServer
.
-
-Bidirectional channels can be assigned to unidirectional channels but not the
-other way around, so if you annotate your channel directions when you declare
-them, such as in function signatures, the type system can help you set up and
-use channels correctly.
-Note that it's pointless to make
unidirectional channels, since you can't
-use them to communicate. Their purpose is served by variables assigned from bidirectional channels
-to distinguish the input and output halves.
-
-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.
-
-{{code "progs/server.go" `/func.main/` `/^}/`}} -
-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:
-
-{{code "progs/server1.go" `/func.startServer/` `/^}/`}} -
-It passes the quit channel to the server
function, which uses it like this:
-
-{{code "progs/server1.go" `/func.server/` `/^}/`}} -
-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:
-
-{{code "progs/server1.go" `/adder,.quit/`}} -... -{{code "progs/server1.go" `/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. diff --git a/doc/install-source.html b/doc/install-source.html index 5b138d5796..ecb386f597 100644 --- a/doc/install-source.html +++ b/doc/install-source.html @@ -225,8 +225,7 @@ If you see the "hello, world" message then Go is installed correctly.
-Start by taking A Tour of Go -or reading the Go Tutorial. +Start by taking A Tour of Go.
diff --git a/doc/makehtml b/doc/makehtml index 8a029132f4..f6f601e3be 100755 --- a/doc/makehtml +++ b/doc/makehtml @@ -5,7 +5,7 @@ set -e -TMPL=${1:-go_tutorial.tmpl} # input file +TMPL=${1:-effective_go.tmpl} # input file HTML=$(dirname $TMPL)/$(basename $TMPL .tmpl).html # output file if ! test -w $HTML diff --git a/doc/progs/cat.go b/doc/progs/cat.go deleted file mode 100644 index 79ad015039..0000000000 --- a/doc/progs/cat.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -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, er) - 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, ew) - 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() - } -} diff --git a/doc/progs/cat_rot13.go b/doc/progs/cat_rot13.go deleted file mode 100644 index c8584ed47c..0000000000 --- a/doc/progs/cat_rot13.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "./file" - "flag" - "fmt" - "os" -) - -var rot13Flag = flag.Bool("rot13", false, "rot13 the input") - -func rot13(b byte) byte { - if 'a' <= b && b <= 'z' { - b = 'a' + ((b-'a')+13)%26 - } - if 'A' <= b && b <= 'Z' { - b = 'A' + ((b-'A')+13)%26 - } - return b -} - -type reader interface { - Read(b []byte) (ret int, err error) - String() string -} - -type rotate13 struct { - source reader -} - -func newRotate13(source reader) *rotate13 { - return &rotate13{source} -} - -func (r13 *rotate13) Read(b []byte) (ret int, err 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 OMIT - -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, er) - 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, ew) - 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() - } -} diff --git a/doc/progs/echo.go b/doc/progs/echo.go deleted file mode 100644 index 432e808207..0000000000 --- a/doc/progs/echo.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "flag" // command line option parser - "os" -) - -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) -} diff --git a/doc/progs/file.go b/doc/progs/file.go deleted file mode 100644 index 75f0f207ac..0000000000 --- a/doc/progs/file.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package file - -import ( - "os" - "syscall" -) - -type File struct { - fd int // file descriptor number - name string // file name at Open time -} - -func newFile(fd int, name string) *File { - if fd < 0 { - return nil - } - return &File{fd, name} -} - -var ( - Stdin = newFile(syscall.Stdin, "/dev/stdin") - Stdout = newFile(syscall.Stdout, "/dev/stdout") - Stderr = newFile(syscall.Stderr, "/dev/stderr") -) - -func OpenFile(name string, mode int, perm uint32) (file *File, err error) { - r, err := syscall.Open(name, mode, perm) - return newFile(r, name), err -} - -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 error) { - return OpenFile(name, O_RDONLY, 0) -} - -func Create(name string) (file *File, err error) { - return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666) -} - -func (file *File) Close() error { - if file == nil { - return os.ErrInvalid - } - err := syscall.Close(file.fd) - file.fd = -1 // so it can't be closed again - return err -} - -func (file *File) Read(b []byte) (ret int, err error) { - if file == nil { - return -1, os.ErrInvalid - } - r, err := syscall.Read(file.fd, b) - return int(r), err -} - -func (file *File) Write(b []byte) (ret int, err error) { - if file == nil { - return -1, os.ErrInvalid - } - r, err := syscall.Write(file.fd, b) - return int(r), err -} - -func (file *File) String() string { - return file.name -} diff --git a/doc/progs/file_windows.go b/doc/progs/file_windows.go deleted file mode 100644 index 8b79ee97de..0000000000 --- a/doc/progs/file_windows.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package file - -import ( - "os" - "syscall" -) - -type File struct { - fd syscall.Handle // file descriptor number - name string // file name at Open time -} - -func newFile(fd syscall.Handle, name string) *File { - if fd == ^syscall.Handle(0) { - return nil - } - return &File{fd, name} -} - -var ( - Stdin = newFile(syscall.Stdin, "/dev/stdin") - Stdout = newFile(syscall.Stdout, "/dev/stdout") - Stderr = newFile(syscall.Stderr, "/dev/stderr") -) - -func OpenFile(name string, mode int, perm uint32) (file *File, err error) { - r, err := syscall.Open(name, mode, perm) - return newFile(r, name), err -} - -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 error) { - return OpenFile(name, O_RDONLY, 0) -} - -func Create(name string) (file *File, err error) { - return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666) -} - -func (file *File) Close() error { - if file == nil { - return os.ErrInvalid - } - err := syscall.Close(file.fd) - file.fd = syscall.InvalidHandle // so it can't be closed again - return err -} - -func (file *File) Read(b []byte) (ret int, err error) { - if file == nil { - return -1, os.ErrInvalid - } - r, err := syscall.Read(file.fd, b) - return int(r), err -} - -func (file *File) Write(b []byte) (ret int, err error) { - if file == nil { - return -1, os.ErrInvalid - } - r, err := syscall.Write(file.fd, b) - return int(r), err -} - -func (file *File) String() string { - return file.name -} diff --git a/doc/progs/run b/doc/progs/run index 5a2b786516..2470d5e8e0 100755 --- a/doc/progs/run +++ b/doc/progs/run @@ -14,12 +14,6 @@ fi rm -f *.$O -if [ "$GOOS" = "windows" ];then - $GC -o file.$O file_windows.go -else - $GC file.go -fi - defer_panic_recover=" defer.go defer2.go @@ -38,29 +32,10 @@ error_handling=" error4.go " -go_tutorial=" - cat.go - cat_rot13.go - echo.go - helloworld.go - helloworld3.go - print.go - print_string.go - server.go - server1.go - sieve.go - sieve1.go - sort.go - sortmain.go - strings.go - sum.go -" - for i in \ $defer_panic_recover \ $effective_go \ $error_handling \ - $go_tutorial \ slices.go \ go1.go \ ; do @@ -80,43 +55,10 @@ function testit { fi } -function testitpipe { - $LD $1.$O - ./$O.out | $2 2>&1 >"$TMPFILE" || true - x=$(echo $(cat "$TMPFILE")) # extra echo canonicalizes - if [ "$x" != "$3" ] - then - echo $1 failed: '"'$x'"' is not '"'$3'"' - fi -} - -testit helloworld "" "Hello, world; or ÎαλημÎÏα κÏÏμε; or ããã«ã¡ã¯ ä¸ç" -testit helloworld3 "" "hello, world can't open file; err=no such file or directory" -testit echo "hello, world" "hello, world" -testit sum "" "6" -testit strings "" "" testit defer "" "0 3210 2" testit defer2 "" "Calling g. Printing in g 0 Printing in g 1 Printing in g 2 Printing in g 3 Panicking! Defer in g 3 Defer in g 2 Defer in g 1 Defer in g 0 Recovered in f 4 Returned normally from f." -alphabet=abcdefghijklmnopqrstuvwxyz -rot13=nopqrstuvwxyzabcdefghijklm -echo $alphabet | testit cat "" $alphabet -echo $alphabet | testit cat_rot13 "--rot13" $rot13 -echo $rot13 | testit cat_rot13 "--rot13" $alphabet - -testit sortmain "" "Sunday Monday Tuesday Wednesday Thursday Friday Saturday" - -testit print "" "18446744073709551615 -1 18446744073709551615 {77 Sunset Strip} [1 2 3 4] 18446744073709551615 {77 Sunset Strip} [1 2 3 4] 18446744073709551615 {77 Sunset Strip} [1 2 3 4]" -testit print_string "" "77 Sunset Strip" - -testitpipe sieve "sed 10q" "2 3 5 7 11 13 17 19 23 29" -testitpipe sieve "sed 10q" "2 3 5 7 11 13 17 19 23 29" - -# server hangs; don't run it, just compile it -$GC server.go -testit server1 "" "" - testit eff_bytesize "" "1.00YB 9.09TB" testit eff_sequence "" "[-1 2 6 16 44]" diff --git a/doc/progs/sieve.go b/doc/progs/sieve.go deleted file mode 100644 index b315309819..0000000000 --- a/doc/progs/sieve.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import "fmt" - -// 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'. - } -} - -// 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 prime sieve: Daisy-chain filter processes 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 - } -} diff --git a/doc/progs/sieve1.go b/doc/progs/sieve1.go deleted file mode 100644 index e1411a3346..0000000000 --- a/doc/progs/sieve1.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import "fmt" - -// Send the sequence 2, 3, 4, ... to returned channel -func generate() chan int { - ch := make(chan int) - go func() { - for i := 2; ; i++ { - ch <- i - } - }() - return ch -} - -// Filter out input values divisible by 'prime', send rest to returned channel -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 -} - -func sieve() chan int { - out := make(chan int) - go func() { - ch := generate() - for { - prime := <-ch - out <- prime - ch = filter(ch, prime) - } - }() - return out -} - -func main() { - primes := sieve() - for i := 0; i < 100; i++ { // Print the first hundred primes. - fmt.Println(<-primes) - } -} diff --git a/doc/progs/sortmain.go b/doc/progs/sortmain.go deleted file mode 100644 index 1bc3355fd0..0000000000 --- a/doc/progs/sortmain.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "./sort" - "fmt" -) - -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") - } -} - -func strings() { - data := []string{"monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"} - a := sort.StringSlice(data) - sort.Sort(a) - if !sort.IsSorted(a) { - panic("fail") - } -} - -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] } - -func days() { - Sunday := day{0, "SUN", "Sunday"} - Monday := day{1, "MON", "Monday"} - Tuesday := day{2, "TUE", "Tuesday"} - Wednesday := day{3, "WED", "Wednesday"} - Thursday := day{4, "THU", "Thursday"} - Friday := day{5, "FRI", "Friday"} - Saturday := day{6, "SAT", "Saturday"} - data := []*day{&Tuesday, &Thursday, &Wednesday, &Sunday, &Monday, &Friday, &Saturday} - a := dayArray{data} - sort.Sort(&a) - if !sort.IsSorted(&a) { - panic("fail") - } - for _, d := range data { - fmt.Printf("%s ", d.longName) - } - fmt.Printf("\n") -} - -func main() { - ints() - strings() - days() -} diff --git a/doc/talks/go_talk-20100323.html b/doc/talks/go_talk-20100323.html index 3143b079ae..7330dd2ae5 100644 --- a/doc/talks/go_talk-20100323.html +++ b/doc/talks/go_talk-20100323.html @@ -380,7 +380,7 @@ containing other data structures as continuous blocks of memory."