diff --git a/.env b/.env new file mode 100644 index 0000000..c436e88 --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +CANONICAL_HOST=127.0.0.1 +FORCE_HTTPS=0 diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..4d0c0cf --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: gobyexample diff --git a/README.md b/README.md index 78fab16..f3165e6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## Go by Example -Content and toolchain for [Go by Example](https://gobyexample.com). +Content, toolchain, and web server for [Go by Example](https://gobyexample.com). ### Overview @@ -10,10 +10,9 @@ comments from source files in `examples` and rendering that data via the site `templates`. The programs implementing this build process are in `tools`. -The build process produces a directory of static files -suitable for serving by any modern HTTP server. The -Go by Example site is served by a -[lightweight Go server](https://github.com/mmcgrana/gobyexample-server). +The build process produces a directory of static files - +`public` - suitable for serving by any modern HTTP server. +We include a lightweight Go server in `server.go`. ### Building @@ -22,7 +21,7 @@ To build the site: ```console $ tools/build -$ open site/index.html +$ open public/index.html ``` To build continuously in a loop: @@ -40,16 +39,53 @@ $ export PATH="$HOME/repos/pygments:$PATH" ``` -### Serving +### Local Deploy -The site is served by the [gobyexample-server](https://github.com/mmcgrana/gobyexample-server) -tool. To export to this tool: - -```console -$ SITEDIR=../gobyexample-server/public tool/build +```bash +$ mkdir -p $GOPATH/src/github.com/mmcgrana +$ cd $GOPATH/src/github.com/mmcgrana +$ git clone git@github.com:mmcgrana/gobyexaple.git +$ cd gobyexample +$ go get +$ foreman start +$ foreman open ``` +### Platform Deploy + +Basic setup: + +```bash +$ export DEPLOY=$USER +$ heroku create gobyexample-$DEPLOY -r $DEPLOY +$ heroku config:add -r $DEPLOY \ + BUILDPACK_URL=https://github.com/kr/heroku-buildpack-go.git -r $DEPLOY \ + CANONICAL_HOST=gobyexample-$DEPLOY.herokuapp.com -r $DEPLOY \ + FORCE_HTTPS=1 \ + AUTH=go:byexample +$ git push $DEPLOY master +$ heroku open -r $DEPLOY +``` + +Add a domain + SSL: + +```bash +$ heroku domains:add $DOMAIN +$ heroku addons:add ssl -r $DEPLOY +# order ssl cert for domain +$ cat > /tmp/server.key +$ cat > /tmp/server.crt.orig +$ cat /tmp/server.crt.orig /tmp/rapidssl_bundle.pem > /tmp/server.crt +$ heroku certs:add /tmp/server.crt /tmp/server.key -r $DEPLOY +# add ALIAS record from domain to ssl endpoint dns +$ heroku config:add CANONICAL_HOST=$DOMAIN -r $DEPLOY +$ heroku open -r $DEPLOY +``` + + + + ### License This work is licensed under a diff --git a/public/404.html b/public/404.html new file mode 100644 index 0000000..2c5c604 --- /dev/null +++ b/public/404.html @@ -0,0 +1,29 @@ + + +
+ +Sorry, we couldn't find that! Check out the home page?
+ +
+ In Go, an array is a numbered sequence of elements of a +specific length. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ + | +
+ func main() {
+ |
+
+ Here we create an array |
+
+ var a [5]int
+ fmt.Println("emp:", a)
+ |
+
+ We can set a value at an index using the
+ |
+
+ a[4] = 100
+ fmt.Println("set:", a)
+ fmt.Println("get:", a[4])
+ |
+
+ The builtin |
+
+ fmt.Println("len:", len(a))
+ |
+
+ Use this syntax to declare and initalize an array +in one line. + + |
+
+ b := [5]int{1, 2, 3, 4, 5}
+ fmt.Println("dcl:", b)
+ |
+
+ Array types are one-dimensional, but you can +compose types to build multi-dimensional data +structures. + + |
+
+ var twoD [2][3]int
+ for i := 0; i < 2; i++ {
+ for j := 0; j < 3; j++ {
+ twoD[i][j] = i + j
+ }
+ }
+ fmt.Println("2d: ", twoD)
+}
+ |
+
+ Note that arrays appear in the form |
+
+ $ go run arrays.go
+emp: [0 0 0 0 0]
+set: [0 0 0 0 100]
+get: 100
+len: 5
+dcl: [1 2 3 4 5]
+2d: [[0 1 2] [1 2 3]]
+ |
+
+ You’ll see slices much more often than arrays in +typical Go. We’ll look at slices next. + + |
+ + + | +
+ Next example: Slices. +
+ + +
+ The primary mechanism for managing state in Go is
+communication over channels. We saw this for example
+with worker pools. There are a few other
+options for managing state though. Here we’ll
+look at using the |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+import "time"
+import "sync/atomic"
+ |
+
+ + | +
+ func main() {
+ |
+
+ We’ll use an unsigned integer to represent our +(always-positive) counter. + + |
+
+ var ops uint64 = 0
+ |
+
+ To simulate concurrent updates, we’ll start 50 +goroutines that each increment the counter about +once a millisecond. + + |
+
+ for i := 0; i < 50; i++ {
+ go func() {
+ for {
+ time.Sleep(time.Millisecond)
+ |
+
+ To atomically increment the counter we
+use |
+
+ atomic.AddUint64(&ops, 1)
+ }
+ }()
+ }
+ |
+
+ Wait a second to allow some ops to accumulate. + + |
+
+ time.Sleep(time.Second)
+ |
+
+ In order to safely use the counter while it’s still
+being updated by other goroutines, we extract a
+copy of the current value into |
+
+ opsFinal := atomic.LoadUint64(&ops)
+ fmt.Println("ops:", opsFinal)
+}
+ |
+
+ Next we’ll look at another approach to managing state: +mutexes. + + |
+ + + | +
+ Running the program shows that we executed about +40,000 operations. + + |
+
+ $ go run atomic-counters.go
+ops: 40200
+ |
+
+ Next we’ll look at mutexes, another tool for managing +state. + + |
+ + + | +
+ Next example: Mutexes. +
+ + +
+ Go provides built-in support for base64 +encoding/decoding. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ This syntax imports the |
+
+ import b64 "encoding/base64"
+import "fmt"
+ |
+
+ + | +
+ func main() {
+ |
+
+ Here’s the |
+
+ data := "abc123!?$*&()'-=@~"
+ |
+
+ Go supports both standard and URL-compatible
+base64. Here’s how to encode using the standard
+encoder. The encoder requires a |
+
+ sEnc := b64.StdEncoding.EncodeToString([]byte(data))
+ fmt.Println(sEnc)
+ |
+
+ Decoding may return an error, which you can check +if you don’t already know the input to be +well-formed. + + |
+
+ sDec, _ := b64.StdEncoding.DecodeString(sEnc)
+ fmt.Println(string(sDec))
+ fmt.Println()
+ |
+
+ This encodes/decodes using a URL-compatible base64 +format. + + |
+
+ uEnc := b64.URLEncoding.EncodeToString([]byte(data))
+ fmt.Println(uEnc)
+ uDec, _ := b64.URLEncoding.DecodeString(uEnc)
+ fmt.Println(string(uDec))
+}
+ |
+
+ The string encodes to slightly different values with the
+standard and URL base64 encoders (trailing |
+
+ $ go run base64-encoding.sh
+YWJjMTIzIT8kKiYoKSctPUB+
+abc123!?$*&()'-=@~
+ |
+
+ + | +
+ YWJjMTIzIT8kKiYoKSctPUB-
+abc123!?$*&()'-=@~
+ |
+
+ Next example: Reading Files. +
+ + +
+ By default channels are unbuffered, meaning that they
+will only accept sends ( |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ + | +
+ func main() {
+ |
+
+ Here we |
+
+ messages := make(chan string, 2)
+ |
+
+ Because this channel is buffered, we can send these +values into the channel without a corresponding +concurrent receive. + + |
+
+ messages <- "buffered"
+ messages <- "channel"
+ |
+
+ Later we can receive these two values as usual. + + |
+
+ fmt.Println(<-messages)
+ fmt.Println(<-messages)
+}
+ |
+
+ + | +
+ $ go run channel-buffering.go
+buffered
+channel
+ |
+
+ Next example: Channel Synchronization. +
+ + +
+ When using channels as function parameters, you can +specify if a channel is meant to only send or receive +values. This specificity increases the type-safety of +the program. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ This |
+
+ func ping(pings chan<- string, msg string) {
+ pings <- msg
+}
+ |
+
+ The |
+
+ func pong(pings <-chan string, pongs chan<- string) {
+ msg := <-pings
+ pongs <- msg
+}
+ |
+
+ + | +
+ func main() {
+ pings := make(chan string, 1)
+ pongs := make(chan string, 1)
+ ping(pings, "passed message")
+ pong(pings, pongs)
+ fmt.Println(<-pongs)
+}
+ |
+
+ + | +
+ $ go run channel-directions.go
+passed message
+ |
+
+ Next example: Select. +
+ + +
+ We can use channels to synchronize execution +across goroutines. Here’s an example of using a +blocking receive to wait for a goroutine to finish. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+import "time"
+ |
+
+ This is the function we’ll run in a goroutine. The
+ |
+
+ func worker(done chan bool) {
+ fmt.Print("working...")
+ time.Sleep(time.Second)
+ fmt.Println("done")
+ |
+
+ Send a value to notify that we’re done. + + |
+
+ done <- true
+}
+ |
+
+ + | +
+ func main() {
+ |
+
+ Start a worker goroutine, giving it the channel to +notify on. + + |
+
+ done := make(chan bool, 1)
+ go worker(done)
+ |
+
+ Block until we receive a notification from the +worker on the channel. + + |
+
+ <-done
+}
+ |
+
+ + | +
+ $ go run channel-synchronization.go
+working...done
+ |
+
+ If you removed the |
+ + + | +
+ Next example: Channel Directions. +
+ + +
+ Channels are the pipes that connect concurrent +goroutines. You can send values into channels from one +goroutine and receive those values into another +goroutine. Channels are a powerful primitive that +underly much of Go’s functionality. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ + | +
+ func main() {
+ |
+
+ Create a new channel with |
+
+ messages := make(chan string)
+ |
+
+ Send a value into a channel using the |
+
+ go func() { messages <- "ping" }()
+ |
+
+ The |
+
+ msg := <-messages
+ fmt.Println(msg)
+}
+ |
+
+ When we run the program the |
+
+ $ go run channels.go
+ping
+ |
+
+ By default sends and receives block until both the
+sender and receiver are ready. This property allowed
+us to wait at the end of our program for the |
+ + + | +
+ Next example: Channel Buffering. +
+ + +
+ Closing a channel indicates that no more values +will be sent on it. This can be useful to communicate +completion to the channel’s receivers. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ In this example we’ll use a |
+
+ func main() {
+ jobs := make(chan int, 5)
+ done := make(chan bool)
+ |
+
+ Here’s the worker goroutine. It repeatedly receives
+from |
+
+ go func() {
+ for {
+ j, more := <-jobs
+ if more {
+ fmt.Println("received job", j)
+ } else {
+ fmt.Println("received all jobs")
+ done <- true
+ return
+ }
+ }
+ }()
+ |
+
+ This sends 3 jobs to the worker over the |
+
+ for j := 1; j <= 3; j++ {
+ jobs <- j
+ fmt.Println("sent job", j)
+ }
+ close(jobs)
+ fmt.Println("sent all jobs")
+ |
+
+ We await the worker using the +synchronization approach +we saw earlier. + + |
+
+ <-done
+}
+ |
+
+ + | +
+ $ go run closing-channels.go
+sent job 1
+received job 1
+sent job 2
+received job 2
+sent job 3
+received job 3
+sent all jobs
+received all jobs
+ |
+
+ The idea of closed channels leads naturally to our next
+example: |
+ + + | +
+ Next example: Range over Channels. +
+ + +
+ Go supports anonymous functions, +which can form closures. +Anonymous functions are useful when you want to define +a function inline without having to name it. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ This function |
+
+ func intSeq() func() int {
+ i := 0
+ return func() int {
+ i += 1
+ return i
+ }
+}
+ |
+
+ + | +
+ func main() {
+ |
+
+ We call |
+
+ nextInt := intSeq()
+ |
+
+ See the effect of the closure by calling |
+
+ fmt.Println(nextInt())
+ fmt.Println(nextInt())
+ fmt.Println(nextInt())
+ |
+
+ To confirm that the state is unique to that +particular function, create and test a new one. + + |
+
+ newInts := intSeq()
+ fmt.Println(newInts())
+}
+ |
+
+ + | +
+ $ go run closures.go
+1
+2
+3
+1
+ |
+
+ The last feature of functions we’ll look at for now is +recursion. + + |
+ + + | +
+ Next example: Recursion. +
+ + +
+ We often need our programs to perform operations on +collections of data, like selecting all items that +satisfy a given predicate or mapping all items to a new +collection with a custom function. + + |
+ + + | +
+ In some languages it’s idiomatic to use generic +data structures and algorithms. Go does not support +generics; in Go it’s common to provide collection +functions if and when they are specifically needed for +your program and data types. + + |
+ + + | +
+ Here are some example collection functions for slices
+of |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "strings"
+import "fmt"
+ |
+
+ Returns the first index of the target string |
+
+ func Index(vs []string, t string) int {
+ for i, v := range vs {
+ if v == t {
+ return i
+ }
+ }
+ return -1
+}
+ |
+
+ Returns |
+
+ func Include(vs []string, t string) bool {
+ return Index(vs, t) >= 0
+}
+ |
+
+ Returns |
+
+ func Any(vs []string, f func(string) bool) bool {
+ for _, v := range vs {
+ if f(v) {
+ return true
+ }
+ }
+ return false
+}
+ |
+
+ Returns |
+
+ func All(vs []string, f func(string) bool) bool {
+ for _, v := range vs {
+ if !f(v) {
+ return false
+ }
+ }
+ return true
+}
+ |
+
+ Returns a new slice containing all strings in the
+slice that satisfy the predicate |
+
+ func Filter(vs []string, f func(string) bool) []string {
+ vsf := make([]string, 0)
+ for _, v := range vs {
+ if f(v) {
+ vsf = append(vsf, v)
+ }
+ }
+ return vsf
+}
+ |
+
+ Returns a new slice containing the results of applying
+the function |
+
+ func Map(vs []string, f func(string) string) []string {
+ vsm := make([]string, len(vs))
+ for i, v := range vs {
+ vsm[i] = f(v)
+ }
+ return vsm
+}
+ |
+
+ + | +
+ func main() {
+ |
+
+ Here we try out our various collection functions. + + |
+
+ var strs = []string{"peach", "apple", "pear", "plum"}
+ |
+
+ + | +
+ fmt.Println(Index(strs, "pear"))
+ |
+
+ + | +
+ fmt.Println(Include(strs, "grape"))
+ |
+
+ + | +
+ fmt.Println(Any(strs, func(v string) bool {
+ return strings.HasPrefix(v, "p")
+ }))
+ |
+
+ + | +
+ fmt.Println(All(strs, func(v string) bool {
+ return strings.HasPrefix(v, "p")
+ }))
+ |
+
+ + | +
+ fmt.Println(Filter(strs, func(v string) bool {
+ return strings.Contains(v, "e")
+ }))
+ |
+
+ The above examples all used anonymous functions, +but you can also use named functions of the correct +type. + + |
+
+ fmt.Println(Map(strs, strings.ToUpper))
+ |
+
+ + | +
+ }
+ |
+
+ + | +
+ $ go run collection-functions.go
+2
+false
+true
+false
+[peach apple pear]
+[PEACH APPLE PEAR PLUM]
+ |
+
+ Next example: String Functions. +
+ + +
+ Command-line arguments
+are a common way to parameterize execution of programs.
+For example, |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "os"
+import "fmt"
+ |
+
+ + | +
+ func main() {
+ |
+
+
|
+
+ argsWithProg := os.Args
+ argsWithoutProg := os.Args[1:]
+ |
+
+ You can get individual args with normal indexing. + + |
+
+ arg := os.Args[3]
+ |
+
+ + | +
+ fmt.Println(argsWithProg)
+ fmt.Println(argsWithoutProg)
+ fmt.Println(arg)
+}
+ |
+
+ To experiment with command-line arguments it’s best to
+build a binary with |
+
+ $ go build command-line-arguments.go
+$ ./command-line-arguments a b c d
+[./command-line-arguments a b c d]
+[a b c d]
+c
+ |
+
+ Next we’ll look at more advanced command-line processing +with flags. + + |
+ + + | +
+ Next example: Command-Line Flags. +
+ + +
+ Command-line flags
+are a common way to specify options for command-line
+programs. For example, in |
+ + + | +
+ + | +
+ package main
+ |
+
+ Go provides a |
+
+ import "flag"
+import "fmt"
+ |
+
+ + | +
+ func main() {
+ |
+
+ Basic flag declarations are available for string,
+integer, and boolean options. Here we declare a
+string flag |
+
+ wordPtr := flag.String("word", "foo", "a string")
+ |
+
+ This declares |
+
+ numbPtr := flag.Int("numb", 42, "an int")
+ boolPtr := flag.Bool("fork", false, "a bool")
+ |
+
+ It’s also possible to declare an option that uses an +existing var declared elsewhere in the program. +Note that we need to pass in a pointer to the flag +declaration function. + + |
+
+ var svar string
+ flag.StringVar(&svar, "svar", "bar", "a string var")
+ |
+
+ Once all flags are declared, call |
+
+ flag.Parse()
+ |
+
+ Here we’ll just dump out the parsed options and
+any trailing positional arguments. Note that we
+need to dereference the points with e.g. |
+
+ fmt.Println("word:", *wordPtr)
+ fmt.Println("numb:", *numbPtr)
+ fmt.Println("fork:", *boolPtr)
+ fmt.Println("svar:", svar)
+ fmt.Println("tail:", flag.Args())
+}
+ |
+
+ To experiment with the command-line flags program it’s +best to first compile it and then run the resulting +binary directly. + + |
+
+ $ go build command-line-flags.go
+ |
+
+ Try out the built program by first giving it values for +all flags. + + |
+
+ $ ./command-line-flags -word=opt -numb=7 -fork -svar=flag
+word: opt
+numb: 7
+fork: true
+svar: flag
+tail: []
+ |
+
+ Note that if you omit flags they automatically take +their default values. + + |
+
+ $ ./command-line-flags -word=opt
+word: opt
+numb: 42
+fork: false
+svar: bar
+tail: []
+ |
+
+ Trailing positional arguments can be provided after +any flags. + + |
+
+ $ ./command-line-flags -word=opt a1 a2 a3
+word: opt
+...
+tail: [a1 a2 a3]
+ |
+
+ Note that the |
+
+ $ ./command-line-flags -word=opt a1 a2 a3 -num=7
+word: opt
+numb: 42
+fork: false
+svar: bar
+trailing: [a1 a2 a3 -num=7]
+ |
+
+ Use |
+
+ $ ./command-line-flags -h
+Usage of ./command-line-flags:
+ -fork=false: a bool
+ -numb=42: an int
+ -svar="bar": a string var
+ -word="foo": a string
+ |
+
+ If you provide a flag that wasn’t specified to the
+ |
+
+ $ ./command-line-flags -wat
+flag provided but not defined: -wat
+Usage of ./command-line-flags:
+...
+ |
+
+ Next we’ll look at environment variables, another common +way to parameterize programs. + + |
+ + + | +
+ Next example: Environment Variables. +
+ + +
+ Go supports constants of character, string, boolean, +and numeric values. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+import "math"
+ |
+
+
|
+
+ const s string = "constant"
+ |
+
+ + | +
+ func main() {
+ fmt.Println(s)
+ |
+
+ A |
+
+ const n = 500000000
+ |
+
+ Constant expressions perform arithmetic with +arbitrary precision. + + |
+
+ const d = 3e20 / n
+ fmt.Println(d)
+ |
+
+ A numeric constant has no type until it’s given +one, such as by an explicit cast. + + |
+
+ fmt.Println(int64(d))
+ |
+
+ A number can be given a type by using it in a
+context that requires one, such as a variable
+assignment or function call. For example, here
+ |
+
+ fmt.Println(math.Sin(n))
+}
+ |
+
+ + | +
+ $ go run constant.go
+constant
+6e+11
+600000000000
+-0.28470407323754404
+ |
+
+ Next example: For. +
+ + +
+ Defer is used to ensure that a function call is
+performed later in a program’s execution, usually for
+purposes of cleanup. |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+import "os"
+ |
+
+ Suppose we wanted to create a file, write to it,
+and then close when we’re done. Here’s how we could
+do that with |
+
+ func main() {
+ |
+
+ Immediately after getting a file object with
+ |
+
+ f := createFile("/tmp/defer.txt")
+ defer closeFile(f)
+ writeFile(f)
+}
+ |
+
+ + | +
+ func createFile(p string) *os.File {
+ fmt.Println("creating")
+ f, err := os.Create(p)
+ if err != nil {
+ panic(err)
+ }
+ return f
+}
+ |
+
+ + | +
+ func writeFile(f *os.File) {
+ fmt.Println("writing")
+ fmt.Fprintln(f, "data")
+ |
+
+ + | +
+ }
+ |
+
+ + | +
+ func closeFile(f *os.File) {
+ fmt.Println("closing")
+ f.Close()
+}
+ |
+
+ Running the program confirms that the file is closed +after being written. + + |
+
+ $ go run defer.go
+creating
+writing
+closing
+ |
+
+ Next example: Collection Functions. +
+ + +
+ Environment variables +are a universal mechanism for conveying configuration +information to Unix programs. +Let’s look at how to set, get, and list environment variables. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "os"
+import "strings"
+import "fmt"
+ |
+
+ + | +
+ func main() {
+ |
+
+ To set a key/value pair, use |
+
+ os.Setenv("FOO", "1")
+ fmt.Println("FOO:", os.Getenv("FOO"))
+ fmt.Println("BAR:", os.Getenv("BAR"))
+ |
+
+ Use |
+
+ fmt.Println()
+ for _, e := range os.Environ() {
+ pair := strings.Split(e, "=")
+ fmt.Println(pair[0])
+ }
+}
+ |
+
+ Running the program shows that we pick up the value
+value for |
+
+ $ go run environment-variables.go
+FOO: 1
+BAR:
+ |
+
+ The list of keys in the environment will depend on your +particular machine. + + |
+
+ TERM_PROGRAM
+PATH
+SHELL
+...
+ |
+
+ If we set |
+
+ $ BAR=2 go run environment-variables.go
+FOO: 1
+BAR: 2
+...
+ |
+
+ Next example: Spawning Processes. +
+ + +
+ A common requirement in programs is getting the number +of seconds, milliseconds, or nanoseconds since the +Unix epoch. +Here’s how to do it in Go. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+import "time"
+ |
+
+ + | +
+ func main() {
+ |
+
+ Use |
+
+ now := time.Now()
+ secs := now.Unix()
+ nanos := now.UnixNano()
+ fmt.Println(now)
+ |
+
+ Note that there is no |
+
+ millis := nanos / 1000000
+ fmt.Println(secs)
+ fmt.Println(millis)
+ fmt.Println(nanos)
+ |
+
+ You can also convert integer seconds or nanoseconds
+since the epoch into the corresponding |
+
+ fmt.Println(time.Unix(secs, 0))
+ fmt.Println(time.Unix(0, nanos))
+}
+ |
+
+ + | +
+ $ go run epoch.go
+2012-10-31 16:13:58.292387 +0000 UTC
+1351700038
+1351700038292
+1351700038292387000
+2012-10-31 16:13:58 +0000 UTC
+2012-10-31 16:13:58.292387 +0000 UTC
+ |
+
+ Next we’ll look at another time-related task: time +parsing and formatting. + + |
+ + + | +
+ Next example: Time Formatting / Parsing. +
+ + +
+ In Go it’s idiomatic to communicate errors via an +explicit, separate return value. This contrasts with +the exceptions used in languages like Java and Ruby and +the overloaded single result / error value sometimes +used in C. Go’s approach makes it easy to see which +functions return errors and to handle them using the +same language constructs employed for any other, +non-error tasks. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "errors"
+import "fmt"
+ |
+
+ By convention, errors are the last return value and
+have type |
+
+ func f1(arg int) (int, error) {
+ if arg == 42 {
+ |
+
+
|
+
+ return -1, errors.New("can't work with 42")
+ |
+
+ + | +
+ }
+ |
+
+ A nil value in the error position indicates that +there was no error. + + |
+
+ return arg + 3, nil
+}
+ |
+
+ It’s possible to use custom types as |
+
+ type argError struct {
+ arg int
+ prob string
+}
+ |
+
+ + | +
+ func (e *argError) Error() string {
+ return fmt.Sprintf("%d - %s", e.arg, e.prob)
+}
+ |
+
+ + | +
+ func f2(arg int) (int, error) {
+ if arg == 42 {
+ |
+
+ In this case we use |
+
+ return -1, &argError{arg, "can't work with it"}
+ }
+ return arg + 3, nil
+}
+ |
+
+ + | +
+ func main() {
+ |
+
+ The two loops below test out each of our
+error-returning functions. Note that the use of an
+inline error check on the |
+
+ for _, i := range []int{7, 42} {
+ if r, e := f1(i); e != nil {
+ fmt.Println("f1 failed:", e)
+ } else {
+ fmt.Println("f1 worked:", r)
+ }
+ }
+ for _, i := range []int{7, 42} {
+ if r, e := f2(i); e != nil {
+ fmt.Println("f2 failed:", e)
+ } else {
+ fmt.Println("f2 worked:", r)
+ }
+ }
+ |
+
+ If you want to programmatically use the data in +a custom error, you’ll need to get the error as an +instance of the custom error type via type +assertion. + + |
+
+ _, e := f2(42)
+ if ae, ok := e.(*argError); ok {
+ fmt.Println(ae.arg)
+ fmt.Println(ae.prob)
+ }
+}
+ |
+
+ + | +
+ $ go run errors.go
+f1 worked: 10
+f1 failed: can't work with 42
+f2 worked: 10
+f2 failed: 42 - can't work with it
+42
+can't work with it
+ |
+
+ See this great post +on the Go blog for more on error handling. + + |
+ + + | +
+ Next example: Goroutines. +
+ + +
+ In the previous example we looked at
+spawning external processes. We
+do this when we need an external process accessible to
+a running Go process. Sometimes we just want to
+completely replace the current Go process with another
+(perhaps non-Go) one. To do this we’ll use Go’s
+implementation of the classic
+ |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "syscall"
+import "os"
+import "os/exec"
+ |
+
+ + | +
+ func main() {
+ |
+
+ For our example we’ll exec |
+
+ binary, lookErr := exec.LookPath("ls")
+ if lookErr != nil {
+ panic(lookErr)
+ }
+ |
+
+
|
+
+ args := []string{"-a", "-l", "-h"}
+ |
+
+
|
+
+ env := os.Environ()
+ |
+
+ Here’s the actual |
+
+ execErr := syscall.Exec(binary, args, env)
+ if execErr != nil {
+ panic(execErr)
+ }
+}
+ |
+
+ When we run our program it is replaced by |
+
+ $ go run execing-processes.go
+total 16
+drwxr-xr-x 4 mark 136B Oct 3 16:29 .
+drwxr-xr-x 91 mark 3.0K Oct 3 12:50 ..
+-rw-r--r-- 1 mark 1.3K Oct 3 16:28 execing-processes.go
+ |
+
+ Note that Go does not offer a classic Unix |
+ + + | +
+ Next example: Signals. +
+ + +
+ Use |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+import "os"
+ |
+
+ + | +
+ func main() {
+ |
+
+
|
+
+ defer fmt.Println("!")
+ |
+
+ Exit with status 3. + + |
+
+ os.Exit(3)
+}
+ |
+
+ Note that unlike e.g. C, Go does not use an integer
+return value from |
+ + + | +
+ If you run |
+
+ $ go run exit.go
+exit status 3
+ |
+
+ By building and executing a binary you can see +the status in the terminal. + + |
+
+ $ go build exit.go
+$ ./exit
+$ echo $?
+3
+ |
+
+ Note that the |
+ + + | +
+
|
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ + | +
+ func main() {
+ |
+
+ The most basic type, with a single condition. + + |
+
+ i := 1
+ for i <= 3 {
+ fmt.Println(i)
+ i = i + 1
+ }
+ |
+
+ A classic initial/condition/after |
+
+ for j := 7; j <= 9; j++ {
+ fmt.Println(j)
+ }
+ |
+
+
|
+
+ for {
+ fmt.Println("loop")
+ break
+ }
+}
+ |
+
+ + | +
+ $ go run for.go
+1
+2
+3
+7
+8
+9
+loop
+ |
+
+ We’ll see some other |
+ + + | +
+ Next example: If/Else. +
+ + +
+ Functions are central in Go. We’ll learn about +functions with a few different examples. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ Here’s a function that takes two |
+
+ func plus(a int, b int) int {
+ |
+
+ Go requires explicit returns, i.e. it won’t +automatically return the value of the last +expression. + + |
+
+ return a + b
+}
+ |
+
+ + | +
+ func main() {
+ |
+
+ Call a function just as you’d expect, with
+ |
+
+ res := plus(1, 2)
+ fmt.Println("1+2 =", res)
+}
+ |
+
+ + | +
+ $ go run functions.go
+1+2 = 3
+ |
+
+ There are several other features to Go functions. One is +multiple return values, which we’ll look at next. + + |
+ + + | +
+ Next example: Multiple Return Values. +
+ + +
+ A goroutine is a lightweight thread of execution. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ + | +
+ func f(from string) {
+ for i := 0; i < 3; i++ {
+ fmt.Println(from, ":", i)
+ }
+}
+ |
+
+ + | +
+ func main() {
+ |
+
+ Suppose we have a function call |
+
+ f("direct")
+ |
+
+ To invoke this function in a goroutine, use
+ |
+
+ go f("goroutine")
+ |
+
+ You can also start a goroutine for an anonymous +function call. + + |
+
+ go func(msg string) {
+ fmt.Println(msg)
+ }("going")
+ |
+
+ Our two goroutines are running asynchronously in
+separate goroutines now, so execution falls through
+to here. This |
+
+ var input string
+ fmt.Scanln(&input)
+ fmt.Println("done")
+}
+ |
+
+ When we run this program, we see the output of the +blocking call first, then the interleaved output of the +two gouroutines. This interleaving reflects the +goroutines being run concurrently by the Go runtime. + + |
+
+ $ go run goroutines.go
+direct : 0
+direct : 1
+direct : 2
+goroutine : 0
+going
+goroutine : 1
+goroutine : 2
+<enter>
+done
+ |
+
+ Next we’ll look at a complement to goroutines in +concurrent Go programs: channels. + + |
+ + + | +
+ Next example: Channels. +
+ + +
+ Our first program will print the classic “hello world” +message. Here’s the full source code. + + |
+
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ + | +
+ func main() {
+ fmt.Println("hello world")
+}
+ |
+
+ To run the program, put the code in |
+
+ $ go run hello-world.go
+hello world
+ |
+
+ Sometimes we’ll want to build our programs into
+binaries. We can do this using |
+
+ $ go build hello-world.go
+$ ls
+hello-world hello-world.go
+ |
+
+ We can then execute the built binary directly. + + |
+
+ $ ./hello-world
+hello world
+ |
+
+ Now that we can run and build basic Go programs, let’s +learn more about the language. + + |
+ + + | +
+ Next example: Values. +
+ + +
+ Branching with |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ + | +
+ func main() {
+ |
+
+ Here’s a basic example. + + |
+
+ if 7%2 == 0 {
+ fmt.Println("7 is even")
+ } else {
+ fmt.Println("7 is odd")
+ }
+ |
+
+ You can have an |
+
+ if 8%4 == 0 {
+ fmt.Println("8 is divisible by 4")
+ }
+ |
+
+ A statement can proceed conditionals; any variables +declared in this statement are available in all +branches. + + |
+
+ if num := 9; num < 0 {
+ fmt.Println(num, "is negative")
+ } else if num < 10 {
+ fmt.Println(num, "has 1 digit")
+ } else {
+ fmt.Println(num, "has multiple digits")
+ }
+}
+ |
+
+ Note that you don’t need parentheses around conditions +in Go, but that the brackets are required. + + |
+ + + | +
+ + | +
+ $ go run if-else.go
+7 is odd
+8 is divisible by 4
+9 has 1 digit
+ |
+
+ There is no ternary if
+in Go, so you’ll need to use a full |
+ + + | +
+ Next example: Switch. +
+ + ++ Go is an + open source programming language designed for + building simple, fast, and reliable software. +
+ ++ Go by Example is a hands-on introduction + to Go using annotated example programs. Check out + the first example or + browse the full list below. +
+ +
+ Interfaces are named collections of method +signatures. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+import "math"
+ |
+
+ Here’s a basic interface for geometric shapes. + + |
+
+ type geometry interface {
+ area() float64
+ perim() float64
+}
+ |
+
+ For our example we’ll implement this interface on
+ |
+
+ type square struct {
+ width, height float64
+}
+type circle struct {
+ radius float64
+}
+ |
+
+ To implement an interface in Go, we just need to
+implement all the methods in the interface. Here we
+implement |
+
+ func (s square) area() float64 {
+ return s.width * s.height
+}
+func (s square) perim() float64 {
+ return 2*s.width + 2*s.height
+}
+ |
+
+ The implementation for |
+
+ func (c circle) area() float64 {
+ return math.Pi * c.radius * c.radius
+}
+func (c circle) perim() float64 {
+ return 2 * math.Pi * c.radius
+}
+ |
+
+ If a variable has an interface type, then we can call
+methods that are in the named interface. Here’s a
+generic |
+
+ func measure(g geometry) {
+ fmt.Println(g)
+ fmt.Println(g.area())
+ fmt.Println(g.perim())
+}
+ |
+
+ + | +
+ func main() {
+ s := square{width: 3, height: 4}
+ c := circle{radius: 5}
+ |
+
+ The |
+
+ measure(s)
+ measure(c)
+}
+ |
+
+ + | +
+ $ go run interfaces.go
+{3 4}
+12
+14
+{5}
+78.53981633974483
+31.41592653589793
+ |
+
+ To learn more about Go’s interfaces, check out this +great blog post. + + |
+ + + | +
+ Next example: Errors. +
+ + +
+ Go offers built-in support for JSON encoding and +decoding, including to and from built-in and custom +data types. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "encoding/json"
+import "fmt"
+import "os"
+ |
+
+ We’ll use these two structs to demonstrate encoding and +decoding of custom types below. + + |
+
+ type Response1 struct {
+ Page int
+ Fruits []string
+}
+type Response2 struct {
+ Page int `json:"page"`
+ Fruits []string `json:"fruits"`
+}
+ |
+
+ + | +
+ func main() {
+ |
+
+ First we’ll look at encoding basic data types to +JSON strings. Here are some examples for atomic +values. + + |
+
+ bolB, _ := json.Marshal(true)
+ fmt.Println(string(bolB))
+ |
+
+ + | +
+ intB, _ := json.Marshal(1)
+ fmt.Println(string(intB))
+ |
+
+ + | +
+ fltB, _ := json.Marshal(2.34)
+ fmt.Println(string(fltB))
+ |
+
+ + | +
+ strB, _ := json.Marshal("gopher")
+ fmt.Println(string(strB))
+ |
+
+ And here are some for slices and maps, which encode +to JSON arrays and objects as you’d expect. + + |
+
+ slcD := []string{"apple", "peach", "pear"}
+ slcB, _ := json.Marshal(slcD)
+ fmt.Println(string(slcB))
+ |
+
+ + | +
+ mapD := map[string]int{"apple": 5, "lettuce": 7}
+ mapB, _ := json.Marshal(mapD)
+ fmt.Println(string(mapB))
+ |
+
+ The JSON package can automatically encode your +custom data types. It will only include exported +fields in the encoded output and will by default +use those names as the JSON keys. + + |
+
+ res1D := &Response1{
+ Page: 1,
+ Fruits: []string{"apple", "peach", "pear"}}
+ res1B, _ := json.Marshal(res1D)
+ fmt.Println(string(res1B))
+ |
+
+ You can use tags on struct field declarations
+to customize the encoded JSON key names. Check the
+definition of |
+
+ res2D := &Response2{
+ Page: 1,
+ Fruits: []string{"apple", "peach", "pear"}}
+ res2B, _ := json.Marshal(res2D)
+ fmt.Println(string(res2B))
+ |
+
+ Now let’s look at decoding JSON data into Go +values. Here’s an example for a generic data +structure. + + |
+
+ byt := []byte(`{"num":6.0,"strs":["a","b"]}`)
+ |
+
+ We need to provide a variable where the JSON
+package can put the decoded data. This
+ |
+
+ var dat map[string]interface{}
+ |
+
+ Here’s the actual decoding, and a check for +associated errors. + + |
+
+ if err := json.Unmarshal(byt, &dat); err != nil {
+ panic(err)
+ }
+ fmt.Println(dat)
+ |
+
+ In order to use the values in the decoded map,
+we’ll need to cast them to their appropriate type.
+For example here we cast the value in |
+
+ num := dat["num"].(float64)
+ fmt.Println(num)
+ |
+
+ Accessing nested data requires a series of +casts. + + |
+
+ strs := dat["strs"].([]interface{})
+ str1 := strs[0].(string)
+ fmt.Println(str1)
+ |
+
+ We can also decode JSON into custom data types. +This has the advantages of adding additional +type-safety to our programs and eliminating the +need for type assertions when accessing the decoded +data. + + |
+
+ str := `{"page": 1, "fruits": ["apple", "peach"]}`
+ res := &Response2{}
+ json.Unmarshal([]byte(str), &res)
+ fmt.Println(res)
+ fmt.Println(res.Fruits[0])
+ |
+
+ In the examples above we always used bytes and
+strings as intermediates between the data and
+JSON representation on standard out. We can also
+stream JSON encodings directly to |
+
+ enc := json.NewEncoder(os.Stdout)
+ d := map[string]int{"apple": 5, "lettuce": 7}
+ enc.Encode(d)
+}
+ |
+
+ + | +
+ $ go run json.go
+true
+1
+2.34
+"gopher"
+["apple","peach","pear"]
+{"apple":5,"lettuce":7}
+{"Page":1,"Fruits":["apple","peach","pear"]}
+{"page":1,"fruits":["apple","peach","pear"]}
+map[num:6 strs:[a b]]
+6
+a
+&{1 [apple peach]}
+apple
+{"apple":5,"lettuce":7}
+ |
+
+ We’ve covered the basic of JSON in Go here, but check +out the JSON and Go +blog post and JSON package docs +for more. + + |
+ + + | +
+ Next example: Time. +
+ + +
+ A line filter is a common type of program that reads
+input on stdin, processes it, and then prints some
+derived result to stdout. |
+ + + | +
+ Here’s an example line filter in Go that writes a +capitalized version of all input text. You can use this +pattern to write your own Go line filters. + + |
+
+ package main
+ |
+
+ + | +
+ import (
+ "bufio"
+ "fmt"
+ "io"
+ "os"
+ "strings"
+)
+ |
+
+ + | +
+ func main() {
+ |
+
+ Wrapping the unbuffered |
+
+ rdr := bufio.NewReader(os.Stdin)
+ out := os.Stdout
+ |
+
+
|
+
+ for {
+ switch line, err := rdr.ReadString('\n'); err {
+ |
+
+ If the read succeeded (the read |
+
+ case nil:
+ ucl := strings.ToUpper(line)
+ if _, err = out.WriteString(ucl); err != nil {
+ fmt.Fprintln(os.Stderr, "error:", err)
+ os.Exit(1)
+ }
+ |
+
+ The |
+
+ case io.EOF:
+ os.Exit(0)
+ |
+
+ Otherwise there’s a problem; print the +error and exit with non-zero status. + + |
+
+ default:
+ fmt.Fprintln(os.Stderr, "error:", err)
+ os.Exit(1)
+ }
+ }
+}
+ |
+
+ To try out our line filter, first make a file with a few +lowercase lines. + + |
+
+ $ echo 'hello' > /tmp/lines
+$ echo 'filter' >> /tmp/lines
+ |
+
+ Then use the line filter to get uppercase lines. + + |
+
+ $ cat /tmp/lines | go run line-filters.go
+HELLO
+FILTER
+ |
+
+ Next example: Command-Line Arguments. +
+ + +
+ Maps are Go’s built-in associative data type +(sometimes called hashes or dicts in other languages). + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ + | +
+ func main() {
+ |
+
+ To create an empty map, use the builtin |
+
+ m := make(map[string]int)
+ |
+
+ Set key/value pairs using typical |
+
+ m["k1"] = 7
+ m["k2"] = 13
+ |
+
+ Printing a map with e.g. |
+
+ fmt.Println("map:", m)
+ |
+
+ Get a value for a key with |
+
+ v1 := m["k1"]
+ fmt.Println("v1: ", v1)
+ |
+
+ The builtin |
+
+ fmt.Println("len:", len(m))
+ |
+
+ The builtin |
+
+ delete(m, "k2")
+ fmt.Println("map:", m)
+ |
+
+ The optional second return value when getting a
+value from a map indiciates if the key was present
+in the map. This can be used to disambiguate
+between missing keys and keys with zero values
+like |
+
+ _, prs := m["k2"]
+ fmt.Println("prs:", prs)
+ |
+
+ You can also declare and initialize a new map in +the same line with this syntax. + + |
+
+ n := map[string]int{"foo": 1, "bar": 2}
+ fmt.Println("map:", n)
+}
+ |
+
+ Note that maps appear in the form |
+
+ $ go run maps.go
+map: map[k1:7 k2:13]
+v1: 7
+len: 2
+map: map[k1:7]
+prs: false
+map: map[foo:1 bar:2]
+ |
+
+ Next example: Range. +
+ + +
+ Go supports methods defined on struct types. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ + | +
+ type rect struct {
+ width, height int
+}
+ |
+
+ This |
+
+ func (r *rect) area() int {
+ return r.width * r.height
+}
+ |
+
+ Methods can be defined for either pointer or value +receiver types. Here’s an example of a value receiver. + + |
+
+ func (r rect) perim() int {
+ return 2*r.width + 2*r.height
+}
+ |
+
+ + | +
+ func main() {
+ r := rect{width: 10, height: 5}
+ |
+
+ Here we call the 2 methods defined for our struct. + + |
+
+ fmt.Println("area: ", r.area())
+ fmt.Println("perim:", r.perim())
+ |
+
+ Go automatically handles conversion between values +and pointers for method calls. You may want to use +a pointer receiver type to avoid copying on method +calls or to allow the method to mutate the +receiving struct. + + |
+
+ rp := &r
+ fmt.Println("area: ", rp.area())
+ fmt.Println("perim:", rp.perim())
+}
+ |
+
+ + | +
+ $ go run methods.go
+area: 50
+perim: 30
+area: 50
+perim: 30
+ |
+
+ Next we’ll look at Go’s mechanism for grouping and +naming related sets of methods: interfaces. + + |
+ + + | +
+ Next example: Interfaces. +
+ + +
+ Go has built-in support for multiple return values. +This feature is used often in idiomatic Go, for example +to return both result and error values from a function. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ The |
+
+ func vals() (int, int) {
+ return 3, 7
+}
+ |
+
+ + | +
+ func main() {
+ |
+
+ Here we use the 2 different return values from the +call with multiple assignment. + + |
+
+ a, b := vals()
+ fmt.Println(a)
+ fmt.Println(b)
+ |
+
+ If you only want a subset of the returned values,
+use the blank identifier |
+
+ _, c := vals()
+ fmt.Println(c)
+}
+ |
+
+ + | +
+ $ go run multiple-return-values.go
+3
+7
+7
+ |
+
+ Accepting a variable number of arguments is another nice +feature of Go functions; we’ll look at this next. + + |
+ + + | +
+ Next example: Variadic Functions. +
+ + +
+ In the previous example we saw how to manage simple +counter state using atomic operations. For more complex +state we can use a mutex +to safely access data across multiple goroutines. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import (
+ "fmt"
+ "math/rand"
+ "runtime"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+ |
+
+ + | +
+ func main() {
+ |
+
+ For our example the |
+
+ var state = make(map[int]int)
+ |
+
+ This |
+
+ var mutex = &sync.Mutex{}
+ |
+
+ To compare the mutex-based approach with another
+we’ll see later, |
+
+ var ops int64 = 0
+ |
+
+ Here we start 100 goroutines to execute repeated +reads against the state. + + |
+
+ for r := 0; r < 100; r++ {
+ go func() {
+ total := 0
+ for {
+ |
+
+ For each read we pick a key to access,
+ |
+
+ key := rand.Intn(5)
+ mutex.Lock()
+ total += state[key]
+ mutex.Unlock()
+ atomic.AddInt64(&ops, 1)
+ |
+
+ In order to ensure that this goroutine
+doesn’t starve the scheduler, we explicitly
+yield after each operation with
+ |
+
+ runtime.Gosched()
+ }
+ }()
+ }
+ |
+
+ We’ll also start 10 goroutines to simulate writes, +using the same pattern we did for reads. + + |
+
+ for w := 0; w < 10; w++ {
+ go func() {
+ for {
+ key := rand.Intn(5)
+ val := rand.Intn(100)
+ mutex.Lock()
+ state[key] = val
+ mutex.Unlock()
+ atomic.AddInt64(&ops, 1)
+ runtime.Gosched()
+ }
+ }()
+ }
+ |
+
+ Let the 110 goroutines work on the |
+
+ time.Sleep(time.Second)
+ |
+
+ Take and report a final operations count. + + |
+
+ opsFinal := atomic.LoadInt64(&ops)
+ fmt.Println("ops:", opsFinal)
+ |
+
+ With a final lock of |
+
+ mutex.Lock()
+ fmt.Println("state:", state)
+ mutex.Unlock()
+}
+ |
+
+ Running the program shows that we executed about
+3,500,000 operations against our |
+
+ $ go run mutexes.go
+ops: 3598302
+state: map[1:38 4:98 2:23 3:85 0:44]
+ |
+
+ Next we’ll look at implementing this same state +management task using only goroutines and channels. + + |
+ + + | +
+ Next example: Stateful Goroutines. +
+ + +
+ Basic sends and receives on channels are blocking.
+However, we can use |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ + | +
+ func main() {
+ messages := make(chan string)
+ signals := make(chan bool)
+ |
+
+ Here’s a non-blocking receive. If a value is
+available on |
+
+ select {
+ case msg := <-messages:
+ fmt.Println("received message", msg)
+ default:
+ fmt.Println("no message received")
+ }
+ |
+
+ A non-blocking send works similarly. + + |
+
+ msg := "hi"
+ select {
+ case messages <- msg:
+ fmt.Println("sent message", msg)
+ default:
+ fmt.Println("no message sent")
+ }
+ |
+
+ We can use multiple |
+
+ select {
+ case msg := <-messages:
+ fmt.Println("received message", msg)
+ case sig := <-signals:
+ fmt.Println("received signal", sig)
+ default:
+ fmt.Println("no activity")
+ }
+}
+ |
+
+ + | +
+ $ go run non-blocking-channel-operations.go
+no message received
+no message sent
+no activity
+ |
+
+ Next example: Closing Channels. +
+ + +
+ Parsing numbers from strings is a basic but common task +in many programs; here’s how to do it in Go. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ The built-in package |
+
+ import "strconv"
+import "fmt"
+ |
+
+ + | +
+ func main() {
+ |
+
+ With |
+
+ f, _ := strconv.ParseFloat("1.234", 64)
+ fmt.Println(f)
+ |
+
+ For |
+
+ i, _ := strconv.ParseInt("123", 0, 64)
+ fmt.Println(i)
+ |
+
+
|
+
+ d, _ := strconv.ParseInt("0x1c8", 0, 64)
+ fmt.Println(d)
+ |
+
+ A |
+
+ u, _ := strconv.ParseUint("789", 0, 64)
+ fmt.Println(u)
+ |
+
+
|
+
+ k, _ := strconv.Atoi("135")
+ fmt.Println(k)
+ |
+
+ Parse functions return an error on bad input. + + |
+
+ _, e := strconv.Atoi("wat")
+ fmt.Println(e)
+}
+ |
+
+ + | +
+ $ go run number-parsing.go
+1.234
+123
+456
+789
+135
+strconv.ParseInt: parsing "wat": invalid syntax
+ |
+
+ Next we’ll look at another common parsing task: URLs. + + |
+ + + | +
+ Next example: URL Parsing. +
+ + +
+ A |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "os"
+ |
+
+ + | +
+ func main() {
+ |
+
+ We’ll use panic throughout this site to check for +unexpected errors. This is the only program on the +site designed to panic. + + |
+
+ panic("a problem")
+ |
+
+ A common use of panic is to abort if a function
+returns an error value that we don’t know how to
+(or want to) handle. Here’s an example of
+ |
+
+ _, err := os.Create("/tmp/file")
+ if err != nil {
+ panic(err)
+ }
+}
+ |
+
+ Running this program will cause it to panic, print +an error message and goroutine traces, and exit with +a non-zero status. + + |
+
+ $ go run panic.go
+panic: a problem
+ |
+
+ + | +
+ goroutine 1 [running]:
+main.main()
+ /.../panic.go:12 +0x47
+...
+exit status 2
+ |
+
+ Note that unlike some languages which use exceptions +for handling of many errors, in Go it is idiomatic +to use error-indicating return values wherever possible. + + |
+ + + | +
+ Next example: Defer. +
+ + +
+ Go supports pointers, +allowing you to pass references to values and records +within your program. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ We’ll show how pointers work in contrast to values with
+2 functions: |
+
+ func zeroval(ival int) {
+ ival = 0
+}
+ |
+
+
|
+
+ func zeroptr(iptr *int) {
+ *iptr = 0
+}
+ |
+
+ + | +
+ func main() {
+ i := 1
+ fmt.Println("initial:", i)
+ |
+
+ + | +
+ zeroval(i)
+ fmt.Println("zeroval:", i)
+ |
+
+ The |
+
+ zeroptr(&i)
+ fmt.Println("zeroptr:", i)
+ |
+
+ Pointers can be printed too. + + |
+
+ fmt.Println("pointer:", &i)
+}
+ |
+
+
|
+
+ $ go run pointers.go
+initial: 1
+zeroval: 1
+zeroptr: 0
+pointer: 0x42131100
+ |
+
+ Next example: Structs. +
+ + +
+ Go’s |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+import "math/rand"
+ |
+
+ + | +
+ func main() {
+ |
+
+ For example, |
+
+ fmt.Print(rand.Intn(100), ",")
+ fmt.Print(rand.Intn(100))
+ fmt.Println()
+ |
+
+
|
+
+ fmt.Println(rand.Float64())
+ |
+
+ This can be used to generate random floats in
+other ranges, for example |
+
+ fmt.Print((rand.Float64()*5)+5, ",")
+ fmt.Print((rand.Float64() * 5) + 5)
+ fmt.Println()
+ |
+
+ To make the pseudorandom generator deterministic, +give it a well-known seed. + + |
+
+ s1 := rand.NewSource(42)
+ r1 := rand.New(s1)
+ |
+
+ Call the resulting |
+
+ fmt.Print(r1.Intn(100), ",")
+ fmt.Print(r1.Intn(100))
+ fmt.Println()
+ |
+
+ If you seed a source with the same number, it +produces the same sequence of random numbers. + + |
+
+ s2 := rand.NewSource(42)
+ r2 := rand.New(s2)
+ fmt.Print(r2.Intn(100), ",")
+ fmt.Print(r2.Intn(100))
+ fmt.Println()
+}
+ |
+
+ + | +
+ $ go run random-numbers.go
+81,87
+0.6645600532184904
+7.123187485356329,8.434115364335547
+5,87
+5,87
+ |
+
+ See the |
+ + + | +
+ Next example: Number Parsing. +
+ + +
+ range iterates over of elements in a variety of
+data structures. Let’s see how to use |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ + | +
+ func main() {
+ |
+
+ Here we use |
+
+ nums := []int{2, 3, 4}
+ sum := 0
+ for _, num := range nums {
+ sum += num
+ }
+ fmt.Println("sum:", sum)
+ |
+
+
|
+
+ for i, num := range nums {
+ if num == 3 {
+ fmt.Println("index:", i)
+ }
+ }
+ |
+
+
|
+
+ kvs := map[string]string{"a": "apple", "b": "bannana"}
+ for k, v := range kvs {
+ fmt.Printf("%s -> %s\n", k, v)
+ }
+ |
+
+
|
+
+ for i, c := range "go" {
+ fmt.Println(i, c)
+ }
+}
+ |
+
+ + | +
+ $ go run range.go
+sum: 9
+index: 1
+a -> apple
+b -> bannana
+0 103
+1 111
+ |
+
+ Next example: Functions. +
+ + +
+ In a previous example we saw how |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ + | +
+ func main() {
+ |
+
+ We’ll iterate over 2 values in the |
+
+ queue := make(chan string, 2)
+ queue <- "one"
+ queue <- "two"
+ close(queue)
+ |
+
+ This |
+
+ for elem := range queue {
+ fmt.Println(elem)
+ }
+}
+ |
+
+ + | +
+ $ go run range-over-channels.go
+one
+two
+ |
+
+ This example also showed that it’s possible to close +a non-empty channel but still have the remaining +values be received. + + |
+ + + | +
+ Next example: Timers. +
+ + +
+ Rate limiting +is an important mechanism for controlling resource +utilization and maintaining quality of service. Go +elegantly supports rate limiting with goroutines, +channels, and tickers. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "time"
+import "fmt"
+ |
+
+ + | +
+ func main() {
+ |
+
+ First we’ll look at basic rate limiting. Suppose +we want to limit our handling of incoming requests. +We’ll serve these requests off a channel of the +same name. + + |
+
+ requests := make(chan int, 5)
+ for i := 1; i <= 5; i++ {
+ requests <- i
+ }
+ close(requests)
+ |
+
+ This |
+
+ limiter := time.Tick(time.Millisecond * 200)
+ |
+
+ By blocking on a receive from the |
+
+ for req := range requests {
+ <-limiter
+ fmt.Println("request", req, time.Now())
+ }
+ |
+
+ We may want to allow short bursts of requests in
+our rate limiting scheme while preserving the
+overall rate limit. We can accomplish this by
+buffering our limiter channel. This |
+
+ burstyLimiter := make(chan time.Time, 3)
+ |
+
+ Fill up the channel to represent allowed bursting. + + |
+
+ for i := 0; i < 3; i++ {
+ burstyLimiter <- time.Now()
+ }
+ |
+
+ Every 200 milliseconds we’ll try to add a new
+value to |
+
+ go func() {
+ for t := range time.Tick(time.Millisecond * 200) {
+ burstyLimiter <- t
+ }
+ }()
+ |
+
+ Now simulate 5 more incoming requests. The first
+3 of these will benefit from the burst capability
+of |
+
+ burstyRequests := make(chan int, 5)
+ for i := 1; i <= 5; i++ {
+ burstyRequests <- i
+ }
+ close(burstyRequests)
+ for req := range burstyRequests {
+ <-burstyLimiter
+ fmt.Println("request", req, time.Now())
+ }
+}
+ |
+
+ Running our program we see the first batch of requests +handled once every ~200 milliseconds as desired. + + |
+
+ $ go run rate-limiting.go
+request 1 2012-10-19 00:38:18.687438 +0000 UTC
+request 2 2012-10-19 00:38:18.887471 +0000 UTC
+request 3 2012-10-19 00:38:19.087238 +0000 UTC
+request 4 2012-10-19 00:38:19.287338 +0000 UTC
+request 5 2012-10-19 00:38:19.487331 +0000 UTC
+ |
+
+ For the second batch of requests we serve the first +3 immediately because of the burstable rate limiting, +then serve the remaining 2 with ~200ms delays each. + + |
+
+ request 1 2012-10-19 00:38:20.487578 +0000 UTC
+request 2 2012-10-19 00:38:20.487645 +0000 UTC
+request 3 2012-10-19 00:38:20.487676 +0000 UTC
+request 4 2012-10-19 00:38:20.687483 +0000 UTC
+request 5 2012-10-19 00:38:20.887542 +0000 UTC
+ |
+
+ Next example: Atomic Counters. +
+ + +
+ Reading and writing files are basic tasks needed for +many Go programs. First we’ll look at some examples of +reading files. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import (
+ "bufio"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+)
+ |
+
+ Reading files requires checking most calls for errors. +This helper will streamline our error checks below. + + |
+
+ func check(e error) {
+ if e != nil {
+ panic(e)
+ }
+}
+ |
+
+ + | +
+ func main() {
+ |
+
+ Perhaps the most basic file reading task is +slurping a file’s entire contents into memory. + + |
+
+ dat, err := ioutil.ReadFile("/tmp/dat")
+ check(err)
+ fmt.Print(string(dat))
+ |
+
+ You’ll often want more control over how and what
+parts of a file are read. For these tasks, start
+by |
+
+ f, err := os.Open("/tmp/dat")
+ |
+
+ Read some bytes from the beginning of the file. +Allow up to 5 to be read but also not how many +actually were read. + + |
+
+ b1 := make([]byte, 5)
+ n1, err := f.Read(b1)
+ check(err)
+ fmt.Printf("%d bytes: %s\n", n1, string(b1))
+ |
+
+ You can also |
+
+ o2, err := f.Seek(6, 0)
+ check(err)
+ b2 := make([]byte, 2)
+ n2, err := f.Read(b2)
+ check(err)
+ fmt.Printf("%d bytes @ %d: %s\n", n2, o2, string(b2))
+ |
+
+ The |
+
+ o3, err := f.Seek(6, 0)
+ check(err)
+ b3 := make([]byte, 2)
+ n3, err := io.ReadAtLeast(f, b3, 2)
+ check(err)
+ fmt.Printf("%d bytes @ %d: %s\n", n3, o3, string(b3))
+ |
+
+ There is no built-in rewind, but |
+
+ _, err = f.Seek(0, 0)
+ check(err)
+ |
+
+ The |
+
+ r4 := bufio.NewReader(f)
+ b4, err := r4.Peek(5)
+ check(err)
+ fmt.Printf("5 bytes: %s\n", string(b4))
+ |
+
+ Close the file when you’re done (usually this would
+be scheduled immediately after |
+
+ f.Close()
+ |
+
+ + | +
+ }
+ |
+
+ + | +
+ $ echo "hello" > /tmp/dat
+$ echo "go" >> /tmp/dat
+$ go run reading-files.go
+hello
+go
+5 bytes: hello
+2 bytes @ 6: go
+2 bytes @ 6: go
+5 bytes: hello
+ |
+
+ Next we’ll look at writing files. + + |
+ + + | +
+ Next example: Writing Files. +
+ + +
+ Go supports +recursive functions. +Here’s a classic factorial example. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ This |
+
+ func fact(n int) int {
+ if n == 0 {
+ return 1
+ }
+ return n * fact(n-1)
+}
+ |
+
+ + | +
+ func main() {
+ fmt.Println(fact(7))
+}
+ |
+
+ + | +
+ $ go run recursion.go
+5040
+ |
+
+ Next example: Pointers. +
+ + +
+ Go offers built-in support for regular expressions. +Here are some examples of common regexp-related tasks +in Go. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "bytes"
+import "fmt"
+import "regexp"
+ |
+
+ + | +
+ func main() {
+ |
+
+ This tests whether a pattern matches a string. + + |
+
+ match, _ := regexp.MatchString("p([a-z]+)ch", "peach")
+ fmt.Println(match)
+ |
+
+ Above we used a string pattern directly, but for
+other regexp tasks you’ll need to |
+
+ r, _ := regexp.Compile("p([a-z]+)ch")
+ |
+
+ Many methods are available on these structs. Here’s +a match test like we saw earlier. + + |
+
+ fmt.Println(r.MatchString("peach"))
+ |
+
+ This finds the match for the regexp. + + |
+
+ fmt.Println(r.FindString("peach punch"))
+ |
+
+ The also finds the first match but returns the +start and end indexes for the match instead of the +matching text. + + |
+
+ fmt.Println(r.FindStringIndex("peach punch"))
+ |
+
+ The |
+
+ fmt.Println(r.FindStringSubmatch("peach punch"))
+ |
+
+ Similarly this will return information about the +indexes of matches and submatches. + + |
+
+ fmt.Println(r.FindStringSubmatchIndex("peach punch"))
+ |
+
+ The |
+
+ fmt.Println(r.FindAllString("peach punch pinch", -1))
+ |
+
+ These |
+
+ fmt.Println(r.FindAllStringSubmatchIndex(
+ "peach punch pinch", -1))
+ |
+
+ Providing a non-negative integer as the second +argument to these functions will limit the number +of matches. + + |
+
+ fmt.Println(r.FindAllString("peach punch pinch", 2))
+ |
+
+ Our examples above had string arguments and used
+names like |
+
+ fmt.Println(r.Match([]byte("peach")))
+ |
+
+ When creating constants with regular expressions
+you can use the |
+
+ r = regexp.MustCompile("p([a-z]+)ch")
+ fmt.Println(r)
+ |
+
+ The |
+
+ fmt.Println(r.ReplaceAllString("a peach", "<fruit>"))
+ |
+
+ The |
+
+ in := []byte("a peach")
+ out := r.ReplaceAllFunc(in, bytes.ToUpper)
+ fmt.Println(string(out))
+}
+ |
+
+ + | +
+ $ go run regular-expressions.go
+true
+true
+peach
+[0 5]
+[peach ea]
+[0 5 1 3]
+[peach punch pinch]
+[[0 5 1 3] [6 11 7 9] [12 17 13 15]]
+[peach punch]
+true
+p([a-z]+)ch
+a <fruit>
+a PEACH
+ |
+
+ For a complete reference on Go regular expressions check
+the |
+ + + | +
+ Next example: JSON. +
+ + +
+ Go’s select lets you wait on multiple channel +operations. Combining goroutines and channels with +select is powerful feature of Go. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "time"
+import "fmt"
+ |
+
+ + | +
+ func main() {
+ |
+
+ For our example we’ll select across two channels. + + |
+
+ c1 := make(chan string)
+ c2 := make(chan string)
+ |
+
+ Each channel will receive a value after some amount +of time, to simulate e.g. blocking RPC operations +executing in concurrent goroutines. + + |
+
+ go func() {
+ time.Sleep(time.Second * 1)
+ c1 <- "one"
+ }()
+ go func() {
+ time.Sleep(time.Second * 2)
+ c2 <- "two"
+ }()
+ |
+
+ We’ll use |
+
+ for i := 0; i < 2; i++ {
+ select {
+ case msg1 := <-c1:
+ fmt.Println("received", msg1)
+ case msg2 := <-c2:
+ fmt.Println("received", msg2)
+ }
+ }
+}
+ |
+
+ We receive the values |
+
+ $ time go run select.go
+received one
+received two
+ |
+
+ Note that the total execution time is only ~2 seconds
+since both the 1 and 2 second |
+
+ real 0m2.245s
+ |
+
+ Next example: Timeouts. +
+ + +
+ SHA1 hashes are +frequently used to compute short identities for binary +or text blobs. For example, the git revision control +system uses SHA1s extensively to +identify versioned files and directories. Here’s how to +compute SHA1 hashes in Go. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ Go implements several hash functions in various
+ |
+
+ import "crypto/sha1"
+import "fmt"
+ |
+
+ + | +
+ func main() {
+ s := "sha1 this string"
+ |
+
+ The pattern for generating a hash is |
+
+ h := sha1.New()
+ |
+
+
|
+
+ h.Write([]byte(s))
+ |
+
+ This gets the finalized hash result as a byte
+slice. The argument to |
+
+ bs := h.Sum(nil)
+ |
+
+ SHA1 values are often printed in hex, for example
+in git commits. Use the |
+
+ fmt.Println(s)
+ fmt.Printf("%x\n", bs)
+}
+ |
+
+ Running the program computes the hash and prints it in +a human-readable hex format. + + |
+
+ $ go run sha1-hashes.go
+sha1 this string
+cf23df2207d99a74fbe169e3eba035e633b65d94
+ |
+
+ You can compute other hashes using a similar pattern to
+the one shown above. For example, to compute MD5 hashes
+import |
+ + + | +
+ Note that if you need cryptographically secure hashes, +you should carefully research +hash strength! + + |
+ + + | +
+ Next example: Base64 Encoding. +
+ + +
+ Sometines we’d like our Go programs to intelligently
+handle Unix signals.
+For example, we might want a server to gracefully
+shutdown when it receives a |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+import "os"
+import "os/signal"
+import "syscall"
+ |
+
+ + | +
+ func main() {
+ |
+
+ Go signal notification works by sending |
+
+ sigs := make(chan os.Signal, 1)
+ done := make(chan bool, 1)
+ |
+
+
|
+
+ signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
+ |
+
+ This goroutine executes a blocking receive for +signals. When it gets one it’ll print it out +and then notify the program that it can finish. + + |
+
+ go func() {
+ sig := <-sigs
+ fmt.Println()
+ fmt.Println(sig)
+ done <- true
+ }()
+ |
+
+ The program will wait here until it gets the
+expected signal (as indicated by the goroutine
+above sending a value on |
+
+ fmt.Println("awaiting signal")
+ <-done
+ fmt.Println("exiting")
+}
+ |
+
+ When we run this program it will block waiting for a
+signal. By typing |
+
+ $ go run signals.go
+awaiting signal
+^C
+interrupt
+exiting
+ |
+
+ Next example: Exit. +
+ + +
+ Slices are a key data type in Go, giving a more +powerful interface to sequences than arrays. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ + | +
+ func main() {
+ |
+
+ Unlike arrays, slices are typed only by the
+elements they contain (not the number of elements).
+To create an empty slice with non-zero length, use
+the builtin |
+
+ s := make([]string, 3)
+ fmt.Println("emp:", s)
+ |
+
+ We can set and get just like with arrays. + + |
+
+ s[0] = "a"
+ s[1] = "b"
+ s[2] = "c"
+ fmt.Println("set:", s)
+ fmt.Println("get:", s[2])
+ |
+
+
|
+
+ fmt.Println("len:", len(s))
+ |
+
+ In addition to these basic operations, slices
+support several more that make them richer than
+arrays. One is the builtin |
+
+ s = append(s, "d")
+ s = append(s, "e", "f")
+ fmt.Println("apd:", s)
+ |
+
+ Slices can also be |
+
+ c := make([]string, len(s))
+ copy(c, s)
+ fmt.Println("cpy:", c)
+ |
+
+ Slices support a “slice” operator with the syntax
+ |
+
+ l := s[2:5]
+ fmt.Println("sl1:", l)
+ |
+
+ This slices up to (but excluding) |
+
+ l = s[:5]
+ fmt.Println("sl2:", l)
+ |
+
+ And this slices up from (and including) |
+
+ l = s[2:]
+ fmt.Println("sl3:", l)
+ |
+
+ We can declare and initialize a variable for slice +in a single line as well. + + |
+
+ t := []string{"g", "h", "i"}
+ fmt.Println("dcl:", t)
+ |
+
+ Slices can be composed into multi-dimensional data +structures. The length of the inner slices can +vary, unlike with multi-dimensional arrays. + + |
+
+ twoD := make([][]int, 3)
+ for i := 0; i < 3; i++ {
+ innerLen := i + 1
+ twoD[i] = make([]int, innerLen)
+ for j := 0; j < innerLen; j++ {
+ twoD[i][j] = i + j
+ }
+ }
+ fmt.Println("2d: ", twoD)
+}
+ |
+
+ Note that while slices are different types than arrays,
+they are rendered similarly by |
+
+ $ go run slices.go
+emp: [ ]
+set: [a b c]
+get: c
+len: 3
+apd: [a b c d e f]
+cpy: [a b c d e f]
+sl1: [c d e]
+sl2: [a b c d e]
+sl3: [c d e f]
+dcl: [g h i]
+2d: [[0] [1 2] [2 3 4]]
+ |
+
+ Check out this great blog post +by the Go team for more details on the design and +implementation of slices in Go. + + |
+ + + | +
+ Now that we’ve seen arrays and slices we’ll look at +Go’s other key builtin data structure: maps. + + |
+ + + | +
+ Next example: Maps. +
+ + +
+ Go’s |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+import "sort"
+ |
+
+ + | +
+ func main() {
+ |
+
+ Sort methods are specific to the builtin type; +here’s an example for strings. Note that sorting is +in-place, so it changes the given slice and doesn’t +return a new one. + + |
+
+ strs := []string{"c", "a", "b"}
+ sort.Strings(strs)
+ fmt.Println("Strings:", strs)
+ |
+
+ An example of sorting |
+
+ ints := []int{7, 2, 4}
+ sort.Ints(ints)
+ fmt.Println("Ints: ", ints)
+ |
+
+ We can also use |
+
+ s := sort.IntsAreSorted(ints)
+ fmt.Println("Sorted: ", s)
+}
+ |
+
+ Running our program prints the sorted string and int
+slices and |
+
+ $ go run sorting.go
+Strings: [a b c]
+Ints: [2 4 7]
+Sorted: true
+ |
+
+ Next example: Sorting by Functions. +
+ + +
+ Sometimes we’ll want to sort a collection by something +other than its natural order. For example, suppose we +wanted to sort strings by their length instead of +alphabetically. Here’s an example of custom sorts sorts +in Go. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "sort"
+import "fmt"
+ |
+
+ In order to sort by a custom function in Go, we need a
+corresponding type. Here we’ve created a |
+
+ type ByLength []string
+ |
+
+ We implement |
+
+ func (s ByLength) Len() int {
+ return len(s)
+}
+func (s ByLength) Swap(i, j int) {
+ s[i], s[j] = s[j], s[i]
+}
+func (s ByLength) Less(i, j int) bool {
+ return len(s[i]) < len(s[j])
+}
+ |
+
+ With all of this in place, we can now implement our
+custom sort by casting the original |
+
+ func main() {
+ fruits := []string{"peach", "banana", "kiwi"}
+ sort.Sort(ByLength(fruits))
+ fmt.Println(fruits)
+}
+ |
+
+ Running our program shows a list sorted by string +length, as desired. + + |
+
+ $ go run sorting-by-functions.go
+[kiwi peach banana]
+ |
+
+ By following this same pattern of creating a custom
+type, implementing the three |
+ + + | +
+ Next example: Panic. +
+ + +
+ Sometimes our Go programs need to spawn other, non-Go
+processes. For example, the syntax highlighting on this
+site is implemented
+by spawning a |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+import "io/ioutil"
+import "os/exec"
+ |
+
+ + | +
+ func main() {
+ |
+
+ We’ll start with a simple command that takes no
+arguments or input and just prints something to
+stdout. The |
+
+ dateCmd := exec.Command("date")
+ |
+
+
|
+
+ dateOut, err := dateCmd.Output()
+ if err != nil {
+ panic(err)
+ }
+ fmt.Println("> date")
+ fmt.Println(string(dateOut))
+ |
+
+ Next we’ll look at a slightly more involved case
+where we pipe data to the external process on its
+ |
+
+ grepCmd := exec.Command("grep", "hello")
+ |
+
+ Here we explicitly grab input/output pipes, start +the process, write some input to it, read the +resulting output, and finally wait for the process +to exit. + + |
+
+ grepIn, _ := grepCmd.StdinPipe()
+ grepOut, _ := grepCmd.StdoutPipe()
+ grepCmd.Start()
+ grepIn.Write([]byte("hello grep\ngoodbye grep"))
+ grepIn.Close()
+ grepBytes, _ := ioutil.ReadAll(grepOut)
+ grepCmd.Wait()
+ |
+
+ We ommited error checks in the above example, but
+you could use the usual |
+
+ fmt.Println("> grep hello")
+ fmt.Println(string(grepBytes))
+ |
+
+ Note that when spawning commands we need to
+provide an explicitly delineated command and
+argument array, vs. being able to just pass in one
+command-line string. If you want to spawn a full
+command with a string, you can use |
+
+ lsCmd := exec.Command("bash", "-c", "ls -a -l -h")
+ lsOut, err := lsCmd.Output()
+ if err != nil {
+ panic(err)
+ }
+ fmt.Println("> ls -a -l -h")
+ fmt.Println(string(lsOut))
+}
+ |
+
+ The spawned programs return output that is the same +as if we had run them directly from the command-line. + + |
+
+ $ go run spawning-processes.go
+> date
+Wed Oct 10 09:53:11 PDT 2012
+ |
+
+ + | +
+ > grep hello
+hello grep
+ |
+
+ + | +
+ > ls -a -l -h
+drwxr-xr-x 4 mark 136B Oct 3 16:29 .
+drwxr-xr-x 91 mark 3.0K Oct 3 12:50 ..
+-rw-r--r-- 1 mark 1.3K Oct 3 16:28 spawning-processes.go
+ |
+
+ Next example: Exec'ing Processes. +
+ + +
+ In the previous example we used explicit locking with +mutexes to synchronize access to shared state across +multiple goroutines. Another option is to use the +built-in synchronization features of goroutines and +channels to achieve the same result. This channel-based +approach aligns with Go’s ideas of sharing memory by +communicating and having each piece of data owned +by exactly 1 goroutine. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import (
+ "fmt"
+ "math/rand"
+ "sync/atomic"
+ "time"
+)
+ |
+
+ In this example our state will be owned by a single
+goroutine. This will guarantee that the data is never
+corrupted with concurrent access. In order to read or
+write that state, other goroutines will send messages
+to the owning goroutine and receive corresponding
+replies. These |
+
+ type readOp struct {
+ key int
+ resp chan int
+}
+type writeOp struct {
+ key int
+ val int
+ resp chan bool
+}
+ |
+
+ + | +
+ func main() {
+ |
+
+ The |
+
+ var state = make(map[int]int)
+ |
+
+ Also as before we’ll count how many operations we +perform. + + |
+
+ var ops int64 = 0
+ |
+
+ The |
+
+ reads := make(chan *readOp)
+ writes := make(chan *writeOp)
+ |
+
+ Here is the goroutine that owns the |
+
+ go func() {
+ for {
+ select {
+ case read := <-reads:
+ read.resp <- state[read.key]
+ case write := <-writes:
+ state[write.key] = write.val
+ write.resp <- true
+ }
+ }
+ }()
+ |
+
+ This starts 100 goroutines to issue reads to the
+state-owning goroutine via the |
+
+ for r := 0; r < 100; r++ {
+ go func() {
+ for {
+ read := &readOp{
+ key: rand.Intn(5),
+ resp: make(chan int)}
+ reads <- read
+ <-read.resp
+ atomic.AddInt64(&ops, 1)
+ }
+ }()
+ }
+ |
+
+ We start 10 writes as well, using a similar +approach. + + |
+
+ for w := 0; w < 10; w++ {
+ go func() {
+ for {
+ write := &writeOp{
+ key: rand.Intn(5),
+ val: rand.Intn(100),
+ resp: make(chan bool)}
+ writes <- write
+ <-write.resp
+ atomic.AddInt64(&ops, 1)
+ }
+ }()
+ }
+ |
+
+ Let the goroutines work for a second. + + |
+
+ time.Sleep(time.Second)
+ |
+
+ Finally, capture and report the |
+
+ opsFinal := atomic.LoadInt64(&ops)
+ fmt.Println("ops:", opsFinal)
+}
+ |
+
+ Running our program shows that the goroutine-based +state management example achieves about 800,000 +operations per second. + + |
+
+ $ go run stateful-goroutines.go
+ops: 807434
+ |
+
+ For this particular case the goroutine-based approach +was a bit more involved than the mutex-based one. It +might be useful in certain cases though, for example +where you have other channels involved or when managing +multiple such mutexes would be error-prone. You should +use whichever approach feels most natural, especially +with respect to understanding the correctness of your +program. + + |
+ + + | +
+ Next example: Sorting. +
+ + +
+ Go offers excellent support for string formatting in
+the |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+import "os"
+ |
+
+ + | +
+ type point struct {
+ x, y int
+}
+ |
+
+ + | +
+ func main() {
+ |
+
+ Go offers several printing “verbs” designed to
+format general Go values. For example, this prints
+an instance of our |
+
+ p := point{1, 2}
+ fmt.Printf("%v\n", p)
+ |
+
+ If the value is a struct, the |
+
+ fmt.Printf("%+v\n", p)
+ |
+
+ The |
+
+ fmt.Printf("%#v\n", p)
+ |
+
+ To print the type of a value, use |
+
+ fmt.Printf("%T\n", p)
+ |
+
+ Formatting booleans is straight-forward. + + |
+
+ fmt.Printf("%t\n", true)
+ |
+
+ There are many options for formatting integers.
+Use |
+
+ fmt.Printf("%d\n", 123)
+ |
+
+ This prints a binary representation. + + |
+
+ fmt.Printf("%b\n", 14)
+ |
+
+ This prints the character corresponding to the +given integer. + + |
+
+ fmt.Printf("%c\n", 33)
+ |
+
+
|
+
+ fmt.Printf("%x\n", 456)
+ |
+
+ There are also several formatting options for
+floats. For basic decimal formatting use |
+
+ fmt.Printf("%f\n", 78.9)
+ |
+
+
|
+
+ fmt.Printf("%e\n", 123400000.0)
+ fmt.Printf("%E\n", 123400000.0)
+ |
+
+ For basic string printing use |
+
+ fmt.Printf("%s\n", "\"string\"")
+ |
+
+ To double-quote strings as in Go source, use |
+
+ fmt.Printf("%q\n", "\"string\"")
+ |
+
+ As with integers as seen earlier, |
+
+ fmt.Printf("%x\n", "hex this")
+ |
+
+ To print a representation of a pointer, use |
+
+ fmt.Printf("%p\n", &p)
+ |
+
+ When formatting numbers you will often want to
+control the width and precision of the resulting
+figure. To specify the width of an integer, use a
+number after the |
+
+ fmt.Printf("|%6d|%6d|\n", 12, 345)
+ |
+
+ You can also specify the width of printed floats, +though usually you’ll also want to restrict the +decimal precision at the same time with the +width.precision syntax. + + |
+
+ fmt.Printf("|%6.2f|%6.2f|\n", 1.2, 3.45)
+ |
+
+ To left-justify, use the |
+
+ fmt.Printf("|%-6.2f|%-6.2f|\n", 1.2, 3.45)
+ |
+
+ You may also want to control width when formatting +strings, especially to ensure that they align in +table-like output. For basic right-justified width. + + |
+
+ fmt.Printf("|%6s|%6s|\n", "foo", "b")
+ |
+
+ To left-justify use the |
+
+ fmt.Printf("|%-6s|%-6s|\n", "foo", "b")
+ |
+
+ So far we’ve seen |
+
+ s := fmt.Sprintf("a %s", "string")
+ fmt.Println(s)
+ |
+
+ You can format+print to |
+
+ fmt.Fprintf(os.Stderr, "an %s\n", "error")
+}
+ |
+
+ + | +
+ $ go run string-formatting.go
+{1 2}
+{x:1 y:2}
+main.point{x:1, y:2}
+main.point
+true
+123
+1110
+!
+1c8
+78.900000
+1.234000e+08
+1.234000E+08
+"string"
+"\"string\""
+6865782074686973
+0x42135100
+| 12| 345|
+| 1.20| 3.45|
+|1.20 |3.45 |
+| foo| b|
+|foo |b |
+a string
+an error
+ |
+
+ Next example: Regular Expressions. +
+ + +
+ The standard library’s |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import s "strings"
+import "fmt"
+ |
+
+ We alias |
+
+ var p = fmt.Println
+ |
+
+ + | +
+ func main() {
+ |
+
+ Here’s a sample of the functions available in
+ |
+
+ p("Contains: ", s.Contains("test", "es"))
+ p("Count: ", s.Count("test", "t"))
+ p("HasPrefix: ", s.HasPrefix("test", "te"))
+ p("HasSuffix: ", s.HasSuffix("test", "st"))
+ p("Index: ", s.Index("test", "e"))
+ p("Join: ", s.Join([]string{"a", "b"}, "-"))
+ p("Repeat: ", s.Repeat("a", 5))
+ p("Replace: ", s.Replace("foo", "o", "0", -1))
+ p("Replace: ", s.Replace("foo", "o", "0", 1))
+ p("Split: ", s.Split("a-b-c-d-e", "-"))
+ p("toLower: ", s.ToLower("TEST"))
+ p("ToUpper: ", s.ToUpper("test"))
+ p()
+ |
+
+ You can find more functions in the |
+ + + | +
+ Not part of |
+
+ p("Len: ", len("hello"))
+ p("Char:", "hello"[1])
+}
+ |
+
+ + | +
+ $ go run string-functions.go
+Contains: true
+Count: 2
+HasPrefix: true
+HasSuffix: true
+Index: 1
+Join: a-b
+Repeat: aaaaa
+Replace: f00
+Replace: f0o
+Split: [a b c d e]
+toLower: test
+ToUpper: TEST
+ |
+
+ + | +
+ Len: 5
+Char: 101
+ |
+
+ Next example: String Formatting. +
+ + +
+ Go’s structs are typed collections of fields. +They’re useful for grouping data together to form +records. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ This |
+
+ type person struct {
+ name string
+ age int
+}
+ |
+
+ + | +
+ func main() {
+ |
+
+ This syntax creates a new struct. + + |
+
+ fmt.Println(person{"Bob", 20})
+ |
+
+ You can name the fields when initializing a struct. + + |
+
+ fmt.Println(person{name: "Alice", age: 30})
+ |
+
+ Omitted fields will be zero-valued. + + |
+
+ fmt.Println(person{name: "Fred"})
+ |
+
+ An |
+
+ fmt.Println(&person{name: "Ann", age: 40})
+ |
+
+ Access struct fields with a dot. + + |
+
+ s := person{name: "Sean", age: 50}
+ fmt.Println(s.name)
+ |
+
+ You can also use dots with struct pointers - the +pointers are automatically dereferenced. + + |
+
+ sp := &s
+ fmt.Println(sp.age)
+ |
+
+ Structs are mutable. + + |
+
+ sp.age = 51
+ fmt.Println(sp.age)
+}
+ |
+
+ + | +
+ $ go run structs.go
+{Bob 20}
+{Alice 30}
+{Fred 0}
+&{Ann 40}
+Sean
+50
+51
+ |
+
+ Next example: Methods. +
+ + +
+ Switch statements express conditionals across many +branches. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+import "time"
+ |
+
+ + | +
+ func main() {
+ |
+
+ Here’s a basic |
+
+ i := 2
+ fmt.Print("write ", i, " as ")
+ switch i {
+ case 1:
+ fmt.Println("one")
+ case 2:
+ fmt.Println("two")
+ case 3:
+ fmt.Println("three")
+ }
+ |
+
+ You can use commas to separate multiple expressions
+in the same |
+
+ switch time.Now().Weekday() {
+ case time.Saturday, time.Sunday:
+ fmt.Println("it's the weekend")
+ default:
+ fmt.Println("it's a weekday")
+ }
+ |
+
+
|
+
+ t := time.Now()
+ switch {
+ case t.Hour() < 12:
+ fmt.Println("it's before noon")
+ default:
+ fmt.Println("it's after noon")
+ }
+}
+ |
+
+ + | +
+ $ go run switch.go
+write 2 as two
+it's the weekend
+it's before noon
+ |
+
+ Next example: Arrays. +
+ + +
+ Timers are for when you want to do +something once in the future - tickers are for when +you want to do something repeatedly at regular +intervals. Here’s an example of a ticker that ticks +periodically until we stop it. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "time"
+import "fmt"
+ |
+
+ + | +
+ func main() {
+ |
+
+ Tickers use a similar mechanism to timers: a
+channel that is sent values. Here we’ll use the
+ |
+
+ ticker := time.NewTicker(time.Millisecond * 500)
+ go func() {
+ for t := range ticker.C {
+ fmt.Println("Tick at", t)
+ }
+ }()
+ |
+
+ Tickers can be stopped like timers. Once a ticker +is stopped it won’t receive any more values on its +channel. We’ll stop ours after 1500ms. + + |
+
+ time.Sleep(time.Millisecond * 1500)
+ ticker.Stop()
+ fmt.Println("Ticker stopped")
+}
+ |
+
+ When we run this program the ticker should tick 3 times +befor we stop it. + + |
+
+ $ go run tickers.go
+Tick at 2012-09-23 11:29:56.487625 -0700 PDT
+Tick at 2012-09-23 11:29:56.988063 -0700 PDT
+Tick at 2012-09-23 11:29:57.488076 -0700 PDT
+Ticker stopped
+ |
+
+ Next example: Worker Pools. +
+ + +
+ Go’s offers extensive support for times and durations; +here are some examples. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+import "time"
+ |
+
+ + | +
+ func main() {
+ p := fmt.Println
+ |
+
+ We’ll start by getting the current time. + + |
+
+ now := time.Now()
+ p(now)
+ |
+
+ You can build a |
+
+ then := time.Date(
+ 2009, 11, 17, 20, 34, 58, 651387237, time.UTC)
+ p(then)
+ |
+
+ You can extract the various components of the time +value as expected. + + |
+
+ p(then.Year())
+ p(then.Month())
+ p(then.Day())
+ p(then.Hour())
+ p(then.Minute())
+ p(then.Second())
+ p(then.Nanosecond())
+ p(then.Location())
+ |
+
+ The Monday-Sunday |
+
+ p(then.Weekday())
+ |
+
+ These methods compare two times, testing if the +first occurs before, after, or at the same time +as the second, respectively. + + |
+
+ p(then.Before(now))
+ p(then.After(now))
+ p(then.Equal(now))
+ |
+
+ The |
+
+ diff := now.Sub(then)
+ p(diff)
+ |
+
+ We can compute the length of the duration in +various units. + + |
+
+ p(diff.Hours())
+ p(diff.Minutes())
+ p(diff.Seconds())
+ p(diff.Nanoseconds())
+ |
+
+ You can use |
+
+ p(then.Add(diff))
+ p(then.Add(-diff))
+}
+ |
+
+ + | +
+ $ go run time.go
+2012-10-31 15:50:13.793654 +0000 UTC
+2009-11-17 20:34:58.651387237 +0000 UTC
+2009
+November
+17
+20
+34
+58
+651387237
+UTC
+Tuesday
+true
+false
+false
+25891h15m15.142266763s
+25891.25420618521
+1.5534752523711128e+06
+9.320851514226677e+07
+93208515142266763
+2012-10-31 15:50:13.793654 +0000 UTC
+2006-12-05 01:19:43.509120474 +0000 UTC
+ |
+
+ Next we’ll look at the related idea of time relative to +the Unix epoch. + + |
+ + + | +
+ Next example: Epoch. +
+ + +
+ Go supports time formatting and parsing via +pattern-based layouts. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+import "time"
+ |
+
+ + | +
+ func main() {
+ p := fmt.Println
+ |
+
+ Here’s a basic example of formatting a time +according to RFC3339. + + |
+
+ t := time.Now()
+ p(t.Format("2006-01-02T15:04:05Z07:00"))
+ |
+
+
|
+
+ p(t.Format("3:04PM"))
+ p(t.Format("Mon Jan _2 15:04:05 2006"))
+ p(t.Format("2006-01-02T15:04:05.999999-07:00"))
+ |
+
+ For purely numeric representations you can also +use standard string formatting with the extracted +components of the time value. + + |
+
+ fmt.Printf("%d-%02d-%02dT%02d:%02d:%02d-00:00\n",
+ t.Year(), t.Month(), t.Day(),
+ t.Hour(), t.Minute(), t.Second())
+ |
+
+ Time parsing uses the same example-based approach
+as |
+
+ withNanos := "2006-01-02T15:04:05.999999999-07:00"
+ t1, e := time.Parse(
+ withNanos,
+ "2012-11-01T22:08:41.117442+00:00")
+ p(t1)
+ kitchen := "3:04PM"
+ t2, e := time.Parse(kitchen, "8:41PM")
+ p(t2)
+ |
+
+
|
+
+ ansic := "Mon Jan _2 15:04:05 2006"
+ _, e = time.Parse(ansic, "8:41PM")
+ p(e)
+ |
+
+ There are several predefined formats that you can +use for both formatting and parsing. + + |
+
+ p(t.Format(time.Kitchen))
+}
+ |
+
+ + | +
+ $ go run time-formatting-parsing.go
+2012-11-02T09:35:03-07:00
+9:35AM
+Fri Nov 2 09:35:03 2012
+2012-11-02T09:35:03.982519-07:00
+2012-11-02T09:35:03-00:00
+0001-01-01 00:00:00 +0000 UTC
+0000-01-01 20:41:00 +0000 UTC
+parsing time "8:41PM" as "Mon Jan _2 15:04:05 2006": ...
+9:35AM
+ |
+
+ Next example: Random Numbers. +
+ + +
+ Timeouts are important for programs that connect to
+external resources or that otherwise need to bound
+execution time. Implementing timeouts in Go is easy and
+elegant thanks to channels and |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "time"
+import "fmt"
+ |
+
+ + | +
+ func main() {
+ |
+
+ For our example, suppose we’re executing an external
+call that returns its result on a channel |
+
+ c1 := make(chan string)
+ go func() {
+ time.Sleep(time.Second * 2)
+ c1 <- "result 1"
+ }()
+ |
+
+ Here’s the |
+
+ select {
+ case res := <-c1:
+ fmt.Println(res)
+ case <-time.After(time.Second * 1):
+ fmt.Println("timeout 1")
+ }
+ |
+
+ If we allow a longer timeout of 3s, then the receive
+from |
+
+ c2 := make(chan string)
+ go func() {
+ time.Sleep(time.Second * 2)
+ c2 <- "result 2"
+ }()
+ select {
+ case res := <-c2:
+ fmt.Println(res)
+ case <-time.After(time.Second * 3):
+ fmt.Println("timeout 2")
+ }
+}
+ |
+
+ Running this program shows the first operation timing +out and the second succeeding. + + |
+
+ $ go run timeouts.go
+timeout 1
+result 2
+ |
+
+ Using this |
+ + + | +
+ Next example: Non-Blocking Channel Operations. +
+ + +
+ We often want to execute Go code at some point in the +future, or repeatedly at some interval. Go’s built-in +timer and ticker features make both of these tasks +easy. We’ll look first at timers and then +at tickers. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "time"
+import "fmt"
+ |
+
+ + | +
+ func main() {
+ |
+
+ Timers represent a single event in the future. You +tell the timer how long you want to wait, and it +provides a channel that will be notified at that +time. This timer will wait 2 seconds. + + |
+
+ timer1 := time.NewTimer(time.Second * 2)
+ |
+
+ The |
+
+ <-timer1.C
+ fmt.Println("Timer 1 expired")
+ |
+
+ If you just wanted to wait, you could have used
+ |
+
+ timer2 := time.NewTimer(time.Second)
+ go func() {
+ <-timer2.C
+ fmt.Println("Timer 2 expired")
+ }()
+ stop2 := timer2.Stop()
+ if stop2 {
+ fmt.Println("Timer 2 stopped")
+ }
+}
+ |
+
+ The first timer will expire ~2s after we start the +program, but the second should be stopped before it has +a chance to expire. + + |
+
+ $ go run timers.go
+Timer 1 expired
+Timer 2 stopped
+ |
+
+ Next example: Tickers. +
+ + +
+ URLs provide a uniform way to locate resources. +Here’s how to parse URLs in Go. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+import "net/url"
+import "strings"
+ |
+
+ + | +
+ func main() {
+ |
+
+ We’ll parse this example URL, which includes a +scheme, authentication info, host, port, path, +query params, and query fragment. + + |
+
+ s := "postgres://user:pass@host.com:5432/path?k=v#f"
+ |
+
+ Parse the URL and ensure there are no errors. + + |
+
+ u, err := url.Parse(s)
+ if err != nil {
+ panic(err)
+ }
+ |
+
+ Accessing the scheme is straightforward. + + |
+
+ fmt.Println(u.Scheme)
+ |
+
+
|
+
+ fmt.Println(u.User)
+ fmt.Println(u.User.Username())
+ p, _ := u.User.Password()
+ fmt.Println(p)
+ |
+
+ The |
+
+ fmt.Println(u.Host)
+ h := strings.Split(u.Host, ":")
+ fmt.Println(h[0])
+ fmt.Println(h[1])
+ |
+
+ Here we extract the |
+
+ fmt.Println(u.Path)
+ fmt.Println(u.Fragment)
+ |
+
+ To get query params in a string of |
+
+ fmt.Println(u.RawQuery)
+ m, _ := url.ParseQuery(u.RawQuery)
+ fmt.Println(m)
+ fmt.Println(m["k"][0])
+}
+ |
+
+ Running our URL parsing program shows all the different +pieces that we extracted. + + |
+
+ $ go run url-parsing.go
+postgres
+user:pass
+user
+pass
+host.com:5432
+host.com
+5432
+/path
+f
+k=v
+map[k:[v]]
+v
+ |
+
+ Next example: SHA1 Hashes. +
+ + +
+ Go has various value types including strings, +integers, floats, booleans, etc. Here are a few +basic examples. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ + | +
+ func main() {
+ |
+
+ Strings, which can be added together with |
+
+ fmt.Println("go" + "lang")
+ |
+
+ Integers and floats. + + |
+
+ fmt.Println("1+1 =", 1+1)
+ fmt.Println("7.0/3.0 =", 7.0/3.0)
+ |
+
+ Booleans, with boolean operators as you’d expect. + + |
+
+ fmt.Println(true && false)
+ fmt.Println(true || false)
+ fmt.Println(!true)
+}
+ |
+
+ + | +
+ $ go run values.go
+golang
+1+1 = 2
+7.0/3.0 = 2.3333333333333335
+false
+true
+false
+ |
+
+ Next example: Variables. +
+ + +
+ In Go, variables are explicitly declared and used by +the compiler to e.g. check type-correctness of function +calls. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ + | +
+ func main() {
+ |
+
+
|
+
+ var a string = "initial"
+ fmt.Println(a)
+ |
+
+ You can declare multiple variables at once. + + |
+
+ var b, c int = 1, 2
+ fmt.Println(b, c)
+ |
+
+ Go will infer the type of initialized variables. + + |
+
+ var d = true
+ fmt.Println(d)
+ |
+
+ Variables declared without a corresponding
+initialization are zero-valued. For example, the
+zero value for an |
+
+ var e int
+ fmt.Println(e)
+ |
+
+ The |
+
+ f := "short"
+ fmt.Println(f)
+}
+ |
+
+ + | +
+ $ go run variables.go
+initial
+1 2
+true
+0
+short
+ |
+
+ Next example: Constants. +
+ + +
+ Variadic functions
+can be called with any number of trailing arguments.
+For example, |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+ |
+
+ Here’s a function that will take an arbitrary number
+of |
+
+ func sum(nums ...int) {
+ fmt.Print(nums, " ")
+ total := 0
+ for _, num := range nums {
+ total += num
+ }
+ fmt.Println(total)
+}
+ |
+
+ + | +
+ func main() {
+ |
+
+ Variadic functions can be called in the usual way +with individual arguments. + + |
+
+ sum(1, 2)
+ sum(1, 2, 3)
+ |
+
+ If you already have multiple args in a slice,
+apply them to a variadic function using
+ |
+
+ nums := []int{1, 2, 3, 4}
+ sum(nums...)
+}
+ |
+
+ + | +
+ $ go run variadic-functions.go
+[1 2] 3
+[1 2 3] 6
+[1 2 3 4] 10
+ |
+
+ Another key aspect of functions in Go is their ability +to form closures, which we’ll look at next. + + |
+ + + | +
+ Next example: Closures. +
+ + +
+ In this example we’ll look at how to implement +a worker pool using goroutines and channels. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import "fmt"
+import "time"
+ |
+
+ Here’s the worker, of which we’ll run several
+concurrent instances. These workers will receive
+work on the |
+
+ func worker(id int, jobs <-chan int, results chan<- int) {
+ for j := range jobs {
+ fmt.Println("worker", id, "processing job", j)
+ time.Sleep(time.Second)
+ results <- j * 2
+ }
+}
+ |
+
+ + | +
+ func main() {
+ |
+
+ In order to use our pool of workers we need to send +them work and collect their results. We make 2 +channels for this. + + |
+
+ jobs := make(chan int, 100)
+ results := make(chan int, 100)
+ |
+
+ This starts up 3 workers, initially blocked +because there are no jobs yet. + + |
+
+ for w := 1; w <= 3; w++ {
+ go worker(w, jobs, results)
+ }
+ |
+
+ Here we send 9 |
+
+ for j := 1; j <= 9; j++ {
+ jobs <- j
+ }
+ close(jobs)
+ |
+
+ Finally we collect all the results of the work. + + |
+
+ for a := 1; a <= 9; a++ {
+ <-results
+ }
+}
+ |
+
+ Our running program shows the 9 jobs being executed by +various workers. The program only takes about 3 seconds +despite doing about 9 seconds of total work because +there are 3 workers operating concurrently. + + |
+
+ $ time go run worker-pools.go
+worker 1 processing job 1
+worker 2 processing job 2
+worker 3 processing job 3
+worker 1 processing job 4
+worker 2 processing job 5
+worker 3 processing job 6
+worker 1 processing job 7
+worker 2 processing job 8
+worker 3 processing job 9
+ |
+
+ + | +
+ real 0m3.149s
+ |
+
+ Next example: Rate Limiting. +
+ + +
+ Writing files in Go follows similar patterns to the +ones we saw earlier for reading. + + |
+ + + | +
+ + | +
+ package main
+ |
+
+ + | +
+ import (
+ "bufio"
+ "fmt"
+ "io/ioutil"
+ "os"
+)
+ |
+
+ + | +
+ func check(e error) {
+ if e != nil {
+ panic(e)
+ }
+}
+ |
+
+ + | +
+ func main() {
+ |
+
+ To start, here’s how to dump a string (or just +bytes) into a file. + + |
+
+ d1 := []byte("hello\ngo\n")
+ err := ioutil.WriteFile("/tmp/dat1", d1, 0644)
+ check(err)
+ |
+
+ For more granular writes, open a file for writing. + + |
+
+ f, err := os.Create("/tmp/dat2")
+ check(err)
+ |
+
+ It’s idiomatic to defer a |
+
+ defer f.Close()
+ |
+
+ You can |
+
+ d2 := []byte{115, 111, 109, 101, 10}
+ n2, err := f.Write(d2)
+ check(err)
+ fmt.Printf("wrote %d bytes\n", n2)
+ |
+
+ A |
+
+ n3, err := f.WriteString("writes\n")
+ fmt.Printf("wrote %d bytes\n", n3)
+ |
+
+ Issue a |
+
+ f.Sync()
+ |
+
+
|
+
+ w := bufio.NewWriter(f)
+ n4, err := w.WriteString("buffered\n")
+ fmt.Printf("wrote %d bytes\n", n4)
+ |
+
+ Use |
+
+ w.Flush()
+ |
+
+ + | +
+ }
+ |
+
+ Try running the file-writing code. + + |
+
+ $ go run writing-files.go
+wrote 5 bytes
+wrote 7 bytes
+wrote 9 bytes
+ |
+
+ Then check the contents of the written files. + + |
+
+ $ cat /tmp/dat1
+hello
+go
+$ cat /tmp/dat2
+some
+writes
+buffered
+ |
+
+ Next we’ll look at applying some of the file I/O ideas
+we’ve just seen to the |
+ + + | +
+ Next example: Line Filters. +
+ + +