Merge branch 'master' into clipboard

This commit is contained in:
Mark McGranaghan 2019-09-01 15:42:08 -07:00
commit ee5c86daa9
251 changed files with 6523 additions and 3873 deletions

2
.env
View File

@ -1,2 +0,0 @@
CANONICAL_HOST=127.0.0.1
FORCE_HTTPS=0

3
.gitattributes vendored Normal file
View File

@ -0,0 +1,3 @@
public/* linguist-generated=true
vendor/* linguist-vendored=true

3
.gitignore vendored
View File

@ -1,2 +1,3 @@
.anvil
*.pyc *.pyc
.idea
.vscode

1
.godir
View File

@ -1 +0,0 @@
gobyexample

18
.travis.yml Normal file
View File

@ -0,0 +1,18 @@
language: go
go:
- 1.12
before_install:
# We need Python to run pygmentize for generating HTML.
- sudo apt-get update
- sudo apt-get install python
install:
- go get -u github.com/russross/blackfriday
script:
- tools/build
env:
- VERBOSE=1 TESTING=1

View File

@ -1,18 +1,18 @@
## Contributing ## Contributing
Go by Example is now in a steady state. We are maintaining it, but are not Thanks for your interest in contributing to Go by Example!
expanding or significantly changing it any more.
With that in mind here are some specific contribution guidelines: * When sending a PR that affects the displayed contents of the site, run
`tools/build` locally and include the generated HTML in the PR. If you
only want to submit a simple typo suggestion (for example, through the
Github website), feel free to send a PR anyway - we'll regenerate the
HTML and merge with your commit.
* If you see a typo or would like to suggest another small change, editing the * We're open to adding more examples to the site. They should be on things
.go or .sh source file should be sufficient for the PR. I can rebuild the used by many programmers and only require the standard library. If you're
HTML when I review the change. interested in adding an example, _please open an issue to discuss the topic
first_.
* We are not going to add any more sections to the site. There are many * We're not going to change the navigation of the site, in particular adding
important topics that Go by Example doesn't cover, which we leave to other
resources.
* We are not going to change the navigation of the site, in particular adding
a "previous section" link or an "index" link other than the on the title a "previous section" link or an "index" link other than the on the title
text. text.

View File

@ -1,9 +1,8 @@
## Go by Example # Go by Example
Content and build toolchain for [Go by Example](https://gobyexample.com), Content and build toolchain for [Go by Example](https://gobyexample.com),
a site that teaches Go via annotated example programs. a site that teaches Go via annotated example programs.
### Overview ### Overview
The Go by Example site is built by extracting code and The Go by Example site is built by extracting code and
@ -17,9 +16,10 @@ The built `public` directory can be served by any
static content system. The production site uses S3 and static content system. The production site uses S3 and
CloudFront, for example. CloudFront, for example.
### Building ### Building
[![Build Status](https://travis-ci.com/mmcgrana/gobyexample.svg "Travis CI status")](https://travis-ci.com/mmcgrana/gobyexample)
To build the site you'll need Go and Python installed. Run: To build the site you'll need Go and Python installed. Run:
```console ```console
@ -34,6 +34,16 @@ To build continuously in a loop:
$ tools/build-loop $ tools/build-loop
``` ```
### Publishing
To upload the site:
```console
$ gem install aws-sdk
$ export AWS_ACCESS_KEY_ID=...
$ export AWS_SECRET_ACCESS_KEY=...
$ tools/upload
```
### License ### License
@ -48,10 +58,13 @@ The Go Gopher is copyright [Renée French](http://reneefrench.blogspot.com/) and
Contributor translations of the Go by Example site are available in: Contributor translations of the Go by Example site are available in:
* [Chinese](http://gobyexample.everyx.in/) by [everyx](https://github.com/everyx) * [Chinese](https://gobyexample.xgwang.me/) by [xg-wang](https://github.com/xg-wang/gobyexample)
* [French](http://le-go-par-l-exemple.keiruaprod.fr) by [keirua](https://github.com/keirua/gobyexample) * [French](http://le-go-par-l-exemple.keiruaprod.fr) by [keirua](https://github.com/keirua/gobyexample)
* [Italian](http://gobyexample.it) by the [Go Italian community](https://github.com/golangit/gobyexample-it) * [Italian](http://gobyexample.it) by the [Go Italian community](https://github.com/golangit/gobyexample-it)
* [Japanese](http://spinute.org/go-by-example) by [spinute](https://github.com/spinute)
* [Korean](https://mingrammer.com/gobyexample/) by [mingrammer](https://github.com/mingrammer)
* [Spanish](http://goconejemplos.com) by the [Go Mexico community](https://github.com/dabit/gobyexample) * [Spanish](http://goconejemplos.com) by the [Go Mexico community](https://github.com/dabit/gobyexample)
* [Ukrainian](http://gobyexample.com.ua/) by [butuzov](https://github.com/butuzov/gobyexample)
### Thanks ### Thanks

View File

@ -32,6 +32,7 @@ Range over Channels
Timers Timers
Tickers Tickers
Worker Pools Worker Pools
WaitGroups
Rate Limiting Rate Limiting
Atomic Counters Atomic Counters
Mutexes Mutexes
@ -56,9 +57,15 @@ Base64 Encoding
Reading Files Reading Files
Writing Files Writing Files
Line Filters Line Filters
File Paths
Directories
Temporary Files and Directories
Command-Line Arguments Command-Line Arguments
Command-Line Flags Command-Line Flags
Command-Line Subcommands
Environment Variables Environment Variables
HTTP Clients
HTTP Servers
Spawning Processes Spawning Processes
Exec'ing Processes Exec'ing Processes
Signals Signals

View File

@ -7,36 +7,36 @@ import "fmt"
func main() { func main() {
// Here we create an array `a` that will hold exactly // Here we create an array `a` that will hold exactly
// 5 `int`s. The type of elements and length are both // 5 `int`s. The type of elements and length are both
// part of the array's type. By default an array is // part of the array's type. By default an array is
// zero-valued, which for `int`s means `0`s. // zero-valued, which for `int`s means `0`s.
var a [5]int var a [5]int
fmt.Println("emp:", a) fmt.Println("emp:", a)
// We can set a value at an index using the // We can set a value at an index using the
// `array[index] = value` syntax, and get a value with // `array[index] = value` syntax, and get a value with
// `array[index]`. // `array[index]`.
a[4] = 100 a[4] = 100
fmt.Println("set:", a) fmt.Println("set:", a)
fmt.Println("get:", a[4]) fmt.Println("get:", a[4])
// The builtin `len` returns the length of an array. // The builtin `len` returns the length of an array.
fmt.Println("len:", len(a)) fmt.Println("len:", len(a))
// Use this syntax to declare and initialize an array // Use this syntax to declare and initialize an array
// in one line. // in one line.
b := [5]int{1, 2, 3, 4, 5} b := [5]int{1, 2, 3, 4, 5}
fmt.Println("dcl:", b) fmt.Println("dcl:", b)
// Array types are one-dimensional, but you can // Array types are one-dimensional, but you can
// compose types to build multi-dimensional data // compose types to build multi-dimensional data
// structures. // structures.
var twoD [2][3]int var twoD [2][3]int
for i := 0; i < 2; i++ { for i := 0; i < 2; i++ {
for j := 0; j < 3; j++ { for j := 0; j < 3; j++ {
twoD[i][j] = i + j twoD[i][j] = i + j
} }
} }
fmt.Println("2d: ", twoD) fmt.Println("2d: ", twoD)
} }

View File

@ -1,2 +1,2 @@
305975d13d24223181d13f042b290906d86c1a0e 305975d13d24223181d13f042b290906d86c1a0e
l-A8eBnwio W7NwfDq8Vdw

View File

@ -13,37 +13,37 @@ import "sync/atomic"
func main() { func main() {
// We'll use an unsigned integer to represent our // We'll use an unsigned integer to represent our
// (always-positive) counter. // (always-positive) counter.
var ops uint64 = 0 var ops uint64
// To simulate concurrent updates, we'll start 50 // To simulate concurrent updates, we'll start 50
// goroutines that each increment the counter about // goroutines that each increment the counter about
// once a millisecond. // once a millisecond.
for i := 0; i < 50; i++ { for i := 0; i < 50; i++ {
go func() { go func() {
for { for {
// To atomically increment the counter we // To atomically increment the counter we
// use `AddUint64`, giving it the memory // use `AddUint64`, giving it the memory
// address of our `ops` counter with the // address of our `ops` counter with the
// `&` syntax. // `&` syntax.
atomic.AddUint64(&ops, 1) atomic.AddUint64(&ops, 1)
// Wait a bit between increments. // Wait a bit between increments.
time.Sleep(time.Millisecond) time.Sleep(time.Millisecond)
} }
}() }()
} }
// Wait a second to allow some ops to accumulate. // Wait a second to allow some ops to accumulate.
time.Sleep(time.Second) time.Sleep(time.Second)
// In order to safely use the counter while it's still // In order to safely use the counter while it's still
// being updated by other goroutines, we extract a // being updated by other goroutines, we extract a
// copy of the current value into `opsFinal` via // copy of the current value into `opsFinal` via
// `LoadUint64`. As above we need to give this // `LoadUint64`. As above we need to give this
// function the memory address `&ops` from which to // function the memory address `&ops` from which to
// fetch the value. // fetch the value.
opsFinal := atomic.LoadUint64(&ops) opsFinal := atomic.LoadUint64(&ops)
fmt.Println("ops:", opsFinal) fmt.Println("ops:", opsFinal)
} }

View File

@ -1,2 +1,2 @@
ce8821f1f4fd99d554ad6cde52403dd3b69bb70a a4190094ea0405b5f2733101beb97939a1d43aee
8p48eFFxDZ KDr9EMMPMgi

View File

@ -11,27 +11,27 @@ import "fmt"
func main() { func main() {
// Here's the `string` we'll encode/decode. // Here's the `string` we'll encode/decode.
data := "abc123!?$*&()'-=@~" data := "abc123!?$*&()'-=@~"
// Go supports both standard and URL-compatible // Go supports both standard and URL-compatible
// base64. Here's how to encode using the standard // base64. Here's how to encode using the standard
// encoder. The encoder requires a `[]byte` so we // encoder. The encoder requires a `[]byte` so we
// cast our `string` to that type. // convert our `string` to that type.
sEnc := b64.StdEncoding.EncodeToString([]byte(data)) sEnc := b64.StdEncoding.EncodeToString([]byte(data))
fmt.Println(sEnc) fmt.Println(sEnc)
// Decoding may return an error, which you can check // Decoding may return an error, which you can check
// if you don't already know the input to be // if you don't already know the input to be
// well-formed. // well-formed.
sDec, _ := b64.StdEncoding.DecodeString(sEnc) sDec, _ := b64.StdEncoding.DecodeString(sEnc)
fmt.Println(string(sDec)) fmt.Println(string(sDec))
fmt.Println() fmt.Println()
// This encodes/decodes using a URL-compatible base64 // This encodes/decodes using a URL-compatible base64
// format. // format.
uEnc := b64.URLEncoding.EncodeToString([]byte(data)) uEnc := b64.URLEncoding.EncodeToString([]byte(data))
fmt.Println(uEnc) fmt.Println(uEnc)
uDec, _ := b64.URLEncoding.DecodeString(uEnc) uDec, _ := b64.URLEncoding.DecodeString(uEnc)
fmt.Println(string(uDec)) fmt.Println(string(uDec))
} }

View File

@ -1,2 +1,2 @@
e57f5be3a796261fb4a55cdb0580a254e14b4930 c20da14820b656c867790f2e99bc37140babca8c
t6rFm2x4Yr y_QTcqdkvZh

View File

@ -11,17 +11,17 @@ import "fmt"
func main() { func main() {
// Here we `make` a channel of strings buffering up to // Here we `make` a channel of strings buffering up to
// 2 values. // 2 values.
messages := make(chan string, 2) messages := make(chan string, 2)
// Because this channel is buffered, we can send these // Because this channel is buffered, we can send these
// values into the channel without a corresponding // values into the channel without a corresponding
// concurrent receive. // concurrent receive.
messages <- "buffered" messages <- "buffered"
messages <- "channel" messages <- "channel"
// Later we can receive these two values as usual. // Later we can receive these two values as usual.
fmt.Println(<-messages) fmt.Println(<-messages)
fmt.Println(<-messages) fmt.Println(<-messages)
} }

View File

@ -1,2 +1,2 @@
122140f7ad1bc5cff4fcd7a9e7245b87aaca3ec5 122140f7ad1bc5cff4fcd7a9e7245b87aaca3ec5
34PVHwO6Bn mPoF-Xi-rip

View File

@ -11,20 +11,20 @@ import "fmt"
// values. It would be a compile-time error to try to // values. It would be a compile-time error to try to
// receive on this channel. // receive on this channel.
func ping(pings chan<- string, msg string) { func ping(pings chan<- string, msg string) {
pings <- msg pings <- msg
} }
// The `pong` function accepts one channel for receives // The `pong` function accepts one channel for receives
// (`pings`) and a second for sends (`pongs`). // (`pings`) and a second for sends (`pongs`).
func pong(pings <-chan string, pongs chan<- string) { func pong(pings <-chan string, pongs chan<- string) {
msg := <-pings msg := <-pings
pongs <- msg pongs <- msg
} }
func main() { func main() {
pings := make(chan string, 1) pings := make(chan string, 1)
pongs := make(chan string, 1) pongs := make(chan string, 1)
ping(pings, "passed message") ping(pings, "passed message")
pong(pings, pongs) pong(pings, pongs)
fmt.Println(<-pongs) fmt.Println(<-pongs)
} }

View File

@ -1,2 +1,2 @@
635cc13dfe33123ac188e01e3002d3aa935d765f 635cc13dfe33123ac188e01e3002d3aa935d765f
P9Fujfpa1f Jnn9_9hC48c

View File

@ -1,6 +1,8 @@
// We can use channels to synchronize execution // We can use channels to synchronize execution
// across goroutines. Here's an example of using a // across goroutines. Here's an example of using a
// blocking receive to wait for a goroutine to finish. // blocking receive to wait for a goroutine to finish.
// When waiting for multiple goroutines to finish,
// you may prefer to use a [WaitGroup](waitgroups).
package main package main
@ -11,22 +13,22 @@ import "time"
// `done` channel will be used to notify another // `done` channel will be used to notify another
// goroutine that this function's work is done. // goroutine that this function's work is done.
func worker(done chan bool) { func worker(done chan bool) {
fmt.Print("working...") fmt.Print("working...")
time.Sleep(time.Second) time.Sleep(time.Second)
fmt.Println("done") fmt.Println("done")
// Send a value to notify that we're done. // Send a value to notify that we're done.
done <- true done <- true
} }
func main() { func main() {
// Start a worker goroutine, giving it the channel to // Start a worker goroutine, giving it the channel to
// notify on. // notify on.
done := make(chan bool, 1) done := make(chan bool, 1)
go worker(done) go worker(done)
// Block until we receive a notification from the // Block until we receive a notification from the
// worker on the channel. // worker on the channel.
<-done <-done
} }

View File

@ -1,2 +1,2 @@
fe3e2ea1a67d0f95ce4cb18f3e8aa16d416de0ce eb022977181884c2ab0f2b69e50311769e67a509
0DfW-1RMqi 8lmP8beav0p

View File

@ -9,18 +9,18 @@ import "fmt"
func main() { func main() {
// Create a new channel with `make(chan val-type)`. // Create a new channel with `make(chan val-type)`.
// Channels are typed by the values they convey. // Channels are typed by the values they convey.
messages := make(chan string) messages := make(chan string)
// _Send_ a value into a channel using the `channel <-` // _Send_ a value into a channel using the `channel <-`
// syntax. Here we send `"ping"` to the `messages` // syntax. Here we send `"ping"` to the `messages`
// channel we made above, from a new goroutine. // channel we made above, from a new goroutine.
go func() { messages <- "ping" }() go func() { messages <- "ping" }()
// The `<-channel` syntax _receives_ a value from the // The `<-channel` syntax _receives_ a value from the
// channel. Here we'll receive the `"ping"` message // channel. Here we'll receive the `"ping"` message
// we sent above and print it out. // we sent above and print it out.
msg := <-messages msg := <-messages
fmt.Println(msg) fmt.Println(msg)
} }

View File

@ -1,2 +1,2 @@
926212c784ab820648906c96f6ab21afbc161526 926212c784ab820648906c96f6ab21afbc161526
Kd8B0T_JGK bRGMAqinovA

View File

@ -11,40 +11,40 @@ import "fmt"
// to a worker goroutine. When we have no more jobs for // to a worker goroutine. When we have no more jobs for
// the worker we'll `close` the `jobs` channel. // the worker we'll `close` the `jobs` channel.
func main() { func main() {
jobs := make(chan int, 5) jobs := make(chan int, 5)
done := make(chan bool) done := make(chan bool)
// Here's the worker goroutine. It repeatedly receives // Here's the worker goroutine. It repeatedly receives
// from `jobs` with `j, more := <-jobs`. In this // from `jobs` with `j, more := <-jobs`. In this
// special 2-value form of receive, the `more` value // special 2-value form of receive, the `more` value
// will be `false` if `jobs` has been `close`d and all // will be `false` if `jobs` has been `close`d and all
// values in the channel have already been received. // values in the channel have already been received.
// We use this to notify on `done` when we've worked // We use this to notify on `done` when we've worked
// all our jobs. // all our jobs.
go func() { go func() {
for { for {
j, more := <-jobs j, more := <-jobs
if more { if more {
fmt.Println("received job", j) fmt.Println("received job", j)
} else { } else {
fmt.Println("received all jobs") fmt.Println("received all jobs")
done <- true done <- true
return return
} }
} }
}() }()
// This sends 3 jobs to the worker over the `jobs` // This sends 3 jobs to the worker over the `jobs`
// channel, then closes it. // channel, then closes it.
for j := 1; j <= 3; j++ { for j := 1; j <= 3; j++ {
jobs <- j jobs <- j
fmt.Println("sent job", j) fmt.Println("sent job", j)
} }
close(jobs) close(jobs)
fmt.Println("sent all jobs") fmt.Println("sent all jobs")
// We await the worker using the // We await the worker using the
// [synchronization](channel-synchronization) approach // [synchronization](channel-synchronization) approach
// we saw earlier. // we saw earlier.
<-done <-done
} }

View File

@ -1,2 +1,2 @@
5205898a520533e46ea24c849848d19ebc2d08a9 5205898a520533e46ea24c849848d19ebc2d08a9
eFZ2SeKswH mkz69rVMHs6

View File

@ -12,29 +12,29 @@ import "fmt"
// returned function _closes over_ the variable `i` to // returned function _closes over_ the variable `i` to
// form a closure. // form a closure.
func intSeq() func() int { func intSeq() func() int {
i := 0 i := 0
return func() int { return func() int {
i += 1 i++
return i return i
} }
} }
func main() { func main() {
// We call `intSeq`, assigning the result (a function) // We call `intSeq`, assigning the result (a function)
// to `nextInt`. This function value captures its // to `nextInt`. This function value captures its
// own `i` value, which will be updated each time // own `i` value, which will be updated each time
// we call `nextInt`. // we call `nextInt`.
nextInt := intSeq() nextInt := intSeq()
// See the effect of the closure by calling `nextInt` // See the effect of the closure by calling `nextInt`
// a few times. // a few times.
fmt.Println(nextInt()) fmt.Println(nextInt())
fmt.Println(nextInt()) fmt.Println(nextInt())
fmt.Println(nextInt()) fmt.Println(nextInt())
// To confirm that the state is unique to that // To confirm that the state is unique to that
// particular function, create and test a new one. // particular function, create and test a new one.
newInts := intSeq() newInts := intSeq()
fmt.Println(newInts()) fmt.Println(newInts())
} }

View File

@ -1,2 +1,2 @@
2e062d01989caada16c4b22ff6a35cd58e4eb819 e304df67e760dda93ffe434aca58aea4a6c94f19
gQtEWkhWyp zb93qzV6iN3

View File

@ -21,91 +21,91 @@ package main
import "strings" import "strings"
import "fmt" import "fmt"
// Returns the first index of the target string `t`, or // Index returns the first index of the target string `t`, or
// -1 if no match is found. // -1 if no match is found.
func Index(vs []string, t string) int { func Index(vs []string, t string) int {
for i, v := range vs { for i, v := range vs {
if v == t { if v == t {
return i return i
} }
} }
return -1 return -1
} }
// Returns `true` if the target string t is in the // Include returns `true` if the target string t is in the
// slice. // slice.
func Include(vs []string, t string) bool { func Include(vs []string, t string) bool {
return Index(vs, t) >= 0 return Index(vs, t) >= 0
} }
// Returns `true` if one of the strings in the slice // Any returns `true` if one of the strings in the slice
// satisfies the predicate `f`. // satisfies the predicate `f`.
func Any(vs []string, f func(string) bool) bool { func Any(vs []string, f func(string) bool) bool {
for _, v := range vs { for _, v := range vs {
if f(v) { if f(v) {
return true return true
} }
} }
return false return false
} }
// Returns `true` if all of the strings in the slice // All returns `true` if all of the strings in the slice
// satisfy the predicate `f`. // satisfy the predicate `f`.
func All(vs []string, f func(string) bool) bool { func All(vs []string, f func(string) bool) bool {
for _, v := range vs { for _, v := range vs {
if !f(v) { if !f(v) {
return false return false
} }
} }
return true return true
} }
// Returns a new slice containing all strings in the // Filter returns a new slice containing all strings in the
// slice that satisfy the predicate `f`. // slice that satisfy the predicate `f`.
func Filter(vs []string, f func(string) bool) []string { func Filter(vs []string, f func(string) bool) []string {
vsf := make([]string, 0) vsf := make([]string, 0)
for _, v := range vs { for _, v := range vs {
if f(v) { if f(v) {
vsf = append(vsf, v) vsf = append(vsf, v)
} }
} }
return vsf return vsf
} }
// Returns a new slice containing the results of applying // Map returns a new slice containing the results of applying
// the function `f` to each string in the original slice. // the function `f` to each string in the original slice.
func Map(vs []string, f func(string) string) []string { func Map(vs []string, f func(string) string) []string {
vsm := make([]string, len(vs)) vsm := make([]string, len(vs))
for i, v := range vs { for i, v := range vs {
vsm[i] = f(v) vsm[i] = f(v)
} }
return vsm return vsm
} }
func main() { func main() {
// Here we try out our various collection functions. // Here we try out our various collection functions.
var strs = []string{"peach", "apple", "pear", "plum"} var strs = []string{"peach", "apple", "pear", "plum"}
fmt.Println(Index(strs, "pear")) fmt.Println(Index(strs, "pear"))
fmt.Println(Include(strs, "grape")) fmt.Println(Include(strs, "grape"))
fmt.Println(Any(strs, func(v string) bool { fmt.Println(Any(strs, func(v string) bool {
return strings.HasPrefix(v, "p") return strings.HasPrefix(v, "p")
})) }))
fmt.Println(All(strs, func(v string) bool { fmt.Println(All(strs, func(v string) bool {
return strings.HasPrefix(v, "p") return strings.HasPrefix(v, "p")
})) }))
fmt.Println(Filter(strs, func(v string) bool { fmt.Println(Filter(strs, func(v string) bool {
return strings.Contains(v, "e") return strings.Contains(v, "e")
})) }))
// The above examples all used anonymous functions, // The above examples all used anonymous functions,
// but you can also use named functions of the correct // but you can also use named functions of the correct
// type. // type.
fmt.Println(Map(strs, strings.ToUpper)) fmt.Println(Map(strs, strings.ToUpper))
} }

View File

@ -1,2 +1,2 @@
ed54b3fc0512ccace0f3d0b74975c9bcd2e7a8a2 d961fc0e0074aed46cfd1516efdadea78781af56
3PNdke3Wia BJB_npWH516

View File

@ -10,17 +10,17 @@ import "fmt"
func main() { func main() {
// `os.Args` provides access to raw command-line // `os.Args` provides access to raw command-line
// arguments. Note that the first value in this slice // arguments. Note that the first value in this slice
// is the path to the program, and `os.Args[1:]` // is the path to the program, and `os.Args[1:]`
// holds the arguments to the program. // holds the arguments to the program.
argsWithProg := os.Args argsWithProg := os.Args
argsWithoutProg := os.Args[1:] argsWithoutProg := os.Args[1:]
// You can get individual args with normal indexing. // You can get individual args with normal indexing.
arg := os.Args[3] arg := os.Args[3]
fmt.Println(argsWithProg) fmt.Println(argsWithProg)
fmt.Println(argsWithoutProg) fmt.Println(argsWithoutProg)
fmt.Println(arg) fmt.Println(arg)
} }

View File

@ -1,2 +1,2 @@
41c970a1ef29ad2a05307e6c783ff52ab80eaccd 41c970a1ef29ad2a05307e6c783ff52ab80eaccd
44uyYt_TRl 6pFdjf800jj

View File

@ -13,37 +13,37 @@ import "fmt"
func main() { func main() {
// Basic flag declarations are available for string, // Basic flag declarations are available for string,
// integer, and boolean options. Here we declare a // integer, and boolean options. Here we declare a
// string flag `word` with a default value `"foo"` // string flag `word` with a default value `"foo"`
// and a short description. This `flag.String` function // and a short description. This `flag.String` function
// returns a string pointer (not a string value); // returns a string pointer (not a string value);
// we'll see how to use this pointer below. // we'll see how to use this pointer below.
wordPtr := flag.String("word", "foo", "a string") wordPtr := flag.String("word", "foo", "a string")
// This declares `numb` and `fork` flags, using a // This declares `numb` and `fork` flags, using a
// similar approach to the `word` flag. // similar approach to the `word` flag.
numbPtr := flag.Int("numb", 42, "an int") numbPtr := flag.Int("numb", 42, "an int")
boolPtr := flag.Bool("fork", false, "a bool") boolPtr := flag.Bool("fork", false, "a bool")
// It's also possible to declare an option that uses an // It's also possible to declare an option that uses an
// existing var declared elsewhere in the program. // existing var declared elsewhere in the program.
// Note that we need to pass in a pointer to the flag // Note that we need to pass in a pointer to the flag
// declaration function. // declaration function.
var svar string var svar string
flag.StringVar(&svar, "svar", "bar", "a string var") flag.StringVar(&svar, "svar", "bar", "a string var")
// Once all flags are declared, call `flag.Parse()` // Once all flags are declared, call `flag.Parse()`
// to execute the command-line parsing. // to execute the command-line parsing.
flag.Parse() flag.Parse()
// Here we'll just dump out the parsed options and // Here we'll just dump out the parsed options and
// any trailing positional arguments. Note that we // any trailing positional arguments. Note that we
// need to dereference the pointers with e.g. `*wordPtr` // need to dereference the pointers with e.g. `*wordPtr`
// to get the actual option values. // to get the actual option values.
fmt.Println("word:", *wordPtr) fmt.Println("word:", *wordPtr)
fmt.Println("numb:", *numbPtr) fmt.Println("numb:", *numbPtr)
fmt.Println("fork:", *boolPtr) fmt.Println("fork:", *boolPtr)
fmt.Println("svar:", svar) fmt.Println("svar:", svar)
fmt.Println("tail:", flag.Args()) fmt.Println("tail:", flag.Args())
} }

View File

@ -1,2 +1,2 @@
e2ba0461c090789168c712cc7ed0f66aab09a8c8 e2ba0461c090789168c712cc7ed0f66aab09a8c8
NASEOq2R3n klFR5DitrCy

View File

@ -54,6 +54,3 @@ $ ./command-line-flags -wat
flag provided but not defined: -wat flag provided but not defined: -wat
Usage of ./command-line-flags: Usage of ./command-line-flags:
... ...
# Next we'll look at environment variables, another common
# way to parameterize programs.

View File

@ -0,0 +1,57 @@
// Some command-line tools, like the `go` tool or `git`
// have many *subcommands*, each with its own set of
// flags. For example, `go build` and `go get` are two
// different subcommands of the `go` tool.
// The `flag` package lets us easily define simple
// subcommands that have their own flags.
package main
import (
"flag"
"fmt"
"os"
)
func main() {
// We declare a subcommand using the `NewFlagSet`
// function, and proceed to define new flags specific
// for this subcommand.
fooCmd := flag.NewFlagSet("foo", flag.ExitOnError)
fooEnable := fooCmd.Bool("enable", false, "enable")
fooName := fooCmd.String("name", "", "name")
// For a different subcommand we can define different
// supported flags.
barCmd := flag.NewFlagSet("bar", flag.ExitOnError)
barLevel := barCmd.Int("level", 0, "level")
// The subcommand is expected as the first argument
// to the program.
if len(os.Args) < 2 {
fmt.Println("expected 'foo' or 'bar' subcommands")
os.Exit(1)
}
// Check which subcommand is invoked.
switch os.Args[1] {
// For every subcommand, we parse its own flags and
// have access to trailing positional arguments.
case "foo":
fooCmd.Parse(os.Args[2:])
fmt.Println("subcommand 'foo'")
fmt.Println(" enable:", *fooEnable)
fmt.Println(" name:", *fooName)
fmt.Println(" tail:", fooCmd.Args())
case "bar":
barCmd.Parse(os.Args[2:])
fmt.Println("subcommand 'bar'")
fmt.Println(" level:", *barLevel)
fmt.Println(" tail:", barCmd.Args())
default:
fmt.Println("expected 'foo' or 'bar' subcommands")
os.Exit(1)
}
}

View File

@ -0,0 +1,2 @@
5a0ec258e4992e9b93b11d48f2f249092ff3db66
gtgSAg76N4I

View File

@ -0,0 +1,24 @@
$ go build command-line-subcommands.go
# First invoke the foo subcommand.
$ ./command-line-subcommands foo -enable -name=joe a1 a2
subcommand 'foo'
enable: true
name: joe
tail: [a1 a2]
# Now try bar.
$ ./command-line-subcommands bar -level 8 a1
subcommand 'bar'
level: 8
tail: [a1]
# But bar won't accept foo's flags.
$ ./command-line-subcommands bar -enable a1
flag provided but not defined: -enable
Usage of bar:
-level int
level
# Next we'll look at environment variables, another common
# way to parameterize programs.

View File

@ -10,24 +10,24 @@ import "math"
const s string = "constant" const s string = "constant"
func main() { func main() {
fmt.Println(s) fmt.Println(s)
// A `const` statement can appear anywhere a `var` // A `const` statement can appear anywhere a `var`
// statement can. // statement can.
const n = 500000000 const n = 500000000
// Constant expressions perform arithmetic with // Constant expressions perform arithmetic with
// arbitrary precision. // arbitrary precision.
const d = 3e20 / n const d = 3e20 / n
fmt.Println(d) fmt.Println(d)
// A numeric constant has no type until it's given // A numeric constant has no type until it's given
// one, such as by an explicit cast. // one, such as by an explicit conversion.
fmt.Println(int64(d)) fmt.Println(int64(d))
// A number can be given a type by using it in a // A number can be given a type by using it in a
// context that requires one, such as a variable // context that requires one, such as a variable
// assignment or function call. For example, here // assignment or function call. For example, here
// `math.Sin` expects a `float64`. // `math.Sin` expects a `float64`.
fmt.Println(math.Sin(n)) fmt.Println(math.Sin(n))
} }

View File

@ -1,2 +1,2 @@
3de4f16f1ed032378268411b2173b95e8000305d 2f2ec3a5ff4eef280199da1908eed261346fb40e
T5sj0eINnp VhP0f8moZd3

View File

@ -13,32 +13,38 @@ import "os"
// do that with `defer`. // do that with `defer`.
func main() { func main() {
// Immediately after getting a file object with // Immediately after getting a file object with
// `createFile`, we defer the closing of that file // `createFile`, we defer the closing of that file
// with `closeFile`. This will be executed at the end // with `closeFile`. This will be executed at the end
// of the enclosing function (`main`), after // of the enclosing function (`main`), after
// `writeFile` has finished. // `writeFile` has finished.
f := createFile("/tmp/defer.txt") f := createFile("/tmp/defer.txt")
defer closeFile(f) defer closeFile(f)
writeFile(f) writeFile(f)
} }
func createFile(p string) *os.File { func createFile(p string) *os.File {
fmt.Println("creating") fmt.Println("creating")
f, err := os.Create(p) f, err := os.Create(p)
if err != nil { if err != nil {
panic(err) panic(err)
} }
return f return f
} }
func writeFile(f *os.File) { func writeFile(f *os.File) {
fmt.Println("writing") fmt.Println("writing")
fmt.Fprintln(f, "data") fmt.Fprintln(f, "data")
} }
func closeFile(f *os.File) { func closeFile(f *os.File) {
fmt.Println("closing") fmt.Println("closing")
f.Close() err := f.Close()
// It's important to check for errors when closing a
// file, even in a deferred function.
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
} }

View File

@ -1,2 +1,2 @@
570699fc50a1d39e9d0ad6a4461aef3248b080e1 fadbe9c05bb42db672316ba19adf3c2189c7b3f5
9aoHwzHcAo OrCaBiCrTKq

View File

@ -0,0 +1,95 @@
// Go has several useful functions for working with
// *directories* in the file system.
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
)
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
// Create a new sub-directory in the current working
// directory.
err := os.Mkdir("subdir", 0755)
check(err)
// When creating temporary directories, it's good
// practice to `defer` their removal. `os.RemoveAll`
// will delete a whole directory tree (similarly to
// `rm -rf`).
defer os.RemoveAll("subdir")
// Helper function to create a new empty file.
createEmptyFile := func(name string) {
d := []byte("")
check(ioutil.WriteFile(name, d, 0644))
}
createEmptyFile("subdir/file1")
// We can create a hierarchy of directories, including
// parents with `MkdirAll`. This is similar to the
// command-line `mkdir -p`.
err = os.MkdirAll("subdir/parent/child", 0755)
check(err)
createEmptyFile("subdir/parent/file2")
createEmptyFile("subdir/parent/file3")
createEmptyFile("subdir/parent/child/file4")
// `ReadDir` lists directory contents, returning a
// slice of `os.FileInfo` objects.
c, err := ioutil.ReadDir("subdir/parent")
check(err)
fmt.Println("Listing subdir/parent")
for _, entry := range c {
fmt.Println(" ", entry.Name(), entry.IsDir())
}
// `Chdir` lets us change the current working directory,
// similarly to `cd`.
err = os.Chdir("subdir/parent/child")
check(err)
// Now we'll see the contents of `subdir/parent/child`
// when listing the *current* directory.
c, err = ioutil.ReadDir(".")
check(err)
fmt.Println("Listing subdir/parent/child")
for _, entry := range c {
fmt.Println(" ", entry.Name(), entry.IsDir())
}
// `cd` back to where we started.
err = os.Chdir("../../..")
check(err)
// We can also visit a directory *recursively*,
// including all its sub-directories. `Walk` accepts
// a callback function to handle every file or
// directory visited.
fmt.Println("Visiting subdir")
err = filepath.Walk("subdir", visit)
}
// `visit` is called for every file or directory found
// recursively by `filepath.Walk`.
func visit(p string, info os.FileInfo, err error) error {
if err != nil {
return err
}
fmt.Println(" ", p, info.IsDir())
return nil
}

View File

@ -0,0 +1,2 @@
83f67db91816b4544072d0a4d099111a21c60723
-7kWq0PmATF

View File

@ -0,0 +1,15 @@
$ go run directories.go
Listing subdir/parent
child true
file2 false
file3 false
Listing subdir/parent/child
file4 false
Visiting subdir
subdir true
subdir/file1 false
subdir/parent true
subdir/parent/child true
subdir/parent/child/file4 false
subdir/parent/file2 false
subdir/parent/file3 false

View File

@ -11,21 +11,21 @@ import "fmt"
func main() { func main() {
// To set a key/value pair, use `os.Setenv`. To get a // To set a key/value pair, use `os.Setenv`. To get a
// value for a key, use `os.Getenv`. This will return // value for a key, use `os.Getenv`. This will return
// an empty string if the key isn't present in the // an empty string if the key isn't present in the
// environment. // environment.
os.Setenv("FOO", "1") os.Setenv("FOO", "1")
fmt.Println("FOO:", os.Getenv("FOO")) fmt.Println("FOO:", os.Getenv("FOO"))
fmt.Println("BAR:", os.Getenv("BAR")) fmt.Println("BAR:", os.Getenv("BAR"))
// Use `os.Environ` to list all key/value pairs in the // Use `os.Environ` to list all key/value pairs in the
// environment. This returns a slice of strings in the // environment. This returns a slice of strings in the
// form `KEY=value`. You can `strings.Split` them to // form `KEY=value`. You can `strings.Split` them to
// get the key and value. Here we print all the keys. // get the key and value. Here we print all the keys.
fmt.Println() fmt.Println()
for _, e := range os.Environ() { for _, e := range os.Environ() {
pair := strings.Split(e, "=") pair := strings.Split(e, "=")
fmt.Println(pair[0]) fmt.Println(pair[0])
} }
} }

View File

@ -1,2 +1,2 @@
4d0832c5a1ddd4e95474791e8802c15452358214 4d0832c5a1ddd4e95474791e8802c15452358214
kfqLhpmEpw CZJ4R_uu6Uu

View File

@ -10,24 +10,24 @@ import "time"
func main() { func main() {
// Use `time.Now` with `Unix` or `UnixNano` to get // Use `time.Now` with `Unix` or `UnixNano` to get
// elapsed time since the Unix epoch in seconds or // elapsed time since the Unix epoch in seconds or
// nanoseconds, respectively. // nanoseconds, respectively.
now := time.Now() now := time.Now()
secs := now.Unix() secs := now.Unix()
nanos := now.UnixNano() nanos := now.UnixNano()
fmt.Println(now) fmt.Println(now)
// Note that there is no `UnixMillis`, so to get the // Note that there is no `UnixMillis`, so to get the
// milliseconds since epoch you'll need to manually // milliseconds since epoch you'll need to manually
// divide from nanoseconds. // divide from nanoseconds.
millis := nanos / 1000000 millis := nanos / 1000000
fmt.Println(secs) fmt.Println(secs)
fmt.Println(millis) fmt.Println(millis)
fmt.Println(nanos) fmt.Println(nanos)
// You can also convert integer seconds or nanoseconds // You can also convert integer seconds or nanoseconds
// since the epoch into the corresponding `time`. // since the epoch into the corresponding `time`.
fmt.Println(time.Unix(secs, 0)) fmt.Println(time.Unix(secs, 0))
fmt.Println(time.Unix(0, nanos)) fmt.Println(time.Unix(0, nanos))
} }

View File

@ -1,2 +1,2 @@
61a498229c8878a97d729cfdd215e5f3960f87ac 61a498229c8878a97d729cfdd215e5f3960f87ac
GP_zEjhlWk eN1Qv2ATB-C

View File

@ -15,17 +15,17 @@ import "fmt"
// By convention, errors are the last return value and // By convention, errors are the last return value and
// have type `error`, a built-in interface. // have type `error`, a built-in interface.
func f1(arg int) (int, error) { func f1(arg int) (int, error) {
if arg == 42 { if arg == 42 {
// `errors.New` constructs a basic `error` value // `errors.New` constructs a basic `error` value
// with the given error message. // with the given error message.
return -1, errors.New("can't work with 42") return -1, errors.New("can't work with 42")
} }
// A nil value in the error position indicates that // A `nil` value in the error position indicates that
// there was no error. // there was no error.
return arg + 3, nil return arg + 3, nil
} }
// It's possible to use custom types as `error`s by // It's possible to use custom types as `error`s by
@ -33,53 +33,53 @@ func f1(arg int) (int, error) {
// variant on the example above that uses a custom type // variant on the example above that uses a custom type
// to explicitly represent an argument error. // to explicitly represent an argument error.
type argError struct { type argError struct {
arg int arg int
prob string prob string
} }
func (e *argError) Error() string { func (e *argError) Error() string {
return fmt.Sprintf("%d - %s", e.arg, e.prob) return fmt.Sprintf("%d - %s", e.arg, e.prob)
} }
func f2(arg int) (int, error) { func f2(arg int) (int, error) {
if arg == 42 { if arg == 42 {
// In this case we use `&argError` syntax to build // In this case we use `&argError` syntax to build
// a new struct, supplying values for the two // a new struct, supplying values for the two
// fields `arg` and `prob`. // fields `arg` and `prob`.
return -1, &argError{arg, "can't work with it"} return -1, &argError{arg, "can't work with it"}
} }
return arg + 3, nil return arg + 3, nil
} }
func main() { func main() {
// The two loops below test out each of our // The two loops below test out each of our
// error-returning functions. Note that the use of an // error-returning functions. Note that the use of an
// inline error check on the `if` line is a common // inline error check on the `if` line is a common
// idiom in Go code. // idiom in Go code.
for _, i := range []int{7, 42} { for _, i := range []int{7, 42} {
if r, e := f1(i); e != nil { if r, e := f1(i); e != nil {
fmt.Println("f1 failed:", e) fmt.Println("f1 failed:", e)
} else { } else {
fmt.Println("f1 worked:", r) fmt.Println("f1 worked:", r)
} }
} }
for _, i := range []int{7, 42} { for _, i := range []int{7, 42} {
if r, e := f2(i); e != nil { if r, e := f2(i); e != nil {
fmt.Println("f2 failed:", e) fmt.Println("f2 failed:", e)
} else { } else {
fmt.Println("f2 worked:", r) fmt.Println("f2 worked:", r)
} }
} }
// If you want to programmatically use the data in // If you want to programmatically use the data in
// a custom error, you'll need to get the error as an // a custom error, you'll need to get the error as an
// instance of the custom error type via type // instance of the custom error type via type
// assertion. // assertion.
_, e := f2(42) _, e := f2(42)
if ae, ok := e.(*argError); ok { if ae, ok := e.(*argError); ok {
fmt.Println(ae.arg) fmt.Println(ae.arg)
fmt.Println(ae.prob) fmt.Println(ae.prob)
} }
} }

View File

@ -1,2 +1,2 @@
07cffb3d4e37162ab7e9e0a192561ddc8042b81a 210ba0f8196006c0380acaec01655816848ef168
BmDQXkPPTk mP_ZR1qjUvA

View File

@ -16,33 +16,33 @@ import "os/exec"
func main() { func main() {
// For our example we'll exec `ls`. Go requires an // For our example we'll exec `ls`. Go requires an
// absolute path to the binary we want to execute, so // absolute path to the binary we want to execute, so
// we'll use `exec.LookPath` to find it (probably // we'll use `exec.LookPath` to find it (probably
// `/bin/ls`). // `/bin/ls`).
binary, lookErr := exec.LookPath("ls") binary, lookErr := exec.LookPath("ls")
if lookErr != nil { if lookErr != nil {
panic(lookErr) panic(lookErr)
} }
// `Exec` requires arguments in slice form (as // `Exec` requires arguments in slice form (as
// apposed to one big string). We'll give `ls` a few // apposed to one big string). We'll give `ls` a few
// common arguments. Note that the first argument should // common arguments. Note that the first argument should
// be the program name. // be the program name.
args := []string{"ls", "-a", "-l", "-h"} args := []string{"ls", "-a", "-l", "-h"}
// `Exec` also needs a set of [environment variables](environment-variables) // `Exec` also needs a set of [environment variables](environment-variables)
// to use. Here we just provide our current // to use. Here we just provide our current
// environment. // environment.
env := os.Environ() env := os.Environ()
// Here's the actual `syscall.Exec` call. If this call is // Here's the actual `syscall.Exec` call. If this call is
// successful, the execution of our process will end // successful, the execution of our process will end
// here and be replaced by the `/bin/ls -a -l -h` // here and be replaced by the `/bin/ls -a -l -h`
// process. If there is an error we'll get a return // process. If there is an error we'll get a return
// value. // value.
execErr := syscall.Exec(binary, args, env) execErr := syscall.Exec(binary, args, env)
if execErr != nil { if execErr != nil {
panic(execErr) panic(execErr)
} }
} }

View File

@ -1,2 +1,2 @@
b527bbb76a42dd4bae541b73a7377b7e83e79905 b527bbb76a42dd4bae541b73a7377b7e83e79905
neqdJ51KLN bf11ADw-2Ho

View File

@ -8,12 +8,12 @@ import "os"
func main() { func main() {
// `defer`s will _not_ be run when using `os.Exit`, so // `defer`s will _not_ be run when using `os.Exit`, so
// this `fmt.Println` will never be called. // this `fmt.Println` will never be called.
defer fmt.Println("!") defer fmt.Println("!")
// Exit with status 3. // Exit with status 3.
os.Exit(3) os.Exit(3)
} }
// Note that unlike e.g. C, Go does not use an integer // Note that unlike e.g. C, Go does not use an integer

View File

@ -1,2 +1,2 @@
dc0bb3eaafa045d6aa05e88aff39322a1ccf822e dc0bb3eaafa045d6aa05e88aff39322a1ccf822e
CDiAh9SXRM vDaM0-MGJ_k

View File

@ -0,0 +1,64 @@
// The `filepath` package provides functions to parse
// and construct *file paths* in a way that is portable
// between operating systems; `dir/file` on Linux vs.
// `dir\file` on Windows, for example.
package main
import (
"fmt"
"path/filepath"
"strings"
)
func main() {
// `Join` should be used to construct paths in a
// portable way. It takes any number of arguments
// and constructs a hierarchical path from them.
p := filepath.Join("dir1", "dir2", "filename")
fmt.Println("p:", p)
// You should always use `Join` instead of
// concatenating `/`s or `\`s manually. In addition
// to providing portability, `Join` will also
// normalize paths by removing superfluous separators
// and directory changes.
fmt.Println(filepath.Join("dir1//", "filename"))
fmt.Println(filepath.Join("dir1/../dir1", "filename"))
// `Dir` and `Base` can be used to split a path to the
// directory and the file. Alternatively, `Split` will
// return both in the same call.
fmt.Println("Dir(p):", filepath.Dir(p))
fmt.Println("Base(p):", filepath.Base(p))
// We can check whether a path is absolute.
fmt.Println(filepath.IsAbs("dir/file"))
fmt.Println(filepath.IsAbs("/dir/file"))
filename := "config.json"
// Some file names have extensions following a dot. We
// can split the extension out of such names with `Ext`.
ext := filepath.Ext(filename)
fmt.Println(ext)
// To find the file's name with the extension removed,
// use `strings.TrimSuffix`.
fmt.Println(strings.TrimSuffix(filename, ext))
// `Rel` finds a relative path between a *base* and a
// *target*. It returns an error if the target cannot
// be made relative to base.
rel, err := filepath.Rel("a/b", "a/b/t/file")
if err != nil {
panic(err)
}
fmt.Println(rel)
rel, err = filepath.Rel("a/b", "a/c/t/file")
if err != nil {
panic(err)
}
fmt.Println(rel)
}

View File

@ -0,0 +1,2 @@
1215302b9e59ee9dee21dcd3c47d5f6c672fb058
QIitbMNiFRx

View File

@ -0,0 +1,12 @@
$ go run file-paths.go
p: dir1/dir2/filename
dir1/filename
dir1/filename
Dir(p): dir1/dir2
Base(p): filename
false
true
.json
config
t/file
../c/t/file

View File

@ -7,32 +7,32 @@ import "fmt"
func main() { func main() {
// The most basic type, with a single condition. // The most basic type, with a single condition.
i := 1 i := 1
for i <= 3 { for i <= 3 {
fmt.Println(i) fmt.Println(i)
i = i + 1 i = i + 1
} }
// A classic initial/condition/after `for` loop. // A classic initial/condition/after `for` loop.
for j := 7; j <= 9; j++ { for j := 7; j <= 9; j++ {
fmt.Println(j) fmt.Println(j)
} }
// `for` without a condition will loop repeatedly // `for` without a condition will loop repeatedly
// until you `break` out of the loop or `return` from // until you `break` out of the loop or `return` from
// the enclosing function. // the enclosing function.
for { for {
fmt.Println("loop") fmt.Println("loop")
break break
} }
// You can also `continue` to the next iteration of // You can also `continue` to the next iteration of
// the loop. // the loop.
for n := 0; n <= 5; n++ { for n := 0; n <= 5; n++ {
if n%2 == 0 { if n%2 == 0 {
continue continue
} }
fmt.Println(n) fmt.Println(n)
} }
} }

View File

@ -1,2 +1,2 @@
33056d6b36f9894fb6359c9cf2ef8725bbdafa19 33056d6b36f9894fb6359c9cf2ef8725bbdafa19
KNLLSX4Io_ lGYfUJwiGfi

View File

@ -9,10 +9,10 @@ import "fmt"
// their sum as an `int`. // their sum as an `int`.
func plus(a int, b int) int { func plus(a int, b int) int {
// Go requires explicit returns, i.e. it won't // Go requires explicit returns, i.e. it won't
// automatically return the value of the last // automatically return the value of the last
// expression. // expression.
return a + b return a + b
} }
// When you have multiple consecutive parameters of // When you have multiple consecutive parameters of
@ -20,16 +20,16 @@ func plus(a int, b int) int {
// like-typed parameters up to the final parameter that // like-typed parameters up to the final parameter that
// declares the type. // declares the type.
func plusPlus(a, b, c int) int { func plusPlus(a, b, c int) int {
return a + b + c return a + b + c
} }
func main() { func main() {
// Call a function just as you'd expect, with // Call a function just as you'd expect, with
// `name(args)`. // `name(args)`.
res := plus(1, 2) res := plus(1, 2)
fmt.Println("1+2 =", res) fmt.Println("1+2 =", res)
res = plusPlus(1, 2, 3) res = plusPlus(1, 2, 3)
fmt.Println("1+2+3 =", res) fmt.Println("1+2+3 =", res)
} }

View File

@ -1,2 +1,2 @@
ae669923c20e5ebf4a7b4b11b8fdf2972accf9e2 ae669923c20e5ebf4a7b4b11b8fdf2972accf9e2
9Nky-Dn49f hzGUvK6iJNm

View File

@ -5,34 +5,33 @@ package main
import "fmt" import "fmt"
func f(from string) { func f(from string) {
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
fmt.Println(from, ":", i) fmt.Println(from, ":", i)
} }
} }
func main() { func main() {
// Suppose we have a function call `f(s)`. Here's how // Suppose we have a function call `f(s)`. Here's how
// we'd call that in the usual way, running it // we'd call that in the usual way, running it
// synchronously. // synchronously.
f("direct") f("direct")
// To invoke this function in a goroutine, use // To invoke this function in a goroutine, use
// `go f(s)`. This new goroutine will execute // `go f(s)`. This new goroutine will execute
// concurrently with the calling one. // concurrently with the calling one.
go f("goroutine") go f("goroutine")
// You can also start a goroutine for an anonymous // You can also start a goroutine for an anonymous
// function call. // function call.
go func(msg string) { go func(msg string) {
fmt.Println(msg) fmt.Println(msg)
}("going") }("going")
// Our two function calls are running asynchronously in // Our two function calls are running asynchronously in
// separate goroutines now, so execution falls through // separate goroutines now, so execution falls through
// to here. This `Scanln` code requires we press a key // to here. This `Scanln` requires we press a key
// before the program exits. // before the program exits.
var input string fmt.Scanln()
fmt.Scanln(&input) fmt.Println("done")
fmt.Println("done")
} }

View File

@ -1,2 +1,2 @@
a847131d7f112172f9d5509fd3cf31aefb6d710e bfdaa0c8104c1257e6fea102fd26d476a3e8c14e
RW_RSAHfj- 6Y8t3Yxd1LD

View File

@ -1,6 +1,6 @@
# When we run this program, we see the output of the # When we run this program, we see the output of the
# blocking call first, then the interleaved output of the # blocking call first, then the interleaved output of the
# two gouroutines. This interleaving reflects the # two goroutines. This interleaving reflects the
# goroutines being run concurrently by the Go runtime. # goroutines being run concurrently by the Go runtime.
$ go run goroutines.go $ go run goroutines.go
direct : 0 direct : 0

View File

@ -5,5 +5,5 @@ package main
import "fmt" import "fmt"
func main() { func main() {
fmt.Println("hello world") fmt.Println("hello world")
} }

View File

@ -1,2 +1,2 @@
c98395a44701add5bf84e2f3a63e300fc1bc4bfe c98395a44701add5bf84e2f3a63e300fc1bc4bfe
2C7wwJ6nxG mp1ENMU6ZYu

View File

@ -0,0 +1,38 @@
// The Go standard library comes with excellent support
// for HTTP clients and servers in the `net/http`
// package. In this example we'll use it to issue simple
// HTTP requests.
package main
import (
"bufio"
"fmt"
"net/http"
)
func main() {
// Issue an HTTP GET request to a server. `http.Get` is a
// convenient shortcut around creating an `http.Client`
// object and calling its `Get` method; it uses the
// `http.DefaultClient` object which has useful default
// settings.
resp, err := http.Get("http://gobyexample.com")
if err != nil {
panic(err)
}
defer resp.Body.Close()
// Print the HTTP response status.
fmt.Println("Response status:", resp.Status)
// Print the first 5 lines of the response body.
scanner := bufio.NewScanner(resp.Body)
for i := 0; scanner.Scan() && i < 5; i++ {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
panic(err)
}
}

View File

@ -0,0 +1,2 @@
ec8fd69aa19e54a7ea05d2a911f09d3a98f0396c
VxYIifr_UuH

View File

@ -0,0 +1,7 @@
$ go run http-clients.go
Response status: 200 OK
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Go by Example</title>

View File

@ -0,0 +1,50 @@
// Writing a basic HTTP server is easy using the
// `net/http` package.
package main
import (
"fmt"
"net/http"
)
// A fundamental concept in `net/http` servers is
// *handlers*. A handler is an object implementing the
// `http.Handler` interface. A common way to write
// a handler is by using the `http.HandlerFunc` adapter
// on functions with the appropriate signature.
func hello(w http.ResponseWriter, req *http.Request) {
// Functions serving as handlers take a
// `http.ResponseWriter` and a `http.Request` as
// arguments. The response writer is used to fill in the
// HTTP response. Here our simple response is just
// "hello\n".
fmt.Fprintf(w, "hello\n")
}
func headers(w http.ResponseWriter, req *http.Request) {
// This handler does something a little more
// sophisticated by reading all the HTTP request
// headers and echoing them into the response body.
for name, headers := range req.Header {
for _, h := range headers {
fmt.Fprintf(w, "%v: %v\n", name, h)
}
}
}
func main() {
// We register our handlers on server routes using the
// `http.HandleFunc` convenience function. It sets up
// the *default router* in the `net/http` package and
// takes a function as an argument.
http.HandleFunc("/hello", hello)
http.HandleFunc("/headers", headers)
// Finally, we call the `ListenAndServe` with the port
// and a handler. `nil` tells it to use the default
// router we've just set up.
http.ListenAndServe(":8090", nil)
}

View File

@ -0,0 +1,2 @@
a4e8d30b7a6f3a6abd96b916d81ce5930bad94f9
lNuS9ysZmxH

View File

@ -0,0 +1,6 @@
# Run the server in the background.
$ go run http-servers.go &
# Access the `/hello` route.
$ curl localhost:8090/hello
hello

View File

@ -7,28 +7,28 @@ import "fmt"
func main() { func main() {
// Here's a basic example. // Here's a basic example.
if 7%2 == 0 { if 7%2 == 0 {
fmt.Println("7 is even") fmt.Println("7 is even")
} else { } else {
fmt.Println("7 is odd") fmt.Println("7 is odd")
} }
// You can have an `if` statement without an else. // You can have an `if` statement without an else.
if 8%4 == 0 { if 8%4 == 0 {
fmt.Println("8 is divisible by 4") fmt.Println("8 is divisible by 4")
} }
// A statement can precede conditionals; any variables // A statement can precede conditionals; any variables
// declared in this statement are available in all // declared in this statement are available in all
// branches. // branches.
if num := 9; num < 0 { if num := 9; num < 0 {
fmt.Println(num, "is negative") fmt.Println(num, "is negative")
} else if num < 10 { } else if num < 10 {
fmt.Println(num, "has 1 digit") fmt.Println(num, "has 1 digit")
} else { } else {
fmt.Println(num, "has multiple digits") fmt.Println(num, "has multiple digits")
} }
} }
// Note that you don't need parentheses around conditions // Note that you don't need parentheses around conditions

View File

@ -1,2 +1,2 @@
89b78f3378e1a574ddfd0260a0404a962852eff8 89b78f3378e1a574ddfd0260a0404a962852eff8
g-aqMz0Ivf p6-WKTqEks4

View File

@ -8,35 +8,35 @@ import "math"
// Here's a basic interface for geometric shapes. // Here's a basic interface for geometric shapes.
type geometry interface { type geometry interface {
area() float64 area() float64
perim() float64 perim() float64
} }
// For our example we'll implement this interface on // For our example we'll implement this interface on
// `rect` and `circle` types. // `rect` and `circle` types.
type rect struct { type rect struct {
width, height float64 width, height float64
} }
type circle struct { type circle struct {
radius float64 radius float64
} }
// To implement an interface in Go, we just need to // To implement an interface in Go, we just need to
// implement all the methods in the interface. Here we // implement all the methods in the interface. Here we
// implement `geometry` on `rect`s. // implement `geometry` on `rect`s.
func (r rect) area() float64 { func (r rect) area() float64 {
return r.width * r.height return r.width * r.height
} }
func (r rect) perim() float64 { func (r rect) perim() float64 {
return 2*r.width + 2*r.height return 2*r.width + 2*r.height
} }
// The implementation for `circle`s. // The implementation for `circle`s.
func (c circle) area() float64 { func (c circle) area() float64 {
return math.Pi * c.radius * c.radius return math.Pi * c.radius * c.radius
} }
func (c circle) perim() float64 { func (c circle) perim() float64 {
return 2 * math.Pi * c.radius return 2 * math.Pi * c.radius
} }
// If a variable has an interface type, then we can call // If a variable has an interface type, then we can call
@ -44,19 +44,19 @@ func (c circle) perim() float64 {
// generic `measure` function taking advantage of this // generic `measure` function taking advantage of this
// to work on any `geometry`. // to work on any `geometry`.
func measure(g geometry) { func measure(g geometry) {
fmt.Println(g) fmt.Println(g)
fmt.Println(g.area()) fmt.Println(g.area())
fmt.Println(g.perim()) fmt.Println(g.perim())
} }
func main() { func main() {
r := rect{width: 3, height: 4} r := rect{width: 3, height: 4}
c := circle{radius: 5} c := circle{radius: 5}
// The `circle` and `rect` struct types both // The `circle` and `rect` struct types both
// implement the `geometry` interface so we can use // implement the `geometry` interface so we can use
// instances of // instances of
// these structs as arguments to `measure`. // these structs as arguments to `measure`.
measure(r) measure(r)
measure(c) measure(c)
} }

View File

@ -1,2 +1,2 @@
3547b935d1e0322c0fb696726c27cae53a275e0a 3547b935d1e0322c0fb696726c27cae53a275e0a
313UebA3rD 0EwsqIn3TTi

View File

@ -10,110 +10,110 @@ import "os"
// We'll use these two structs to demonstrate encoding and // We'll use these two structs to demonstrate encoding and
// decoding of custom types below. // decoding of custom types below.
type Response1 struct { type response1 struct {
Page int Page int
Fruits []string Fruits []string
} }
type Response2 struct { type response2 struct {
Page int `json:"page"` Page int `json:"page"`
Fruits []string `json:"fruits"` Fruits []string `json:"fruits"`
} }
func main() { func main() {
// First we'll look at encoding basic data types to // First we'll look at encoding basic data types to
// JSON strings. Here are some examples for atomic // JSON strings. Here are some examples for atomic
// values. // values.
bolB, _ := json.Marshal(true) bolB, _ := json.Marshal(true)
fmt.Println(string(bolB)) fmt.Println(string(bolB))
intB, _ := json.Marshal(1) intB, _ := json.Marshal(1)
fmt.Println(string(intB)) fmt.Println(string(intB))
fltB, _ := json.Marshal(2.34) fltB, _ := json.Marshal(2.34)
fmt.Println(string(fltB)) fmt.Println(string(fltB))
strB, _ := json.Marshal("gopher") strB, _ := json.Marshal("gopher")
fmt.Println(string(strB)) fmt.Println(string(strB))
// And here are some for slices and maps, which encode // And here are some for slices and maps, which encode
// to JSON arrays and objects as you'd expect. // to JSON arrays and objects as you'd expect.
slcD := []string{"apple", "peach", "pear"} slcD := []string{"apple", "peach", "pear"}
slcB, _ := json.Marshal(slcD) slcB, _ := json.Marshal(slcD)
fmt.Println(string(slcB)) fmt.Println(string(slcB))
mapD := map[string]int{"apple": 5, "lettuce": 7} mapD := map[string]int{"apple": 5, "lettuce": 7}
mapB, _ := json.Marshal(mapD) mapB, _ := json.Marshal(mapD)
fmt.Println(string(mapB)) fmt.Println(string(mapB))
// The JSON package can automatically encode your // The JSON package can automatically encode your
// custom data types. It will only include exported // custom data types. It will only include exported
// fields in the encoded output and will by default // fields in the encoded output and will by default
// use those names as the JSON keys. // use those names as the JSON keys.
res1D := &Response1{ res1D := &response1{
Page: 1, Page: 1,
Fruits: []string{"apple", "peach", "pear"}} Fruits: []string{"apple", "peach", "pear"}}
res1B, _ := json.Marshal(res1D) res1B, _ := json.Marshal(res1D)
fmt.Println(string(res1B)) fmt.Println(string(res1B))
// You can use tags on struct field declarations // You can use tags on struct field declarations
// to customize the encoded JSON key names. Check the // to customize the encoded JSON key names. Check the
// definition of `Response2` above to see an example // definition of `response2` above to see an example
// of such tags. // of such tags.
res2D := &Response2{ res2D := &response2{
Page: 1, Page: 1,
Fruits: []string{"apple", "peach", "pear"}} Fruits: []string{"apple", "peach", "pear"}}
res2B, _ := json.Marshal(res2D) res2B, _ := json.Marshal(res2D)
fmt.Println(string(res2B)) fmt.Println(string(res2B))
// Now let's look at decoding JSON data into Go // Now let's look at decoding JSON data into Go
// values. Here's an example for a generic data // values. Here's an example for a generic data
// structure. // structure.
byt := []byte(`{"num":6.13,"strs":["a","b"]}`) byt := []byte(`{"num":6.13,"strs":["a","b"]}`)
// We need to provide a variable where the JSON // We need to provide a variable where the JSON
// package can put the decoded data. This // package can put the decoded data. This
// `map[string]interface{}` will hold a map of strings // `map[string]interface{}` will hold a map of strings
// to arbitrary data types. // to arbitrary data types.
var dat map[string]interface{} var dat map[string]interface{}
// Here's the actual decoding, and a check for // Here's the actual decoding, and a check for
// associated errors. // associated errors.
if err := json.Unmarshal(byt, &dat); err != nil { if err := json.Unmarshal(byt, &dat); err != nil {
panic(err) panic(err)
} }
fmt.Println(dat) fmt.Println(dat)
// In order to use the values in the decoded map, // In order to use the values in the decoded map,
// we'll need to cast them to their appropriate type. // we'll need to convert them to their appropriate type.
// For example here we cast the value in `num` to // For example here we convert the value in `num` to
// the expected `float64` type. // the expected `float64` type.
num := dat["num"].(float64) num := dat["num"].(float64)
fmt.Println(num) fmt.Println(num)
// Accessing nested data requires a series of // Accessing nested data requires a series of
// casts. // conversions.
strs := dat["strs"].([]interface{}) strs := dat["strs"].([]interface{})
str1 := strs[0].(string) str1 := strs[0].(string)
fmt.Println(str1) fmt.Println(str1)
// We can also decode JSON into custom data types. // We can also decode JSON into custom data types.
// This has the advantages of adding additional // This has the advantages of adding additional
// type-safety to our programs and eliminating the // type-safety to our programs and eliminating the
// need for type assertions when accessing the decoded // need for type assertions when accessing the decoded
// data. // data.
str := `{"page": 1, "fruits": ["apple", "peach"]}` str := `{"page": 1, "fruits": ["apple", "peach"]}`
res := Response2{} res := response2{}
json.Unmarshal([]byte(str), &res) json.Unmarshal([]byte(str), &res)
fmt.Println(res) fmt.Println(res)
fmt.Println(res.Fruits[0]) fmt.Println(res.Fruits[0])
// In the examples above we always used bytes and // In the examples above we always used bytes and
// strings as intermediates between the data and // strings as intermediates between the data and
// JSON representation on standard out. We can also // JSON representation on standard out. We can also
// stream JSON encodings directly to `os.Writer`s like // stream JSON encodings directly to `os.Writer`s like
// `os.Stdout` or even HTTP response bodies. // `os.Stdout` or even HTTP response bodies.
enc := json.NewEncoder(os.Stdout) enc := json.NewEncoder(os.Stdout)
d := map[string]int{"apple": 5, "lettuce": 7} d := map[string]int{"apple": 5, "lettuce": 7}
enc.Encode(d) enc.Encode(d)
} }

View File

@ -1,2 +1,2 @@
dee52e022a957b97c53fb2d2835653ef507502be d4dc2281f64061f077d8f1e9687538f41a339b25
WxRgpycMaH xC6SHbzGBZC

View File

@ -9,33 +9,33 @@
package main package main
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"os" "os"
"strings" "strings"
) )
func main() { func main() {
// Wrapping the unbuffered `os.Stdin` with a buffered // Wrapping the unbuffered `os.Stdin` with a buffered
// scanner gives us a convenient `Scan` method that // scanner gives us a convenient `Scan` method that
// advances the scanner to the next token; which is // advances the scanner to the next token; which is
// the next line in the default scanner. // the next line in the default scanner.
scanner := bufio.NewScanner(os.Stdin) scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() { for scanner.Scan() {
// `Text` returns the current token, here the next line, // `Text` returns the current token, here the next line,
// from the input. // from the input.
ucl := strings.ToUpper(scanner.Text()) ucl := strings.ToUpper(scanner.Text())
// Write out the uppercased line. // Write out the uppercased line.
fmt.Println(ucl) fmt.Println(ucl)
} }
// Check for errors during `Scan`. End of file is // Check for errors during `Scan`. End of file is
// expected and not reported by `Scan` as an error. // expected and not reported by `Scan` as an error.
if err := scanner.Err(); err != nil { if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "error:", err) fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1) os.Exit(1)
} }
} }

View File

@ -1,2 +1,2 @@
87f4a67edf741979f8ff6da85947aa177547f9ef 87f4a67edf741979f8ff6da85947aa177547f9ef
mpYwOHj2ma hnaOIaQAjKF

View File

@ -7,44 +7,44 @@ import "fmt"
func main() { func main() {
// To create an empty map, use the builtin `make`: // To create an empty map, use the builtin `make`:
// `make(map[key-type]val-type)`. // `make(map[key-type]val-type)`.
m := make(map[string]int) m := make(map[string]int)
// Set key/value pairs using typical `name[key] = val` // Set key/value pairs using typical `name[key] = val`
// syntax. // syntax.
m["k1"] = 7 m["k1"] = 7
m["k2"] = 13 m["k2"] = 13
// Printing a map with e.g. `Println` will show all of // Printing a map with e.g. `fmt.Println` will show all of
// its key/value pairs. // its key/value pairs.
fmt.Println("map:", m) fmt.Println("map:", m)
// Get a value for a key with `name[key]`. // Get a value for a key with `name[key]`.
v1 := m["k1"] v1 := m["k1"]
fmt.Println("v1: ", v1) fmt.Println("v1: ", v1)
// The builtin `len` returns the number of key/value // The builtin `len` returns the number of key/value
// pairs when called on a map. // pairs when called on a map.
fmt.Println("len:", len(m)) fmt.Println("len:", len(m))
// The builtin `delete` removes key/value pairs from // The builtin `delete` removes key/value pairs from
// a map. // a map.
delete(m, "k2") delete(m, "k2")
fmt.Println("map:", m) fmt.Println("map:", m)
// The optional second return value when getting a // The optional second return value when getting a
// value from a map indicates if the key was present // value from a map indicates if the key was present
// in the map. This can be used to disambiguate // in the map. This can be used to disambiguate
// between missing keys and keys with zero values // between missing keys and keys with zero values
// like `0` or `""`. Here we didn't need the value // like `0` or `""`. Here we didn't need the value
// itself, so we ignored it with the _blank identifier_ // itself, so we ignored it with the _blank identifier_
// `_`. // `_`.
_, prs := m["k2"] _, prs := m["k2"]
fmt.Println("prs:", prs) fmt.Println("prs:", prs)
// You can also declare and initialize a new map in // You can also declare and initialize a new map in
// the same line with this syntax. // the same line with this syntax.
n := map[string]int{"foo": 1, "bar": 2} n := map[string]int{"foo": 1, "bar": 2}
fmt.Println("map:", n) fmt.Println("map:", n)
} }

View File

@ -1,2 +1,2 @@
2895d63b87f88ab374256c12dd1539cf7b070b77 3e39d07e3f80ecbac558c6fb8baee2a5f914cf97
E6cGoiKqka U67R66Oab8r

View File

@ -6,4 +6,4 @@ v1: 7
len: 2 len: 2
map: map[k1:7] map: map[k1:7]
prs: false prs: false
map: map[foo:1 bar:2] map: map[bar:2 foo:1]

View File

@ -5,33 +5,33 @@ package main
import "fmt" import "fmt"
type rect struct { type rect struct {
width, height int width, height int
} }
// This `area` method has a _receiver type_ of `*rect`. // This `area` method has a _receiver type_ of `*rect`.
func (r *rect) area() int { func (r *rect) area() int {
return r.width * r.height return r.width * r.height
} }
// Methods can be defined for either pointer or value // Methods can be defined for either pointer or value
// receiver types. Here's an example of a value receiver. // receiver types. Here's an example of a value receiver.
func (r rect) perim() int { func (r rect) perim() int {
return 2*r.width + 2*r.height return 2*r.width + 2*r.height
} }
func main() { func main() {
r := rect{width: 10, height: 5} r := rect{width: 10, height: 5}
// Here we call the 2 methods defined for our struct. // Here we call the 2 methods defined for our struct.
fmt.Println("area: ", r.area()) fmt.Println("area: ", r.area())
fmt.Println("perim:", r.perim()) fmt.Println("perim:", r.perim())
// Go automatically handles conversion between values // Go automatically handles conversion between values
// and pointers for method calls. You may want to use // and pointers for method calls. You may want to use
// a pointer receiver type to avoid copying on method // a pointer receiver type to avoid copying on method
// calls or to allow the method to mutate the // calls or to allow the method to mutate the
// receiving struct. // receiving struct.
rp := &r rp := &r
fmt.Println("area: ", rp.area()) fmt.Println("area: ", rp.area())
fmt.Println("perim:", rp.perim()) fmt.Println("perim:", rp.perim())
} }

View File

@ -1,2 +1,2 @@
24cfb9ad45e43c2d49163149bc55925a4e1b3c7a 24cfb9ad45e43c2d49163149bc55925a4e1b3c7a
254m_9Yjwa ffMb0txGnYB

View File

@ -9,19 +9,19 @@ import "fmt"
// The `(int, int)` in this function signature shows that // The `(int, int)` in this function signature shows that
// the function returns 2 `int`s. // the function returns 2 `int`s.
func vals() (int, int) { func vals() (int, int) {
return 3, 7 return 3, 7
} }
func main() { func main() {
// Here we use the 2 different return values from the // Here we use the 2 different return values from the
// call with _multiple assignment_. // call with _multiple assignment_.
a, b := vals() a, b := vals()
fmt.Println(a) fmt.Println(a)
fmt.Println(b) fmt.Println(b)
// If you only want a subset of the returned values, // If you only want a subset of the returned values,
// use the blank identifier `_`. // use the blank identifier `_`.
_, c := vals() _, c := vals()
fmt.Println(c) fmt.Println(c)
} }

View File

@ -1,2 +1,2 @@
5063ce3d3c70c6bd70f4b709de24bb93d0f24e0c 5063ce3d3c70c6bd70f4b709de24bb93d0f24e0c
chwFmr5dG1 FZoIB5LXQGZ

View File

@ -1,85 +1,85 @@
// In the previous example we saw how to manage simple // In the previous example we saw how to manage simple
// counter state using [atomic operations](atomic-counters). // counter state using [atomic operations](atomic-counters).
// For more complex state we can use a _[mutex](http://en.wikipedia.org/wiki/Mutual_exclusion)_ // For more complex state we can use a <em>[mutex](http://en.wikipedia.org/wiki/Mutual_exclusion)</em>
// to safely access data across multiple goroutines. // to safely access data across multiple goroutines.
package main package main
import ( import (
"fmt" "fmt"
"math/rand" "math/rand"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
) )
func main() { func main() {
// For our example the `state` will be a map. // For our example the `state` will be a map.
var state = make(map[int]int) var state = make(map[int]int)
// This `mutex` will synchronize access to `state`. // This `mutex` will synchronize access to `state`.
var mutex = &sync.Mutex{} var mutex = &sync.Mutex{}
// We'll keep track of how many read and write // We'll keep track of how many read and write
// operations we do. // operations we do.
var readOps uint64 = 0 var readOps uint64
var writeOps uint64 = 0 var writeOps uint64
// Here we start 100 goroutines to execute repeated // Here we start 100 goroutines to execute repeated
// reads against the state, once per millisecond in // reads against the state, once per millisecond in
// each goroutine. // each goroutine.
for r := 0; r < 100; r++ { for r := 0; r < 100; r++ {
go func() { go func() {
total := 0 total := 0
for { for {
// For each read we pick a key to access, // For each read we pick a key to access,
// `Lock()` the `mutex` to ensure // `Lock()` the `mutex` to ensure
// exclusive access to the `state`, read // exclusive access to the `state`, read
// the value at the chosen key, // the value at the chosen key,
// `Unlock()` the mutex, and increment // `Unlock()` the mutex, and increment
// the `readOps` count. // the `readOps` count.
key := rand.Intn(5) key := rand.Intn(5)
mutex.Lock() mutex.Lock()
total += state[key] total += state[key]
mutex.Unlock() mutex.Unlock()
atomic.AddUint64(&readOps, 1) atomic.AddUint64(&readOps, 1)
// Wait a bit between reads. // Wait a bit between reads.
time.Sleep(time.Millisecond) time.Sleep(time.Millisecond)
} }
}() }()
} }
// We'll also start 10 goroutines to simulate writes, // We'll also start 10 goroutines to simulate writes,
// using the same pattern we did for reads. // using the same pattern we did for reads.
for w := 0; w < 10; w++ { for w := 0; w < 10; w++ {
go func() { go func() {
for { for {
key := rand.Intn(5) key := rand.Intn(5)
val := rand.Intn(100) val := rand.Intn(100)
mutex.Lock() mutex.Lock()
state[key] = val state[key] = val
mutex.Unlock() mutex.Unlock()
atomic.AddUint64(&writeOps, 1) atomic.AddUint64(&writeOps, 1)
time.Sleep(time.Millisecond) time.Sleep(time.Millisecond)
} }
}() }()
} }
// Let the 10 goroutines work on the `state` and // Let the 10 goroutines work on the `state` and
// `mutex` for a second. // `mutex` for a second.
time.Sleep(time.Second) time.Sleep(time.Second)
// Take and report final operation counts. // Take and report final operation counts.
readOpsFinal := atomic.LoadUint64(&readOps) readOpsFinal := atomic.LoadUint64(&readOps)
fmt.Println("readOps:", readOpsFinal) fmt.Println("readOps:", readOpsFinal)
writeOpsFinal := atomic.LoadUint64(&writeOps) writeOpsFinal := atomic.LoadUint64(&writeOps)
fmt.Println("writeOps:", writeOpsFinal) fmt.Println("writeOps:", writeOpsFinal)
// With a final lock of `state`, show how it ended up. // With a final lock of `state`, show how it ended up.
mutex.Lock() mutex.Lock()
fmt.Println("state:", state) fmt.Println("state:", state)
mutex.Unlock() mutex.Unlock()
} }

View File

@ -1,2 +1,2 @@
e82356cbb37143862b0a9bbc68856f4b272c4918 ca257d9594a6219d5803193132e999a32dc8c856
a9Wky7k-Bw IRewFKz2OPN

View File

@ -8,39 +8,42 @@ package main
import "fmt" import "fmt"
func main() { func main() {
messages := make(chan string) messages := make(chan string)
signals := make(chan bool) signals := make(chan bool)
// Here's a non-blocking receive. If a value is // Here's a non-blocking receive. If a value is
// available on `messages` then `select` will take // available on `messages` then `select` will take
// the `<-messages` `case` with that value. If not // the `<-messages` `case` with that value. If not
// it will immediately take the `default` case. // it will immediately take the `default` case.
select { select {
case msg := <-messages: case msg := <-messages:
fmt.Println("received message", msg) fmt.Println("received message", msg)
default: default:
fmt.Println("no message received") fmt.Println("no message received")
} }
// A non-blocking send works similarly. // A non-blocking send works similarly. Here `msg`
msg := "hi" // cannot be sent to the `messages` channel, because
select { // the channel has no buffer and there is no receiver.
case messages <- msg: // Therefore the `default` case is selected.
fmt.Println("sent message", msg) msg := "hi"
default: select {
fmt.Println("no message sent") case messages <- msg:
} fmt.Println("sent message", msg)
default:
fmt.Println("no message sent")
}
// We can use multiple `case`s above the `default` // We can use multiple `case`s above the `default`
// clause to implement a multi-way non-blocking // clause to implement a multi-way non-blocking
// select. Here we attempt non-blocking receives // select. Here we attempt non-blocking receives
// on both `messages` and `signals`. // on both `messages` and `signals`.
select { select {
case msg := <-messages: case msg := <-messages:
fmt.Println("received message", msg) fmt.Println("received message", msg)
case sig := <-signals: case sig := <-signals:
fmt.Println("received signal", sig) fmt.Println("received signal", sig)
default: default:
fmt.Println("no activity") fmt.Println("no activity")
} }
} }

View File

@ -1,2 +1,2 @@
119ced4df4f79795b163483b6abfd855e76ef577 a6e0a8bb87153c7ed0de4996172f7ad5d89c6814
M972dltae2 n5ttmOsMrrJ

View File

@ -10,31 +10,31 @@ import "fmt"
func main() { func main() {
// With `ParseFloat`, this `64` tells how many bits of // With `ParseFloat`, this `64` tells how many bits of
// precision to parse. // precision to parse.
f, _ := strconv.ParseFloat("1.234", 64) f, _ := strconv.ParseFloat("1.234", 64)
fmt.Println(f) fmt.Println(f)
// For `ParseInt`, the `0` means infer the base from // For `ParseInt`, the `0` means infer the base from
// the string. `64` requires that the result fit in 64 // the string. `64` requires that the result fit in 64
// bits. // bits.
i, _ := strconv.ParseInt("123", 0, 64) i, _ := strconv.ParseInt("123", 0, 64)
fmt.Println(i) fmt.Println(i)
// `ParseInt` will recognize hex-formatted numbers. // `ParseInt` will recognize hex-formatted numbers.
d, _ := strconv.ParseInt("0x1c8", 0, 64) d, _ := strconv.ParseInt("0x1c8", 0, 64)
fmt.Println(d) fmt.Println(d)
// A `ParseUint` is also available. // A `ParseUint` is also available.
u, _ := strconv.ParseUint("789", 0, 64) u, _ := strconv.ParseUint("789", 0, 64)
fmt.Println(u) fmt.Println(u)
// `Atoi` is a convenience function for basic base-10 // `Atoi` is a convenience function for basic base-10
// `int` parsing. // `int` parsing.
k, _ := strconv.Atoi("135") k, _ := strconv.Atoi("135")
fmt.Println(k) fmt.Println(k)
// Parse functions return an error on bad input. // Parse functions return an error on bad input.
_, e := strconv.Atoi("wat") _, e := strconv.Atoi("wat")
fmt.Println(e) fmt.Println(e)
} }

View File

@ -1,2 +1,2 @@
0d2155e9863a73c098d44637e92403d7f5e8e965 0d2155e9863a73c098d44637e92403d7f5e8e965
N90EppECFk NZh4LjhguvN

View File

@ -9,17 +9,17 @@ import "os"
func main() { func main() {
// We'll use panic throughout this site to check for // We'll use panic throughout this site to check for
// unexpected errors. This is the only program on the // unexpected errors. This is the only program on the
// site designed to panic. // site designed to panic.
panic("a problem") panic("a problem")
// A common use of panic is to abort if a function // A common use of panic is to abort if a function
// returns an error value that we don't know how to // returns an error value that we don't know how to
// (or want to) handle. Here's an example of // (or want to) handle. Here's an example of
// `panic`king if we get an unexpected error when creating a new file. // `panic`king if we get an unexpected error when creating a new file.
_, err := os.Create("/tmp/file") _, err := os.Create("/tmp/file")
if err != nil { if err != nil {
panic(err) panic(err)
} }
} }

View File

@ -1,2 +1,2 @@
91639bbcfcc6ed088295a9ee6b1c36ab35ae402a 91639bbcfcc6ed088295a9ee6b1c36ab35ae402a
c86oXzfQOt 91HXbZZZopt

View File

@ -12,7 +12,7 @@ import "fmt"
// value. `zeroval` will get a copy of `ival` distinct // value. `zeroval` will get a copy of `ival` distinct
// from the one in the calling function. // from the one in the calling function.
func zeroval(ival int) { func zeroval(ival int) {
ival = 0 ival = 0
} }
// `zeroptr` in contrast has an `*int` parameter, meaning // `zeroptr` in contrast has an `*int` parameter, meaning
@ -22,21 +22,21 @@ func zeroval(ival int) {
// Assigning a value to a dereferenced pointer changes the // Assigning a value to a dereferenced pointer changes the
// value at the referenced address. // value at the referenced address.
func zeroptr(iptr *int) { func zeroptr(iptr *int) {
*iptr = 0 *iptr = 0
} }
func main() { func main() {
i := 1 i := 1
fmt.Println("initial:", i) fmt.Println("initial:", i)
zeroval(i) zeroval(i)
fmt.Println("zeroval:", i) fmt.Println("zeroval:", i)
// The `&i` syntax gives the memory address of `i`, // The `&i` syntax gives the memory address of `i`,
// i.e. a pointer to `i`. // i.e. a pointer to `i`.
zeroptr(&i) zeroptr(&i)
fmt.Println("zeroptr:", i) fmt.Println("zeroptr:", i)
// Pointers can be printed too. // Pointers can be printed too.
fmt.Println("pointer:", &i) fmt.Println("pointer:", &i)
} }

View File

@ -1,2 +1,2 @@
85cff3345d2f22b65a5d54eb8f7aa8f508f27887 85cff3345d2f22b65a5d54eb8f7aa8f508f27887
KdE4TBbUL2 fnQkHp4hriG

View File

@ -10,45 +10,45 @@ import "math/rand"
func main() { func main() {
// For example, `rand.Intn` returns a random `int` n, // For example, `rand.Intn` returns a random `int` n,
// `0 <= n < 100`. // `0 <= n < 100`.
fmt.Print(rand.Intn(100), ",") fmt.Print(rand.Intn(100), ",")
fmt.Print(rand.Intn(100)) fmt.Print(rand.Intn(100))
fmt.Println() fmt.Println()
// `rand.Float64` returns a `float64` `f`, // `rand.Float64` returns a `float64` `f`,
// `0.0 <= f < 1.0`. // `0.0 <= f < 1.0`.
fmt.Println(rand.Float64()) fmt.Println(rand.Float64())
// This can be used to generate random floats in // This can be used to generate random floats in
// other ranges, for example `5.0 <= f' < 10.0`. // other ranges, for example `5.0 <= f' < 10.0`.
fmt.Print((rand.Float64()*5)+5, ",") fmt.Print((rand.Float64()*5)+5, ",")
fmt.Print((rand.Float64() * 5) + 5) fmt.Print((rand.Float64() * 5) + 5)
fmt.Println() fmt.Println()
// The default number generator is deterministic, so it'll // The default number generator is deterministic, so it'll
// produce the same sequence of numbers each time by default. // produce the same sequence of numbers each time by default.
// To produce varying sequences, give it a seed that changes. // To produce varying sequences, give it a seed that changes.
// Note that this is not safe to use for random numbers you // Note that this is not safe to use for random numbers you
// intend to be secret, use `crypto/rand` for those. // intend to be secret, use `crypto/rand` for those.
s1 := rand.NewSource(time.Now().UnixNano()) s1 := rand.NewSource(time.Now().UnixNano())
r1 := rand.New(s1) r1 := rand.New(s1)
// Call the resulting `rand.Rand` just like the // Call the resulting `rand.Rand` just like the
// functions on the `rand` package. // functions on the `rand` package.
fmt.Print(r1.Intn(100), ",") fmt.Print(r1.Intn(100), ",")
fmt.Print(r1.Intn(100)) fmt.Print(r1.Intn(100))
fmt.Println() fmt.Println()
// If you seed a source with the same number, it // If you seed a source with the same number, it
// produces the same sequence of random numbers. // produces the same sequence of random numbers.
s2 := rand.NewSource(42) s2 := rand.NewSource(42)
r2 := rand.New(s2) r2 := rand.New(s2)
fmt.Print(r2.Intn(100), ",") fmt.Print(r2.Intn(100), ",")
fmt.Print(r2.Intn(100)) fmt.Print(r2.Intn(100))
fmt.Println() fmt.Println()
s3 := rand.NewSource(42) s3 := rand.NewSource(42)
r3 := rand.New(s3) r3 := rand.New(s3)
fmt.Print(r3.Intn(100), ",") fmt.Print(r3.Intn(100), ",")
fmt.Print(r3.Intn(100)) fmt.Print(r3.Intn(100))
} }

View File

@ -1,2 +1,2 @@
8e97de760147b061dd09939db294c892211b6b80 8e97de760147b061dd09939db294c892211b6b80
ZdFpbahgC1 jiJaIjxL2sP

View File

@ -9,17 +9,17 @@ import "fmt"
func main() { func main() {
// We'll iterate over 2 values in the `queue` channel. // We'll iterate over 2 values in the `queue` channel.
queue := make(chan string, 2) queue := make(chan string, 2)
queue <- "one" queue <- "one"
queue <- "two" queue <- "two"
close(queue) close(queue)
// This `range` iterates over each element as it's // This `range` iterates over each element as it's
// received from `queue`. Because we `close`d the // received from `queue`. Because we `close`d the
// channel above, the iteration terminates after // channel above, the iteration terminates after
// receiving the 2 elements. // receiving the 2 elements.
for elem := range queue { for elem := range queue {
fmt.Println(elem) fmt.Println(elem)
} }
} }

View File

@ -1,2 +1,2 @@
8b5d8a77e84c34771c5b14af014ecef3f88b2a6c 8b5d8a77e84c34771c5b14af014ecef3f88b2a6c
I63ge2ISDs QnARPm-ddFB

Some files were not shown because too many files have changed in this diff Show More