Merge branch 'master' into clipboard
This commit is contained in:
commit
ee5c86daa9
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
public/* linguist-generated=true
|
||||
vendor/* linguist-vendored=true
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
.anvil
|
||||
*.pyc
|
||||
.idea
|
||||
.vscode
|
||||
|
18
.travis.yml
Normal file
18
.travis.yml
Normal 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
|
@ -1,18 +1,18 @@
|
||||
## Contributing
|
||||
|
||||
Go by Example is now in a steady state. We are maintaining it, but are not
|
||||
expanding or significantly changing it any more.
|
||||
Thanks for your interest in contributing to Go by Example!
|
||||
|
||||
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
|
||||
.go or .sh source file should be sufficient for the PR. I can rebuild the
|
||||
HTML when I review the change.
|
||||
* We're open to adding more examples to the site. They should be on things
|
||||
used by many programmers and only require the standard library. If you're
|
||||
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
|
||||
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
|
||||
* We're 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
|
||||
text.
|
||||
|
21
README.md
21
README.md
@ -1,9 +1,8 @@
|
||||
## Go by Example
|
||||
# Go by Example
|
||||
|
||||
Content and build toolchain for [Go by Example](https://gobyexample.com),
|
||||
a site that teaches Go via annotated example programs.
|
||||
|
||||
|
||||
### Overview
|
||||
|
||||
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
|
||||
CloudFront, for example.
|
||||
|
||||
|
||||
### Building
|
||||
|
||||
[](https://travis-ci.com/mmcgrana/gobyexample)
|
||||
|
||||
To build the site you'll need Go and Python installed. Run:
|
||||
|
||||
```console
|
||||
@ -34,6 +34,16 @@ To build continuously in a 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
|
||||
|
||||
@ -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:
|
||||
|
||||
* [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)
|
||||
* [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)
|
||||
* [Ukrainian](http://gobyexample.com.ua/) by [butuzov](https://github.com/butuzov/gobyexample)
|
||||
|
||||
### Thanks
|
||||
|
||||
|
@ -32,6 +32,7 @@ Range over Channels
|
||||
Timers
|
||||
Tickers
|
||||
Worker Pools
|
||||
WaitGroups
|
||||
Rate Limiting
|
||||
Atomic Counters
|
||||
Mutexes
|
||||
@ -56,9 +57,15 @@ Base64 Encoding
|
||||
Reading Files
|
||||
Writing Files
|
||||
Line Filters
|
||||
File Paths
|
||||
Directories
|
||||
Temporary Files and Directories
|
||||
Command-Line Arguments
|
||||
Command-Line Flags
|
||||
Command-Line Subcommands
|
||||
Environment Variables
|
||||
HTTP Clients
|
||||
HTTP Servers
|
||||
Spawning Processes
|
||||
Exec'ing Processes
|
||||
Signals
|
||||
|
@ -7,36 +7,36 @@ import "fmt"
|
||||
|
||||
func main() {
|
||||
|
||||
// Here we create an array `a` that will hold exactly
|
||||
// 5 `int`s. The type of elements and length are both
|
||||
// part of the array's type. By default an array is
|
||||
// zero-valued, which for `int`s means `0`s.
|
||||
var a [5]int
|
||||
fmt.Println("emp:", a)
|
||||
// Here we create an array `a` that will hold exactly
|
||||
// 5 `int`s. The type of elements and length are both
|
||||
// part of the array's type. By default an array is
|
||||
// zero-valued, which for `int`s means `0`s.
|
||||
var a [5]int
|
||||
fmt.Println("emp:", a)
|
||||
|
||||
// We can set a value at an index using the
|
||||
// `array[index] = value` syntax, and get a value with
|
||||
// `array[index]`.
|
||||
a[4] = 100
|
||||
fmt.Println("set:", a)
|
||||
fmt.Println("get:", a[4])
|
||||
// We can set a value at an index using the
|
||||
// `array[index] = value` syntax, and get a value with
|
||||
// `array[index]`.
|
||||
a[4] = 100
|
||||
fmt.Println("set:", a)
|
||||
fmt.Println("get:", a[4])
|
||||
|
||||
// The builtin `len` returns the length of an array.
|
||||
fmt.Println("len:", len(a))
|
||||
// The builtin `len` returns the length of an array.
|
||||
fmt.Println("len:", len(a))
|
||||
|
||||
// Use this syntax to declare and initialize an array
|
||||
// in one line.
|
||||
b := [5]int{1, 2, 3, 4, 5}
|
||||
fmt.Println("dcl:", b)
|
||||
// Use this syntax to declare and initialize 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)
|
||||
// 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)
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
305975d13d24223181d13f042b290906d86c1a0e
|
||||
l-A8eBnwio
|
||||
W7NwfDq8Vdw
|
||||
|
@ -13,37 +13,37 @@ import "sync/atomic"
|
||||
|
||||
func main() {
|
||||
|
||||
// We'll use an unsigned integer to represent our
|
||||
// (always-positive) counter.
|
||||
var ops uint64 = 0
|
||||
// We'll use an unsigned integer to represent our
|
||||
// (always-positive) counter.
|
||||
var ops uint64
|
||||
|
||||
// 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 {
|
||||
// To atomically increment the counter we
|
||||
// use `AddUint64`, giving it the memory
|
||||
// address of our `ops` counter with the
|
||||
// `&` syntax.
|
||||
atomic.AddUint64(&ops, 1)
|
||||
// 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 {
|
||||
// To atomically increment the counter we
|
||||
// use `AddUint64`, giving it the memory
|
||||
// address of our `ops` counter with the
|
||||
// `&` syntax.
|
||||
atomic.AddUint64(&ops, 1)
|
||||
|
||||
// Wait a bit between increments.
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
}()
|
||||
}
|
||||
// Wait a bit between increments.
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Wait a second to allow some ops to accumulate.
|
||||
time.Sleep(time.Second)
|
||||
// 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` via
|
||||
// `LoadUint64`. As above we need to give this
|
||||
// function the memory address `&ops` from which to
|
||||
// fetch the value.
|
||||
opsFinal := atomic.LoadUint64(&ops)
|
||||
fmt.Println("ops:", opsFinal)
|
||||
// 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` via
|
||||
// `LoadUint64`. As above we need to give this
|
||||
// function the memory address `&ops` from which to
|
||||
// fetch the value.
|
||||
opsFinal := atomic.LoadUint64(&ops)
|
||||
fmt.Println("ops:", opsFinal)
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
ce8821f1f4fd99d554ad6cde52403dd3b69bb70a
|
||||
8p48eFFxDZ
|
||||
a4190094ea0405b5f2733101beb97939a1d43aee
|
||||
KDr9EMMPMgi
|
||||
|
@ -11,27 +11,27 @@ import "fmt"
|
||||
|
||||
func main() {
|
||||
|
||||
// Here's the `string` we'll encode/decode.
|
||||
data := "abc123!?$*&()'-=@~"
|
||||
// Here's the `string` we'll encode/decode.
|
||||
data := "abc123!?$*&()'-=@~"
|
||||
|
||||
// Go supports both standard and URL-compatible
|
||||
// base64. Here's how to encode using the standard
|
||||
// encoder. The encoder requires a `[]byte` so we
|
||||
// cast our `string` to that type.
|
||||
sEnc := b64.StdEncoding.EncodeToString([]byte(data))
|
||||
fmt.Println(sEnc)
|
||||
// Go supports both standard and URL-compatible
|
||||
// base64. Here's how to encode using the standard
|
||||
// encoder. The encoder requires a `[]byte` so we
|
||||
// convert our `string` to that type.
|
||||
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()
|
||||
// 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))
|
||||
// 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))
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
e57f5be3a796261fb4a55cdb0580a254e14b4930
|
||||
t6rFm2x4Yr
|
||||
c20da14820b656c867790f2e99bc37140babca8c
|
||||
y_QTcqdkvZh
|
||||
|
@ -11,17 +11,17 @@ import "fmt"
|
||||
|
||||
func main() {
|
||||
|
||||
// Here we `make` a channel of strings buffering up to
|
||||
// 2 values.
|
||||
messages := make(chan string, 2)
|
||||
// Here we `make` a channel of strings buffering up to
|
||||
// 2 values.
|
||||
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"
|
||||
// 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)
|
||||
// Later we can receive these two values as usual.
|
||||
fmt.Println(<-messages)
|
||||
fmt.Println(<-messages)
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
122140f7ad1bc5cff4fcd7a9e7245b87aaca3ec5
|
||||
34PVHwO6Bn
|
||||
mPoF-Xi-rip
|
||||
|
@ -11,20 +11,20 @@ import "fmt"
|
||||
// values. It would be a compile-time error to try to
|
||||
// receive on this channel.
|
||||
func ping(pings chan<- string, msg string) {
|
||||
pings <- msg
|
||||
pings <- msg
|
||||
}
|
||||
|
||||
// The `pong` function accepts one channel for receives
|
||||
// (`pings`) and a second for sends (`pongs`).
|
||||
func pong(pings <-chan string, pongs chan<- string) {
|
||||
msg := <-pings
|
||||
pongs <- msg
|
||||
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)
|
||||
pings := make(chan string, 1)
|
||||
pongs := make(chan string, 1)
|
||||
ping(pings, "passed message")
|
||||
pong(pings, pongs)
|
||||
fmt.Println(<-pongs)
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
635cc13dfe33123ac188e01e3002d3aa935d765f
|
||||
P9Fujfpa1f
|
||||
Jnn9_9hC48c
|
||||
|
@ -1,6 +1,8 @@
|
||||
// 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.
|
||||
// When waiting for multiple goroutines to finish,
|
||||
// you may prefer to use a [WaitGroup](waitgroups).
|
||||
|
||||
package main
|
||||
|
||||
@ -11,22 +13,22 @@ import "time"
|
||||
// `done` channel will be used to notify another
|
||||
// goroutine that this function's work is done.
|
||||
func worker(done chan bool) {
|
||||
fmt.Print("working...")
|
||||
time.Sleep(time.Second)
|
||||
fmt.Println("done")
|
||||
fmt.Print("working...")
|
||||
time.Sleep(time.Second)
|
||||
fmt.Println("done")
|
||||
|
||||
// Send a value to notify that we're done.
|
||||
done <- true
|
||||
// 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)
|
||||
// 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
|
||||
// Block until we receive a notification from the
|
||||
// worker on the channel.
|
||||
<-done
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
fe3e2ea1a67d0f95ce4cb18f3e8aa16d416de0ce
|
||||
0DfW-1RMqi
|
||||
eb022977181884c2ab0f2b69e50311769e67a509
|
||||
8lmP8beav0p
|
||||
|
@ -9,18 +9,18 @@ import "fmt"
|
||||
|
||||
func main() {
|
||||
|
||||
// Create a new channel with `make(chan val-type)`.
|
||||
// Channels are typed by the values they convey.
|
||||
messages := make(chan string)
|
||||
// Create a new channel with `make(chan val-type)`.
|
||||
// Channels are typed by the values they convey.
|
||||
messages := make(chan string)
|
||||
|
||||
// _Send_ a value into a channel using the `channel <-`
|
||||
// syntax. Here we send `"ping"` to the `messages`
|
||||
// channel we made above, from a new goroutine.
|
||||
go func() { messages <- "ping" }()
|
||||
// _Send_ a value into a channel using the `channel <-`
|
||||
// syntax. Here we send `"ping"` to the `messages`
|
||||
// channel we made above, from a new goroutine.
|
||||
go func() { messages <- "ping" }()
|
||||
|
||||
// The `<-channel` syntax _receives_ a value from the
|
||||
// channel. Here we'll receive the `"ping"` message
|
||||
// we sent above and print it out.
|
||||
msg := <-messages
|
||||
fmt.Println(msg)
|
||||
// The `<-channel` syntax _receives_ a value from the
|
||||
// channel. Here we'll receive the `"ping"` message
|
||||
// we sent above and print it out.
|
||||
msg := <-messages
|
||||
fmt.Println(msg)
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
926212c784ab820648906c96f6ab21afbc161526
|
||||
Kd8B0T_JGK
|
||||
bRGMAqinovA
|
||||
|
@ -11,40 +11,40 @@ import "fmt"
|
||||
// to a worker goroutine. When we have no more jobs for
|
||||
// the worker we'll `close` the `jobs` channel.
|
||||
func main() {
|
||||
jobs := make(chan int, 5)
|
||||
done := make(chan bool)
|
||||
jobs := make(chan int, 5)
|
||||
done := make(chan bool)
|
||||
|
||||
// Here's the worker goroutine. It repeatedly receives
|
||||
// from `jobs` with `j, more := <-jobs`. In this
|
||||
// special 2-value form of receive, the `more` value
|
||||
// will be `false` if `jobs` has been `close`d and all
|
||||
// values in the channel have already been received.
|
||||
// We use this to notify on `done` when we've worked
|
||||
// all our jobs.
|
||||
go func() {
|
||||
for {
|
||||
j, more := <-jobs
|
||||
if more {
|
||||
fmt.Println("received job", j)
|
||||
} else {
|
||||
fmt.Println("received all jobs")
|
||||
done <- true
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
// Here's the worker goroutine. It repeatedly receives
|
||||
// from `jobs` with `j, more := <-jobs`. In this
|
||||
// special 2-value form of receive, the `more` value
|
||||
// will be `false` if `jobs` has been `close`d and all
|
||||
// values in the channel have already been received.
|
||||
// We use this to notify on `done` when we've worked
|
||||
// all our jobs.
|
||||
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 `jobs`
|
||||
// channel, then closes it.
|
||||
for j := 1; j <= 3; j++ {
|
||||
jobs <- j
|
||||
fmt.Println("sent job", j)
|
||||
}
|
||||
close(jobs)
|
||||
fmt.Println("sent all jobs")
|
||||
// This sends 3 jobs to the worker over the `jobs`
|
||||
// channel, then closes it.
|
||||
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](channel-synchronization) approach
|
||||
// we saw earlier.
|
||||
<-done
|
||||
// We await the worker using the
|
||||
// [synchronization](channel-synchronization) approach
|
||||
// we saw earlier.
|
||||
<-done
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
5205898a520533e46ea24c849848d19ebc2d08a9
|
||||
eFZ2SeKswH
|
||||
mkz69rVMHs6
|
||||
|
@ -12,29 +12,29 @@ import "fmt"
|
||||
// returned function _closes over_ the variable `i` to
|
||||
// form a closure.
|
||||
func intSeq() func() int {
|
||||
i := 0
|
||||
return func() int {
|
||||
i += 1
|
||||
return i
|
||||
}
|
||||
i := 0
|
||||
return func() int {
|
||||
i++
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
// We call `intSeq`, assigning the result (a function)
|
||||
// to `nextInt`. This function value captures its
|
||||
// own `i` value, which will be updated each time
|
||||
// we call `nextInt`.
|
||||
nextInt := intSeq()
|
||||
// We call `intSeq`, assigning the result (a function)
|
||||
// to `nextInt`. This function value captures its
|
||||
// own `i` value, which will be updated each time
|
||||
// we call `nextInt`.
|
||||
nextInt := intSeq()
|
||||
|
||||
// See the effect of the closure by calling `nextInt`
|
||||
// a few times.
|
||||
fmt.Println(nextInt())
|
||||
fmt.Println(nextInt())
|
||||
fmt.Println(nextInt())
|
||||
// See the effect of the closure by calling `nextInt`
|
||||
// a few times.
|
||||
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())
|
||||
// To confirm that the state is unique to that
|
||||
// particular function, create and test a new one.
|
||||
newInts := intSeq()
|
||||
fmt.Println(newInts())
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
2e062d01989caada16c4b22ff6a35cd58e4eb819
|
||||
gQtEWkhWyp
|
||||
e304df67e760dda93ffe434aca58aea4a6c94f19
|
||||
zb93qzV6iN3
|
||||
|
@ -21,91 +21,91 @@ package main
|
||||
import "strings"
|
||||
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.
|
||||
func Index(vs []string, t string) int {
|
||||
for i, v := range vs {
|
||||
if v == t {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
for i, v := range vs {
|
||||
if v == t {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Returns `true` if the target string t is in the
|
||||
// Include returns `true` if the target string t is in the
|
||||
// slice.
|
||||
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`.
|
||||
func Any(vs []string, f func(string) bool) bool {
|
||||
for _, v := range vs {
|
||||
if f(v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
for _, v := range vs {
|
||||
if f(v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
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`.
|
||||
func All(vs []string, f func(string) bool) bool {
|
||||
for _, v := range vs {
|
||||
if !f(v) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
for _, v := range vs {
|
||||
if !f(v) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
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`.
|
||||
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
|
||||
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
|
||||
// Map returns a new slice containing the results of applying
|
||||
// the function `f` to each string in the original slice.
|
||||
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
|
||||
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"}
|
||||
// Here we try out our various collection functions.
|
||||
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 {
|
||||
return strings.HasPrefix(v, "p")
|
||||
}))
|
||||
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(All(strs, func(v string) bool {
|
||||
return strings.HasPrefix(v, "p")
|
||||
}))
|
||||
|
||||
fmt.Println(Filter(strs, func(v string) bool {
|
||||
return strings.Contains(v, "e")
|
||||
}))
|
||||
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))
|
||||
// The above examples all used anonymous functions,
|
||||
// but you can also use named functions of the correct
|
||||
// type.
|
||||
fmt.Println(Map(strs, strings.ToUpper))
|
||||
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
ed54b3fc0512ccace0f3d0b74975c9bcd2e7a8a2
|
||||
3PNdke3Wia
|
||||
d961fc0e0074aed46cfd1516efdadea78781af56
|
||||
BJB_npWH516
|
||||
|
@ -10,17 +10,17 @@ import "fmt"
|
||||
|
||||
func main() {
|
||||
|
||||
// `os.Args` provides access to raw command-line
|
||||
// arguments. Note that the first value in this slice
|
||||
// is the path to the program, and `os.Args[1:]`
|
||||
// holds the arguments to the program.
|
||||
argsWithProg := os.Args
|
||||
argsWithoutProg := os.Args[1:]
|
||||
// `os.Args` provides access to raw command-line
|
||||
// arguments. Note that the first value in this slice
|
||||
// is the path to the program, and `os.Args[1:]`
|
||||
// holds the arguments to the program.
|
||||
argsWithProg := os.Args
|
||||
argsWithoutProg := os.Args[1:]
|
||||
|
||||
// You can get individual args with normal indexing.
|
||||
arg := os.Args[3]
|
||||
// You can get individual args with normal indexing.
|
||||
arg := os.Args[3]
|
||||
|
||||
fmt.Println(argsWithProg)
|
||||
fmt.Println(argsWithoutProg)
|
||||
fmt.Println(arg)
|
||||
fmt.Println(argsWithProg)
|
||||
fmt.Println(argsWithoutProg)
|
||||
fmt.Println(arg)
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
41c970a1ef29ad2a05307e6c783ff52ab80eaccd
|
||||
44uyYt_TRl
|
||||
6pFdjf800jj
|
||||
|
@ -13,37 +13,37 @@ import "fmt"
|
||||
|
||||
func main() {
|
||||
|
||||
// Basic flag declarations are available for string,
|
||||
// integer, and boolean options. Here we declare a
|
||||
// string flag `word` with a default value `"foo"`
|
||||
// and a short description. This `flag.String` function
|
||||
// returns a string pointer (not a string value);
|
||||
// we'll see how to use this pointer below.
|
||||
wordPtr := flag.String("word", "foo", "a string")
|
||||
// Basic flag declarations are available for string,
|
||||
// integer, and boolean options. Here we declare a
|
||||
// string flag `word` with a default value `"foo"`
|
||||
// and a short description. This `flag.String` function
|
||||
// returns a string pointer (not a string value);
|
||||
// we'll see how to use this pointer below.
|
||||
wordPtr := flag.String("word", "foo", "a string")
|
||||
|
||||
// This declares `numb` and `fork` flags, using a
|
||||
// similar approach to the `word` flag.
|
||||
numbPtr := flag.Int("numb", 42, "an int")
|
||||
boolPtr := flag.Bool("fork", false, "a bool")
|
||||
// This declares `numb` and `fork` flags, using a
|
||||
// similar approach to the `word` flag.
|
||||
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")
|
||||
// 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()`
|
||||
// to execute the command-line parsing.
|
||||
flag.Parse()
|
||||
// Once all flags are declared, call `flag.Parse()`
|
||||
// to execute the command-line parsing.
|
||||
flag.Parse()
|
||||
|
||||
// Here we'll just dump out the parsed options and
|
||||
// any trailing positional arguments. Note that we
|
||||
// need to dereference the pointers with e.g. `*wordPtr`
|
||||
// to get the actual option values.
|
||||
fmt.Println("word:", *wordPtr)
|
||||
fmt.Println("numb:", *numbPtr)
|
||||
fmt.Println("fork:", *boolPtr)
|
||||
fmt.Println("svar:", svar)
|
||||
fmt.Println("tail:", flag.Args())
|
||||
// Here we'll just dump out the parsed options and
|
||||
// any trailing positional arguments. Note that we
|
||||
// need to dereference the pointers with e.g. `*wordPtr`
|
||||
// to get the actual option values.
|
||||
fmt.Println("word:", *wordPtr)
|
||||
fmt.Println("numb:", *numbPtr)
|
||||
fmt.Println("fork:", *boolPtr)
|
||||
fmt.Println("svar:", svar)
|
||||
fmt.Println("tail:", flag.Args())
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
e2ba0461c090789168c712cc7ed0f66aab09a8c8
|
||||
NASEOq2R3n
|
||||
klFR5DitrCy
|
||||
|
@ -54,6 +54,3 @@ $ ./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.
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
5a0ec258e4992e9b93b11d48f2f249092ff3db66
|
||||
gtgSAg76N4I
|
@ -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.
|
@ -10,24 +10,24 @@ import "math"
|
||||
const s string = "constant"
|
||||
|
||||
func main() {
|
||||
fmt.Println(s)
|
||||
fmt.Println(s)
|
||||
|
||||
// A `const` statement can appear anywhere a `var`
|
||||
// statement can.
|
||||
const n = 500000000
|
||||
// A `const` statement can appear anywhere a `var`
|
||||
// statement can.
|
||||
const n = 500000000
|
||||
|
||||
// Constant expressions perform arithmetic with
|
||||
// arbitrary precision.
|
||||
const d = 3e20 / n
|
||||
fmt.Println(d)
|
||||
// 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 numeric constant has no type until it's given
|
||||
// one, such as by an explicit conversion.
|
||||
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
|
||||
// `math.Sin` expects a `float64`.
|
||||
fmt.Println(math.Sin(n))
|
||||
// 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
|
||||
// `math.Sin` expects a `float64`.
|
||||
fmt.Println(math.Sin(n))
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
3de4f16f1ed032378268411b2173b95e8000305d
|
||||
T5sj0eINnp
|
||||
2f2ec3a5ff4eef280199da1908eed261346fb40e
|
||||
VhP0f8moZd3
|
||||
|
@ -13,32 +13,38 @@ import "os"
|
||||
// do that with `defer`.
|
||||
func main() {
|
||||
|
||||
// Immediately after getting a file object with
|
||||
// `createFile`, we defer the closing of that file
|
||||
// with `closeFile`. This will be executed at the end
|
||||
// of the enclosing function (`main`), after
|
||||
// `writeFile` has finished.
|
||||
f := createFile("/tmp/defer.txt")
|
||||
defer closeFile(f)
|
||||
writeFile(f)
|
||||
// Immediately after getting a file object with
|
||||
// `createFile`, we defer the closing of that file
|
||||
// with `closeFile`. This will be executed at the end
|
||||
// of the enclosing function (`main`), after
|
||||
// `writeFile` has finished.
|
||||
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
|
||||
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")
|
||||
fmt.Println("writing")
|
||||
fmt.Fprintln(f, "data")
|
||||
|
||||
}
|
||||
|
||||
func closeFile(f *os.File) {
|
||||
fmt.Println("closing")
|
||||
f.Close()
|
||||
fmt.Println("closing")
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
570699fc50a1d39e9d0ad6a4461aef3248b080e1
|
||||
9aoHwzHcAo
|
||||
fadbe9c05bb42db672316ba19adf3c2189c7b3f5
|
||||
OrCaBiCrTKq
|
||||
|
95
examples/directories/directories.go
Normal file
95
examples/directories/directories.go
Normal 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
|
||||
}
|
2
examples/directories/directories.hash
Normal file
2
examples/directories/directories.hash
Normal file
@ -0,0 +1,2 @@
|
||||
83f67db91816b4544072d0a4d099111a21c60723
|
||||
-7kWq0PmATF
|
15
examples/directories/directories.sh
Normal file
15
examples/directories/directories.sh
Normal 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
|
@ -11,21 +11,21 @@ import "fmt"
|
||||
|
||||
func main() {
|
||||
|
||||
// To set a key/value pair, use `os.Setenv`. To get a
|
||||
// value for a key, use `os.Getenv`. This will return
|
||||
// an empty string if the key isn't present in the
|
||||
// environment.
|
||||
os.Setenv("FOO", "1")
|
||||
fmt.Println("FOO:", os.Getenv("FOO"))
|
||||
fmt.Println("BAR:", os.Getenv("BAR"))
|
||||
// To set a key/value pair, use `os.Setenv`. To get a
|
||||
// value for a key, use `os.Getenv`. This will return
|
||||
// an empty string if the key isn't present in the
|
||||
// environment.
|
||||
os.Setenv("FOO", "1")
|
||||
fmt.Println("FOO:", os.Getenv("FOO"))
|
||||
fmt.Println("BAR:", os.Getenv("BAR"))
|
||||
|
||||
// Use `os.Environ` to list all key/value pairs in the
|
||||
// environment. This returns a slice of strings in the
|
||||
// form `KEY=value`. You can `strings.Split` them to
|
||||
// get the key and value. Here we print all the keys.
|
||||
fmt.Println()
|
||||
for _, e := range os.Environ() {
|
||||
pair := strings.Split(e, "=")
|
||||
fmt.Println(pair[0])
|
||||
}
|
||||
// Use `os.Environ` to list all key/value pairs in the
|
||||
// environment. This returns a slice of strings in the
|
||||
// form `KEY=value`. You can `strings.Split` them to
|
||||
// get the key and value. Here we print all the keys.
|
||||
fmt.Println()
|
||||
for _, e := range os.Environ() {
|
||||
pair := strings.Split(e, "=")
|
||||
fmt.Println(pair[0])
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
4d0832c5a1ddd4e95474791e8802c15452358214
|
||||
kfqLhpmEpw
|
||||
CZJ4R_uu6Uu
|
||||
|
@ -10,24 +10,24 @@ import "time"
|
||||
|
||||
func main() {
|
||||
|
||||
// Use `time.Now` with `Unix` or `UnixNano` to get
|
||||
// elapsed time since the Unix epoch in seconds or
|
||||
// nanoseconds, respectively.
|
||||
now := time.Now()
|
||||
secs := now.Unix()
|
||||
nanos := now.UnixNano()
|
||||
fmt.Println(now)
|
||||
// Use `time.Now` with `Unix` or `UnixNano` to get
|
||||
// elapsed time since the Unix epoch in seconds or
|
||||
// nanoseconds, respectively.
|
||||
now := time.Now()
|
||||
secs := now.Unix()
|
||||
nanos := now.UnixNano()
|
||||
fmt.Println(now)
|
||||
|
||||
// Note that there is no `UnixMillis`, so to get the
|
||||
// milliseconds since epoch you'll need to manually
|
||||
// divide from nanoseconds.
|
||||
millis := nanos / 1000000
|
||||
fmt.Println(secs)
|
||||
fmt.Println(millis)
|
||||
fmt.Println(nanos)
|
||||
// Note that there is no `UnixMillis`, so to get the
|
||||
// milliseconds since epoch you'll need to manually
|
||||
// divide from nanoseconds.
|
||||
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 `time`.
|
||||
fmt.Println(time.Unix(secs, 0))
|
||||
fmt.Println(time.Unix(0, nanos))
|
||||
// You can also convert integer seconds or nanoseconds
|
||||
// since the epoch into the corresponding `time`.
|
||||
fmt.Println(time.Unix(secs, 0))
|
||||
fmt.Println(time.Unix(0, nanos))
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
61a498229c8878a97d729cfdd215e5f3960f87ac
|
||||
GP_zEjhlWk
|
||||
eN1Qv2ATB-C
|
||||
|
@ -15,17 +15,17 @@ import "fmt"
|
||||
// By convention, errors are the last return value and
|
||||
// have type `error`, a built-in interface.
|
||||
func f1(arg int) (int, error) {
|
||||
if arg == 42 {
|
||||
if arg == 42 {
|
||||
|
||||
// `errors.New` constructs a basic `error` value
|
||||
// with the given error message.
|
||||
return -1, errors.New("can't work with 42")
|
||||
// `errors.New` constructs a basic `error` value
|
||||
// with the given error message.
|
||||
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
|
||||
// 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 `error`s by
|
||||
@ -33,53 +33,53 @@ func f1(arg int) (int, error) {
|
||||
// variant on the example above that uses a custom type
|
||||
// to explicitly represent an argument error.
|
||||
type argError struct {
|
||||
arg int
|
||||
prob string
|
||||
arg int
|
||||
prob 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) {
|
||||
if arg == 42 {
|
||||
if arg == 42 {
|
||||
|
||||
// In this case we use `&argError` syntax to build
|
||||
// a new struct, supplying values for the two
|
||||
// fields `arg` and `prob`.
|
||||
return -1, &argError{arg, "can't work with it"}
|
||||
}
|
||||
return arg + 3, nil
|
||||
// In this case we use `&argError` syntax to build
|
||||
// a new struct, supplying values for the two
|
||||
// fields `arg` and `prob`.
|
||||
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 `if` line is a common
|
||||
// idiom in Go code.
|
||||
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)
|
||||
}
|
||||
}
|
||||
// The two loops below test out each of our
|
||||
// error-returning functions. Note that the use of an
|
||||
// inline error check on the `if` line is a common
|
||||
// idiom in Go code.
|
||||
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)
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
07cffb3d4e37162ab7e9e0a192561ddc8042b81a
|
||||
BmDQXkPPTk
|
||||
210ba0f8196006c0380acaec01655816848ef168
|
||||
mP_ZR1qjUvA
|
||||
|
@ -16,33 +16,33 @@ import "os/exec"
|
||||
|
||||
func main() {
|
||||
|
||||
// For our example we'll exec `ls`. Go requires an
|
||||
// absolute path to the binary we want to execute, so
|
||||
// we'll use `exec.LookPath` to find it (probably
|
||||
// `/bin/ls`).
|
||||
binary, lookErr := exec.LookPath("ls")
|
||||
if lookErr != nil {
|
||||
panic(lookErr)
|
||||
}
|
||||
// For our example we'll exec `ls`. Go requires an
|
||||
// absolute path to the binary we want to execute, so
|
||||
// we'll use `exec.LookPath` to find it (probably
|
||||
// `/bin/ls`).
|
||||
binary, lookErr := exec.LookPath("ls")
|
||||
if lookErr != nil {
|
||||
panic(lookErr)
|
||||
}
|
||||
|
||||
// `Exec` requires arguments in slice form (as
|
||||
// apposed to one big string). We'll give `ls` a few
|
||||
// common arguments. Note that the first argument should
|
||||
// be the program name.
|
||||
args := []string{"ls", "-a", "-l", "-h"}
|
||||
// `Exec` requires arguments in slice form (as
|
||||
// apposed to one big string). We'll give `ls` a few
|
||||
// common arguments. Note that the first argument should
|
||||
// be the program name.
|
||||
args := []string{"ls", "-a", "-l", "-h"}
|
||||
|
||||
// `Exec` also needs a set of [environment variables](environment-variables)
|
||||
// to use. Here we just provide our current
|
||||
// environment.
|
||||
env := os.Environ()
|
||||
// `Exec` also needs a set of [environment variables](environment-variables)
|
||||
// to use. Here we just provide our current
|
||||
// environment.
|
||||
env := os.Environ()
|
||||
|
||||
// Here's the actual `syscall.Exec` call. If this call is
|
||||
// successful, the execution of our process will end
|
||||
// here and be replaced by the `/bin/ls -a -l -h`
|
||||
// process. If there is an error we'll get a return
|
||||
// value.
|
||||
execErr := syscall.Exec(binary, args, env)
|
||||
if execErr != nil {
|
||||
panic(execErr)
|
||||
}
|
||||
// Here's the actual `syscall.Exec` call. If this call is
|
||||
// successful, the execution of our process will end
|
||||
// here and be replaced by the `/bin/ls -a -l -h`
|
||||
// process. If there is an error we'll get a return
|
||||
// value.
|
||||
execErr := syscall.Exec(binary, args, env)
|
||||
if execErr != nil {
|
||||
panic(execErr)
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
b527bbb76a42dd4bae541b73a7377b7e83e79905
|
||||
neqdJ51KLN
|
||||
bf11ADw-2Ho
|
||||
|
@ -8,12 +8,12 @@ import "os"
|
||||
|
||||
func main() {
|
||||
|
||||
// `defer`s will _not_ be run when using `os.Exit`, so
|
||||
// this `fmt.Println` will never be called.
|
||||
defer fmt.Println("!")
|
||||
// `defer`s will _not_ be run when using `os.Exit`, so
|
||||
// this `fmt.Println` will never be called.
|
||||
defer fmt.Println("!")
|
||||
|
||||
// Exit with status 3.
|
||||
os.Exit(3)
|
||||
// Exit with status 3.
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
// Note that unlike e.g. C, Go does not use an integer
|
||||
|
@ -1,2 +1,2 @@
|
||||
dc0bb3eaafa045d6aa05e88aff39322a1ccf822e
|
||||
CDiAh9SXRM
|
||||
vDaM0-MGJ_k
|
||||
|
64
examples/file-paths/file-paths.go
Normal file
64
examples/file-paths/file-paths.go
Normal 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)
|
||||
}
|
2
examples/file-paths/file-paths.hash
Normal file
2
examples/file-paths/file-paths.hash
Normal file
@ -0,0 +1,2 @@
|
||||
1215302b9e59ee9dee21dcd3c47d5f6c672fb058
|
||||
QIitbMNiFRx
|
12
examples/file-paths/file-paths.sh
Normal file
12
examples/file-paths/file-paths.sh
Normal 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
|
@ -7,32 +7,32 @@ import "fmt"
|
||||
|
||||
func main() {
|
||||
|
||||
// The most basic type, with a single condition.
|
||||
i := 1
|
||||
for i <= 3 {
|
||||
fmt.Println(i)
|
||||
i = i + 1
|
||||
}
|
||||
// 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` loop.
|
||||
for j := 7; j <= 9; j++ {
|
||||
fmt.Println(j)
|
||||
}
|
||||
// A classic initial/condition/after `for` loop.
|
||||
for j := 7; j <= 9; j++ {
|
||||
fmt.Println(j)
|
||||
}
|
||||
|
||||
// `for` without a condition will loop repeatedly
|
||||
// until you `break` out of the loop or `return` from
|
||||
// the enclosing function.
|
||||
for {
|
||||
fmt.Println("loop")
|
||||
break
|
||||
}
|
||||
// `for` without a condition will loop repeatedly
|
||||
// until you `break` out of the loop or `return` from
|
||||
// the enclosing function.
|
||||
for {
|
||||
fmt.Println("loop")
|
||||
break
|
||||
}
|
||||
|
||||
// You can also `continue` to the next iteration of
|
||||
// the loop.
|
||||
for n := 0; n <= 5; n++ {
|
||||
if n%2 == 0 {
|
||||
continue
|
||||
}
|
||||
fmt.Println(n)
|
||||
}
|
||||
// You can also `continue` to the next iteration of
|
||||
// the loop.
|
||||
for n := 0; n <= 5; n++ {
|
||||
if n%2 == 0 {
|
||||
continue
|
||||
}
|
||||
fmt.Println(n)
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
33056d6b36f9894fb6359c9cf2ef8725bbdafa19
|
||||
KNLLSX4Io_
|
||||
lGYfUJwiGfi
|
||||
|
@ -9,10 +9,10 @@ import "fmt"
|
||||
// their sum as an `int`.
|
||||
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
|
||||
// Go requires explicit returns, i.e. it won't
|
||||
// automatically return the value of the last
|
||||
// expression.
|
||||
return a + b
|
||||
}
|
||||
|
||||
// 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
|
||||
// declares the type.
|
||||
func plusPlus(a, b, c int) int {
|
||||
return a + b + c
|
||||
return a + b + c
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
// Call a function just as you'd expect, with
|
||||
// `name(args)`.
|
||||
res := plus(1, 2)
|
||||
fmt.Println("1+2 =", res)
|
||||
// Call a function just as you'd expect, with
|
||||
// `name(args)`.
|
||||
res := plus(1, 2)
|
||||
fmt.Println("1+2 =", res)
|
||||
|
||||
res = plusPlus(1, 2, 3)
|
||||
fmt.Println("1+2+3 =", res)
|
||||
res = plusPlus(1, 2, 3)
|
||||
fmt.Println("1+2+3 =", res)
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
ae669923c20e5ebf4a7b4b11b8fdf2972accf9e2
|
||||
9Nky-Dn49f
|
||||
hzGUvK6iJNm
|
||||
|
@ -5,34 +5,33 @@ package main
|
||||
import "fmt"
|
||||
|
||||
func f(from string) {
|
||||
for i := 0; i < 3; i++ {
|
||||
fmt.Println(from, ":", i)
|
||||
}
|
||||
for i := 0; i < 3; i++ {
|
||||
fmt.Println(from, ":", i)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
// Suppose we have a function call `f(s)`. Here's how
|
||||
// we'd call that in the usual way, running it
|
||||
// synchronously.
|
||||
f("direct")
|
||||
// Suppose we have a function call `f(s)`. Here's how
|
||||
// we'd call that in the usual way, running it
|
||||
// synchronously.
|
||||
f("direct")
|
||||
|
||||
// To invoke this function in a goroutine, use
|
||||
// `go f(s)`. This new goroutine will execute
|
||||
// concurrently with the calling one.
|
||||
go f("goroutine")
|
||||
// To invoke this function in a goroutine, use
|
||||
// `go f(s)`. This new goroutine will execute
|
||||
// concurrently with the calling one.
|
||||
go f("goroutine")
|
||||
|
||||
// You can also start a goroutine for an anonymous
|
||||
// function call.
|
||||
go func(msg string) {
|
||||
fmt.Println(msg)
|
||||
}("going")
|
||||
// You can also start a goroutine for an anonymous
|
||||
// function call.
|
||||
go func(msg string) {
|
||||
fmt.Println(msg)
|
||||
}("going")
|
||||
|
||||
// Our two function calls are running asynchronously in
|
||||
// separate goroutines now, so execution falls through
|
||||
// to here. This `Scanln` code requires we press a key
|
||||
// before the program exits.
|
||||
var input string
|
||||
fmt.Scanln(&input)
|
||||
fmt.Println("done")
|
||||
// Our two function calls are running asynchronously in
|
||||
// separate goroutines now, so execution falls through
|
||||
// to here. This `Scanln` requires we press a key
|
||||
// before the program exits.
|
||||
fmt.Scanln()
|
||||
fmt.Println("done")
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
a847131d7f112172f9d5509fd3cf31aefb6d710e
|
||||
RW_RSAHfj-
|
||||
bfdaa0c8104c1257e6fea102fd26d476a3e8c14e
|
||||
6Y8t3Yxd1LD
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 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
|
||||
# two goroutines. This interleaving reflects the
|
||||
# goroutines being run concurrently by the Go runtime.
|
||||
$ go run goroutines.go
|
||||
direct : 0
|
||||
|
@ -5,5 +5,5 @@ package main
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("hello world")
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
c98395a44701add5bf84e2f3a63e300fc1bc4bfe
|
||||
2C7wwJ6nxG
|
||||
mp1ENMU6ZYu
|
||||
|
38
examples/http-clients/http-clients.go
Normal file
38
examples/http-clients/http-clients.go
Normal 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)
|
||||
}
|
||||
}
|
2
examples/http-clients/http-clients.hash
Normal file
2
examples/http-clients/http-clients.hash
Normal file
@ -0,0 +1,2 @@
|
||||
ec8fd69aa19e54a7ea05d2a911f09d3a98f0396c
|
||||
VxYIifr_UuH
|
7
examples/http-clients/http-clients.sh
Normal file
7
examples/http-clients/http-clients.sh
Normal 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>
|
50
examples/http-servers/http-servers.go
Normal file
50
examples/http-servers/http-servers.go
Normal 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)
|
||||
}
|
2
examples/http-servers/http-servers.hash
Normal file
2
examples/http-servers/http-servers.hash
Normal file
@ -0,0 +1,2 @@
|
||||
a4e8d30b7a6f3a6abd96b916d81ce5930bad94f9
|
||||
lNuS9ysZmxH
|
6
examples/http-servers/http-servers.sh
Normal file
6
examples/http-servers/http-servers.sh
Normal 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
|
@ -7,28 +7,28 @@ import "fmt"
|
||||
|
||||
func main() {
|
||||
|
||||
// Here's a basic example.
|
||||
if 7%2 == 0 {
|
||||
fmt.Println("7 is even")
|
||||
} else {
|
||||
fmt.Println("7 is odd")
|
||||
}
|
||||
// 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` statement without an else.
|
||||
if 8%4 == 0 {
|
||||
fmt.Println("8 is divisible by 4")
|
||||
}
|
||||
// You can have an `if` statement without an else.
|
||||
if 8%4 == 0 {
|
||||
fmt.Println("8 is divisible by 4")
|
||||
}
|
||||
|
||||
// A statement can precede 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")
|
||||
}
|
||||
// A statement can precede 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
|
||||
|
@ -1,2 +1,2 @@
|
||||
89b78f3378e1a574ddfd0260a0404a962852eff8
|
||||
g-aqMz0Ivf
|
||||
p6-WKTqEks4
|
||||
|
@ -8,35 +8,35 @@ import "math"
|
||||
|
||||
// Here's a basic interface for geometric shapes.
|
||||
type geometry interface {
|
||||
area() float64
|
||||
perim() float64
|
||||
area() float64
|
||||
perim() float64
|
||||
}
|
||||
|
||||
// For our example we'll implement this interface on
|
||||
// `rect` and `circle` types.
|
||||
type rect struct {
|
||||
width, height float64
|
||||
width, height float64
|
||||
}
|
||||
type circle struct {
|
||||
radius float64
|
||||
radius float64
|
||||
}
|
||||
|
||||
// To implement an interface in Go, we just need to
|
||||
// implement all the methods in the interface. Here we
|
||||
// implement `geometry` on `rect`s.
|
||||
func (r rect) area() float64 {
|
||||
return r.width * r.height
|
||||
return r.width * r.height
|
||||
}
|
||||
func (r rect) perim() float64 {
|
||||
return 2*r.width + 2*r.height
|
||||
return 2*r.width + 2*r.height
|
||||
}
|
||||
|
||||
// The implementation for `circle`s.
|
||||
func (c circle) area() float64 {
|
||||
return math.Pi * c.radius * c.radius
|
||||
return math.Pi * c.radius * c.radius
|
||||
}
|
||||
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
|
||||
@ -44,19 +44,19 @@ func (c circle) perim() float64 {
|
||||
// generic `measure` function taking advantage of this
|
||||
// to work on any `geometry`.
|
||||
func measure(g geometry) {
|
||||
fmt.Println(g)
|
||||
fmt.Println(g.area())
|
||||
fmt.Println(g.perim())
|
||||
fmt.Println(g)
|
||||
fmt.Println(g.area())
|
||||
fmt.Println(g.perim())
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := rect{width: 3, height: 4}
|
||||
c := circle{radius: 5}
|
||||
r := rect{width: 3, height: 4}
|
||||
c := circle{radius: 5}
|
||||
|
||||
// The `circle` and `rect` struct types both
|
||||
// implement the `geometry` interface so we can use
|
||||
// instances of
|
||||
// these structs as arguments to `measure`.
|
||||
measure(r)
|
||||
measure(c)
|
||||
// The `circle` and `rect` struct types both
|
||||
// implement the `geometry` interface so we can use
|
||||
// instances of
|
||||
// these structs as arguments to `measure`.
|
||||
measure(r)
|
||||
measure(c)
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
3547b935d1e0322c0fb696726c27cae53a275e0a
|
||||
313UebA3rD
|
||||
0EwsqIn3TTi
|
||||
|
@ -10,110 +10,110 @@ 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 response1 struct {
|
||||
Page int
|
||||
Fruits []string
|
||||
}
|
||||
type Response2 struct {
|
||||
Page int `json:"page"`
|
||||
Fruits []string `json:"fruits"`
|
||||
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))
|
||||
// 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))
|
||||
intB, _ := json.Marshal(1)
|
||||
fmt.Println(string(intB))
|
||||
|
||||
fltB, _ := json.Marshal(2.34)
|
||||
fmt.Println(string(fltB))
|
||||
fltB, _ := json.Marshal(2.34)
|
||||
fmt.Println(string(fltB))
|
||||
|
||||
strB, _ := json.Marshal("gopher")
|
||||
fmt.Println(string(strB))
|
||||
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))
|
||||
// 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))
|
||||
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))
|
||||
// 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 `Response2` above to see an example
|
||||
// of such tags.
|
||||
res2D := &Response2{
|
||||
Page: 1,
|
||||
Fruits: []string{"apple", "peach", "pear"}}
|
||||
res2B, _ := json.Marshal(res2D)
|
||||
fmt.Println(string(res2B))
|
||||
// You can use tags on struct field declarations
|
||||
// to customize the encoded JSON key names. Check the
|
||||
// definition of `response2` above to see an example
|
||||
// of such tags.
|
||||
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.13,"strs":["a","b"]}`)
|
||||
// Now let's look at decoding JSON data into Go
|
||||
// values. Here's an example for a generic data
|
||||
// structure.
|
||||
byt := []byte(`{"num":6.13,"strs":["a","b"]}`)
|
||||
|
||||
// We need to provide a variable where the JSON
|
||||
// package can put the decoded data. This
|
||||
// `map[string]interface{}` will hold a map of strings
|
||||
// to arbitrary data types.
|
||||
var dat map[string]interface{}
|
||||
// We need to provide a variable where the JSON
|
||||
// package can put the decoded data. This
|
||||
// `map[string]interface{}` will hold a map of strings
|
||||
// to arbitrary data types.
|
||||
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)
|
||||
// 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` to
|
||||
// the expected `float64` type.
|
||||
num := dat["num"].(float64)
|
||||
fmt.Println(num)
|
||||
// In order to use the values in the decoded map,
|
||||
// we'll need to convert them to their appropriate type.
|
||||
// For example here we convert the value in `num` to
|
||||
// the expected `float64` type.
|
||||
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)
|
||||
// Accessing nested data requires a series of
|
||||
// conversions.
|
||||
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])
|
||||
// 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 `os.Writer`s like
|
||||
// `os.Stdout` or even HTTP response bodies.
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
d := map[string]int{"apple": 5, "lettuce": 7}
|
||||
enc.Encode(d)
|
||||
// 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 `os.Writer`s like
|
||||
// `os.Stdout` or even HTTP response bodies.
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
d := map[string]int{"apple": 5, "lettuce": 7}
|
||||
enc.Encode(d)
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
dee52e022a957b97c53fb2d2835653ef507502be
|
||||
WxRgpycMaH
|
||||
d4dc2281f64061f077d8f1e9687538f41a339b25
|
||||
xC6SHbzGBZC
|
||||
|
@ -9,33 +9,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// Wrapping the unbuffered `os.Stdin` with a buffered
|
||||
// scanner gives us a convenient `Scan` method that
|
||||
// advances the scanner to the next token; which is
|
||||
// the next line in the default scanner.
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
// Wrapping the unbuffered `os.Stdin` with a buffered
|
||||
// scanner gives us a convenient `Scan` method that
|
||||
// advances the scanner to the next token; which is
|
||||
// the next line in the default scanner.
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
|
||||
for scanner.Scan() {
|
||||
// `Text` returns the current token, here the next line,
|
||||
// from the input.
|
||||
ucl := strings.ToUpper(scanner.Text())
|
||||
for scanner.Scan() {
|
||||
// `Text` returns the current token, here the next line,
|
||||
// from the input.
|
||||
ucl := strings.ToUpper(scanner.Text())
|
||||
|
||||
// Write out the uppercased line.
|
||||
fmt.Println(ucl)
|
||||
}
|
||||
// Write out the uppercased line.
|
||||
fmt.Println(ucl)
|
||||
}
|
||||
|
||||
// Check for errors during `Scan`. End of file is
|
||||
// expected and not reported by `Scan` as an error.
|
||||
if err := scanner.Err(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "error:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
// Check for errors during `Scan`. End of file is
|
||||
// expected and not reported by `Scan` as an error.
|
||||
if err := scanner.Err(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "error:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
87f4a67edf741979f8ff6da85947aa177547f9ef
|
||||
mpYwOHj2ma
|
||||
hnaOIaQAjKF
|
||||
|
@ -7,44 +7,44 @@ import "fmt"
|
||||
|
||||
func main() {
|
||||
|
||||
// To create an empty map, use the builtin `make`:
|
||||
// `make(map[key-type]val-type)`.
|
||||
m := make(map[string]int)
|
||||
// To create an empty map, use the builtin `make`:
|
||||
// `make(map[key-type]val-type)`.
|
||||
m := make(map[string]int)
|
||||
|
||||
// Set key/value pairs using typical `name[key] = val`
|
||||
// syntax.
|
||||
m["k1"] = 7
|
||||
m["k2"] = 13
|
||||
// Set key/value pairs using typical `name[key] = val`
|
||||
// syntax.
|
||||
m["k1"] = 7
|
||||
m["k2"] = 13
|
||||
|
||||
// Printing a map with e.g. `Println` will show all of
|
||||
// its key/value pairs.
|
||||
fmt.Println("map:", m)
|
||||
// Printing a map with e.g. `fmt.Println` will show all of
|
||||
// its key/value pairs.
|
||||
fmt.Println("map:", m)
|
||||
|
||||
// Get a value for a key with `name[key]`.
|
||||
v1 := m["k1"]
|
||||
fmt.Println("v1: ", v1)
|
||||
// Get a value for a key with `name[key]`.
|
||||
v1 := m["k1"]
|
||||
fmt.Println("v1: ", v1)
|
||||
|
||||
// The builtin `len` returns the number of key/value
|
||||
// pairs when called on a map.
|
||||
fmt.Println("len:", len(m))
|
||||
// The builtin `len` returns the number of key/value
|
||||
// pairs when called on a map.
|
||||
fmt.Println("len:", len(m))
|
||||
|
||||
// The builtin `delete` removes key/value pairs from
|
||||
// a map.
|
||||
delete(m, "k2")
|
||||
fmt.Println("map:", m)
|
||||
// The builtin `delete` removes key/value pairs from
|
||||
// a map.
|
||||
delete(m, "k2")
|
||||
fmt.Println("map:", m)
|
||||
|
||||
// The optional second return value when getting a
|
||||
// value from a map indicates if the key was present
|
||||
// in the map. This can be used to disambiguate
|
||||
// between missing keys and keys with zero values
|
||||
// like `0` or `""`. Here we didn't need the value
|
||||
// itself, so we ignored it with the _blank identifier_
|
||||
// `_`.
|
||||
_, prs := m["k2"]
|
||||
fmt.Println("prs:", prs)
|
||||
// The optional second return value when getting a
|
||||
// value from a map indicates if the key was present
|
||||
// in the map. This can be used to disambiguate
|
||||
// between missing keys and keys with zero values
|
||||
// like `0` or `""`. Here we didn't need the value
|
||||
// itself, so we ignored it with the _blank identifier_
|
||||
// `_`.
|
||||
_, 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)
|
||||
// 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)
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
2895d63b87f88ab374256c12dd1539cf7b070b77
|
||||
E6cGoiKqka
|
||||
3e39d07e3f80ecbac558c6fb8baee2a5f914cf97
|
||||
U67R66Oab8r
|
||||
|
@ -6,4 +6,4 @@ v1: 7
|
||||
len: 2
|
||||
map: map[k1:7]
|
||||
prs: false
|
||||
map: map[foo:1 bar:2]
|
||||
map: map[bar:2 foo:1]
|
||||
|
@ -5,33 +5,33 @@ package main
|
||||
import "fmt"
|
||||
|
||||
type rect struct {
|
||||
width, height int
|
||||
width, height int
|
||||
}
|
||||
|
||||
// This `area` method has a _receiver type_ of `*rect`.
|
||||
func (r *rect) area() int {
|
||||
return r.width * r.height
|
||||
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
|
||||
return 2*r.width + 2*r.height
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := rect{width: 10, height: 5}
|
||||
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())
|
||||
// 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 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())
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
24cfb9ad45e43c2d49163149bc55925a4e1b3c7a
|
||||
254m_9Yjwa
|
||||
ffMb0txGnYB
|
||||
|
@ -9,19 +9,19 @@ import "fmt"
|
||||
// The `(int, int)` in this function signature shows that
|
||||
// the function returns 2 `int`s.
|
||||
func vals() (int, int) {
|
||||
return 3, 7
|
||||
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)
|
||||
// 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)
|
||||
// If you only want a subset of the returned values,
|
||||
// use the blank identifier `_`.
|
||||
_, c := vals()
|
||||
fmt.Println(c)
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
5063ce3d3c70c6bd70f4b709de24bb93d0f24e0c
|
||||
chwFmr5dG1
|
||||
FZoIB5LXQGZ
|
||||
|
@ -1,85 +1,85 @@
|
||||
// In the previous example we saw how to manage simple
|
||||
// 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.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// For our example the `state` will be a map.
|
||||
var state = make(map[int]int)
|
||||
// For our example the `state` will be a map.
|
||||
var state = make(map[int]int)
|
||||
|
||||
// This `mutex` will synchronize access to `state`.
|
||||
var mutex = &sync.Mutex{}
|
||||
// This `mutex` will synchronize access to `state`.
|
||||
var mutex = &sync.Mutex{}
|
||||
|
||||
// We'll keep track of how many read and write
|
||||
// operations we do.
|
||||
var readOps uint64 = 0
|
||||
var writeOps uint64 = 0
|
||||
// We'll keep track of how many read and write
|
||||
// operations we do.
|
||||
var readOps uint64
|
||||
var writeOps uint64
|
||||
|
||||
// Here we start 100 goroutines to execute repeated
|
||||
// reads against the state, once per millisecond in
|
||||
// each goroutine.
|
||||
for r := 0; r < 100; r++ {
|
||||
go func() {
|
||||
total := 0
|
||||
for {
|
||||
// Here we start 100 goroutines to execute repeated
|
||||
// reads against the state, once per millisecond in
|
||||
// each goroutine.
|
||||
for r := 0; r < 100; r++ {
|
||||
go func() {
|
||||
total := 0
|
||||
for {
|
||||
|
||||
// For each read we pick a key to access,
|
||||
// `Lock()` the `mutex` to ensure
|
||||
// exclusive access to the `state`, read
|
||||
// the value at the chosen key,
|
||||
// `Unlock()` the mutex, and increment
|
||||
// the `readOps` count.
|
||||
key := rand.Intn(5)
|
||||
mutex.Lock()
|
||||
total += state[key]
|
||||
mutex.Unlock()
|
||||
atomic.AddUint64(&readOps, 1)
|
||||
// For each read we pick a key to access,
|
||||
// `Lock()` the `mutex` to ensure
|
||||
// exclusive access to the `state`, read
|
||||
// the value at the chosen key,
|
||||
// `Unlock()` the mutex, and increment
|
||||
// the `readOps` count.
|
||||
key := rand.Intn(5)
|
||||
mutex.Lock()
|
||||
total += state[key]
|
||||
mutex.Unlock()
|
||||
atomic.AddUint64(&readOps, 1)
|
||||
|
||||
// Wait a bit between reads.
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
}()
|
||||
}
|
||||
// Wait a bit between reads.
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// 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.AddUint64(&writeOps, 1)
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
}()
|
||||
}
|
||||
// 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.AddUint64(&writeOps, 1)
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Let the 10 goroutines work on the `state` and
|
||||
// `mutex` for a second.
|
||||
time.Sleep(time.Second)
|
||||
// Let the 10 goroutines work on the `state` and
|
||||
// `mutex` for a second.
|
||||
time.Sleep(time.Second)
|
||||
|
||||
// Take and report final operation counts.
|
||||
readOpsFinal := atomic.LoadUint64(&readOps)
|
||||
fmt.Println("readOps:", readOpsFinal)
|
||||
writeOpsFinal := atomic.LoadUint64(&writeOps)
|
||||
fmt.Println("writeOps:", writeOpsFinal)
|
||||
// Take and report final operation counts.
|
||||
readOpsFinal := atomic.LoadUint64(&readOps)
|
||||
fmt.Println("readOps:", readOpsFinal)
|
||||
writeOpsFinal := atomic.LoadUint64(&writeOps)
|
||||
fmt.Println("writeOps:", writeOpsFinal)
|
||||
|
||||
// With a final lock of `state`, show how it ended up.
|
||||
mutex.Lock()
|
||||
fmt.Println("state:", state)
|
||||
mutex.Unlock()
|
||||
// With a final lock of `state`, show how it ended up.
|
||||
mutex.Lock()
|
||||
fmt.Println("state:", state)
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
e82356cbb37143862b0a9bbc68856f4b272c4918
|
||||
a9Wky7k-Bw
|
||||
ca257d9594a6219d5803193132e999a32dc8c856
|
||||
IRewFKz2OPN
|
||||
|
@ -8,39 +8,42 @@ package main
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
messages := make(chan string)
|
||||
signals := make(chan bool)
|
||||
messages := make(chan string)
|
||||
signals := make(chan bool)
|
||||
|
||||
// Here's a non-blocking receive. If a value is
|
||||
// available on `messages` then `select` will take
|
||||
// the `<-messages` `case` with that value. If not
|
||||
// it will immediately take the `default` case.
|
||||
select {
|
||||
case msg := <-messages:
|
||||
fmt.Println("received message", msg)
|
||||
default:
|
||||
fmt.Println("no message received")
|
||||
}
|
||||
// Here's a non-blocking receive. If a value is
|
||||
// available on `messages` then `select` will take
|
||||
// the `<-messages` `case` with that value. If not
|
||||
// it will immediately take the `default` case.
|
||||
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")
|
||||
}
|
||||
// A non-blocking send works similarly. Here `msg`
|
||||
// cannot be sent to the `messages` channel, because
|
||||
// the channel has no buffer and there is no receiver.
|
||||
// Therefore the `default` case is selected.
|
||||
msg := "hi"
|
||||
select {
|
||||
case messages <- msg:
|
||||
fmt.Println("sent message", msg)
|
||||
default:
|
||||
fmt.Println("no message sent")
|
||||
}
|
||||
|
||||
// We can use multiple `case`s above the `default`
|
||||
// clause to implement a multi-way non-blocking
|
||||
// select. Here we attempt non-blocking receives
|
||||
// on both `messages` and `signals`.
|
||||
select {
|
||||
case msg := <-messages:
|
||||
fmt.Println("received message", msg)
|
||||
case sig := <-signals:
|
||||
fmt.Println("received signal", sig)
|
||||
default:
|
||||
fmt.Println("no activity")
|
||||
}
|
||||
// We can use multiple `case`s above the `default`
|
||||
// clause to implement a multi-way non-blocking
|
||||
// select. Here we attempt non-blocking receives
|
||||
// on both `messages` and `signals`.
|
||||
select {
|
||||
case msg := <-messages:
|
||||
fmt.Println("received message", msg)
|
||||
case sig := <-signals:
|
||||
fmt.Println("received signal", sig)
|
||||
default:
|
||||
fmt.Println("no activity")
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
119ced4df4f79795b163483b6abfd855e76ef577
|
||||
M972dltae2
|
||||
a6e0a8bb87153c7ed0de4996172f7ad5d89c6814
|
||||
n5ttmOsMrrJ
|
||||
|
@ -10,31 +10,31 @@ import "fmt"
|
||||
|
||||
func main() {
|
||||
|
||||
// With `ParseFloat`, this `64` tells how many bits of
|
||||
// precision to parse.
|
||||
f, _ := strconv.ParseFloat("1.234", 64)
|
||||
fmt.Println(f)
|
||||
// With `ParseFloat`, this `64` tells how many bits of
|
||||
// precision to parse.
|
||||
f, _ := strconv.ParseFloat("1.234", 64)
|
||||
fmt.Println(f)
|
||||
|
||||
// For `ParseInt`, the `0` means infer the base from
|
||||
// the string. `64` requires that the result fit in 64
|
||||
// bits.
|
||||
i, _ := strconv.ParseInt("123", 0, 64)
|
||||
fmt.Println(i)
|
||||
// For `ParseInt`, the `0` means infer the base from
|
||||
// the string. `64` requires that the result fit in 64
|
||||
// bits.
|
||||
i, _ := strconv.ParseInt("123", 0, 64)
|
||||
fmt.Println(i)
|
||||
|
||||
// `ParseInt` will recognize hex-formatted numbers.
|
||||
d, _ := strconv.ParseInt("0x1c8", 0, 64)
|
||||
fmt.Println(d)
|
||||
// `ParseInt` will recognize hex-formatted numbers.
|
||||
d, _ := strconv.ParseInt("0x1c8", 0, 64)
|
||||
fmt.Println(d)
|
||||
|
||||
// A `ParseUint` is also available.
|
||||
u, _ := strconv.ParseUint("789", 0, 64)
|
||||
fmt.Println(u)
|
||||
// A `ParseUint` is also available.
|
||||
u, _ := strconv.ParseUint("789", 0, 64)
|
||||
fmt.Println(u)
|
||||
|
||||
// `Atoi` is a convenience function for basic base-10
|
||||
// `int` parsing.
|
||||
k, _ := strconv.Atoi("135")
|
||||
fmt.Println(k)
|
||||
// `Atoi` is a convenience function for basic base-10
|
||||
// `int` parsing.
|
||||
k, _ := strconv.Atoi("135")
|
||||
fmt.Println(k)
|
||||
|
||||
// Parse functions return an error on bad input.
|
||||
_, e := strconv.Atoi("wat")
|
||||
fmt.Println(e)
|
||||
// Parse functions return an error on bad input.
|
||||
_, e := strconv.Atoi("wat")
|
||||
fmt.Println(e)
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
0d2155e9863a73c098d44637e92403d7f5e8e965
|
||||
N90EppECFk
|
||||
NZh4LjhguvN
|
||||
|
@ -9,17 +9,17 @@ 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")
|
||||
// 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
|
||||
// `panic`king if we get an unexpected error when creating a new file.
|
||||
_, err := os.Create("/tmp/file")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// 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
|
||||
// `panic`king if we get an unexpected error when creating a new file.
|
||||
_, err := os.Create("/tmp/file")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
91639bbcfcc6ed088295a9ee6b1c36ab35ae402a
|
||||
c86oXzfQOt
|
||||
91HXbZZZopt
|
||||
|
@ -12,7 +12,7 @@ import "fmt"
|
||||
// value. `zeroval` will get a copy of `ival` distinct
|
||||
// from the one in the calling function.
|
||||
func zeroval(ival int) {
|
||||
ival = 0
|
||||
ival = 0
|
||||
}
|
||||
|
||||
// `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
|
||||
// value at the referenced address.
|
||||
func zeroptr(iptr *int) {
|
||||
*iptr = 0
|
||||
*iptr = 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
i := 1
|
||||
fmt.Println("initial:", i)
|
||||
i := 1
|
||||
fmt.Println("initial:", i)
|
||||
|
||||
zeroval(i)
|
||||
fmt.Println("zeroval:", i)
|
||||
zeroval(i)
|
||||
fmt.Println("zeroval:", i)
|
||||
|
||||
// The `&i` syntax gives the memory address of `i`,
|
||||
// i.e. a pointer to `i`.
|
||||
zeroptr(&i)
|
||||
fmt.Println("zeroptr:", i)
|
||||
// The `&i` syntax gives the memory address of `i`,
|
||||
// i.e. a pointer to `i`.
|
||||
zeroptr(&i)
|
||||
fmt.Println("zeroptr:", i)
|
||||
|
||||
// Pointers can be printed too.
|
||||
fmt.Println("pointer:", &i)
|
||||
// Pointers can be printed too.
|
||||
fmt.Println("pointer:", &i)
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
85cff3345d2f22b65a5d54eb8f7aa8f508f27887
|
||||
KdE4TBbUL2
|
||||
fnQkHp4hriG
|
||||
|
@ -10,45 +10,45 @@ import "math/rand"
|
||||
|
||||
func main() {
|
||||
|
||||
// For example, `rand.Intn` returns a random `int` n,
|
||||
// `0 <= n < 100`.
|
||||
fmt.Print(rand.Intn(100), ",")
|
||||
fmt.Print(rand.Intn(100))
|
||||
fmt.Println()
|
||||
// For example, `rand.Intn` returns a random `int` n,
|
||||
// `0 <= n < 100`.
|
||||
fmt.Print(rand.Intn(100), ",")
|
||||
fmt.Print(rand.Intn(100))
|
||||
fmt.Println()
|
||||
|
||||
// `rand.Float64` returns a `float64` `f`,
|
||||
// `0.0 <= f < 1.0`.
|
||||
fmt.Println(rand.Float64())
|
||||
// `rand.Float64` returns a `float64` `f`,
|
||||
// `0.0 <= f < 1.0`.
|
||||
fmt.Println(rand.Float64())
|
||||
|
||||
// This can be used to generate random floats in
|
||||
// other ranges, for example `5.0 <= f' < 10.0`.
|
||||
fmt.Print((rand.Float64()*5)+5, ",")
|
||||
fmt.Print((rand.Float64() * 5) + 5)
|
||||
fmt.Println()
|
||||
// This can be used to generate random floats in
|
||||
// other ranges, for example `5.0 <= f' < 10.0`.
|
||||
fmt.Print((rand.Float64()*5)+5, ",")
|
||||
fmt.Print((rand.Float64() * 5) + 5)
|
||||
fmt.Println()
|
||||
|
||||
// The default number generator is deterministic, so it'll
|
||||
// produce the same sequence of numbers each time by default.
|
||||
// To produce varying sequences, give it a seed that changes.
|
||||
// Note that this is not safe to use for random numbers you
|
||||
// intend to be secret, use `crypto/rand` for those.
|
||||
s1 := rand.NewSource(time.Now().UnixNano())
|
||||
r1 := rand.New(s1)
|
||||
// The default number generator is deterministic, so it'll
|
||||
// produce the same sequence of numbers each time by default.
|
||||
// To produce varying sequences, give it a seed that changes.
|
||||
// Note that this is not safe to use for random numbers you
|
||||
// intend to be secret, use `crypto/rand` for those.
|
||||
s1 := rand.NewSource(time.Now().UnixNano())
|
||||
r1 := rand.New(s1)
|
||||
|
||||
// Call the resulting `rand.Rand` just like the
|
||||
// functions on the `rand` package.
|
||||
fmt.Print(r1.Intn(100), ",")
|
||||
fmt.Print(r1.Intn(100))
|
||||
fmt.Println()
|
||||
// Call the resulting `rand.Rand` just like the
|
||||
// functions on the `rand` package.
|
||||
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()
|
||||
s3 := rand.NewSource(42)
|
||||
r3 := rand.New(s3)
|
||||
fmt.Print(r3.Intn(100), ",")
|
||||
fmt.Print(r3.Intn(100))
|
||||
// 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()
|
||||
s3 := rand.NewSource(42)
|
||||
r3 := rand.New(s3)
|
||||
fmt.Print(r3.Intn(100), ",")
|
||||
fmt.Print(r3.Intn(100))
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
8e97de760147b061dd09939db294c892211b6b80
|
||||
ZdFpbahgC1
|
||||
jiJaIjxL2sP
|
||||
|
@ -9,17 +9,17 @@ import "fmt"
|
||||
|
||||
func main() {
|
||||
|
||||
// We'll iterate over 2 values in the `queue` channel.
|
||||
queue := make(chan string, 2)
|
||||
queue <- "one"
|
||||
queue <- "two"
|
||||
close(queue)
|
||||
// We'll iterate over 2 values in the `queue` channel.
|
||||
queue := make(chan string, 2)
|
||||
queue <- "one"
|
||||
queue <- "two"
|
||||
close(queue)
|
||||
|
||||
// This `range` iterates over each element as it's
|
||||
// received from `queue`. Because we `close`d the
|
||||
// channel above, the iteration terminates after
|
||||
// receiving the 2 elements.
|
||||
for elem := range queue {
|
||||
fmt.Println(elem)
|
||||
}
|
||||
// This `range` iterates over each element as it's
|
||||
// received from `queue`. Because we `close`d the
|
||||
// channel above, the iteration terminates after
|
||||
// receiving the 2 elements.
|
||||
for elem := range queue {
|
||||
fmt.Println(elem)
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
8b5d8a77e84c34771c5b14af014ecef3f88b2a6c
|
||||
I63ge2ISDs
|
||||
QnARPm-ddFB
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user