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
|
||||
|
@ -1,2 +1,2 @@
|
||||
305975d13d24223181d13f042b290906d86c1a0e
|
||||
l-A8eBnwio
|
||||
W7NwfDq8Vdw
|
||||
|
@ -15,7 +15,7 @@ func main() {
|
||||
|
||||
// We'll use an unsigned integer to represent our
|
||||
// (always-positive) counter.
|
||||
var ops uint64 = 0
|
||||
var ops uint64
|
||||
|
||||
// To simulate concurrent updates, we'll start 50
|
||||
// goroutines that each increment the counter about
|
||||
|
@ -1,2 +1,2 @@
|
||||
ce8821f1f4fd99d554ad6cde52403dd3b69bb70a
|
||||
8p48eFFxDZ
|
||||
a4190094ea0405b5f2733101beb97939a1d43aee
|
||||
KDr9EMMPMgi
|
||||
|
@ -17,7 +17,7 @@ func main() {
|
||||
// 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.
|
||||
// convert our `string` to that type.
|
||||
sEnc := b64.StdEncoding.EncodeToString([]byte(data))
|
||||
fmt.Println(sEnc)
|
||||
|
||||
|
@ -1,2 +1,2 @@
|
||||
e57f5be3a796261fb4a55cdb0580a254e14b4930
|
||||
t6rFm2x4Yr
|
||||
c20da14820b656c867790f2e99bc37140babca8c
|
||||
y_QTcqdkvZh
|
||||
|
@ -1,2 +1,2 @@
|
||||
122140f7ad1bc5cff4fcd7a9e7245b87aaca3ec5
|
||||
34PVHwO6Bn
|
||||
mPoF-Xi-rip
|
||||
|
@ -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
|
||||
|
||||
|
@ -1,2 +1,2 @@
|
||||
fe3e2ea1a67d0f95ce4cb18f3e8aa16d416de0ce
|
||||
0DfW-1RMqi
|
||||
eb022977181884c2ab0f2b69e50311769e67a509
|
||||
8lmP8beav0p
|
||||
|
@ -1,2 +1,2 @@
|
||||
926212c784ab820648906c96f6ab21afbc161526
|
||||
Kd8B0T_JGK
|
||||
bRGMAqinovA
|
||||
|
@ -1,2 +1,2 @@
|
||||
5205898a520533e46ea24c849848d19ebc2d08a9
|
||||
eFZ2SeKswH
|
||||
mkz69rVMHs6
|
||||
|
@ -14,7 +14,7 @@ import "fmt"
|
||||
func intSeq() func() int {
|
||||
i := 0
|
||||
return func() int {
|
||||
i += 1
|
||||
i++
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
2e062d01989caada16c4b22ff6a35cd58e4eb819
|
||||
gQtEWkhWyp
|
||||
e304df67e760dda93ffe434aca58aea4a6c94f19
|
||||
zb93qzV6iN3
|
||||
|
@ -21,7 +21,7 @@ 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 {
|
||||
@ -32,13 +32,13 @@ func Index(vs []string, t string) int {
|
||||
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
|
||||
}
|
||||
|
||||
// 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 {
|
||||
@ -49,7 +49,7 @@ func Any(vs []string, f func(string) bool) bool {
|
||||
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 {
|
||||
@ -60,7 +60,7 @@ func All(vs []string, f func(string) bool) bool {
|
||||
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)
|
||||
@ -72,7 +72,7 @@ func Filter(vs []string, f func(string) bool) []string {
|
||||
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))
|
||||
|
@ -1,2 +1,2 @@
|
||||
ed54b3fc0512ccace0f3d0b74975c9bcd2e7a8a2
|
||||
3PNdke3Wia
|
||||
d961fc0e0074aed46cfd1516efdadea78781af56
|
||||
BJB_npWH516
|
||||
|
@ -1,2 +1,2 @@
|
||||
41c970a1ef29ad2a05307e6c783ff52ab80eaccd
|
||||
44uyYt_TRl
|
||||
6pFdjf800jj
|
||||
|
@ -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.
|
@ -22,7 +22,7 @@ func main() {
|
||||
fmt.Println(d)
|
||||
|
||||
// A numeric constant has no type until it's given
|
||||
// one, such as by an explicit cast.
|
||||
// one, such as by an explicit conversion.
|
||||
fmt.Println(int64(d))
|
||||
|
||||
// A number can be given a type by using it in a
|
||||
|
@ -1,2 +1,2 @@
|
||||
3de4f16f1ed032378268411b2173b95e8000305d
|
||||
T5sj0eINnp
|
||||
2f2ec3a5ff4eef280199da1908eed261346fb40e
|
||||
VhP0f8moZd3
|
||||
|
@ -40,5 +40,11 @@ func writeFile(f *os.File) {
|
||||
|
||||
func closeFile(f *os.File) {
|
||||
fmt.Println("closing")
|
||||
f.Close()
|
||||
err := f.Close()
|
||||
// It's important to check for errors when closing a
|
||||
// file, even in a deferred function.
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
@ -1,2 +1,2 @@
|
||||
4d0832c5a1ddd4e95474791e8802c15452358214
|
||||
kfqLhpmEpw
|
||||
CZJ4R_uu6Uu
|
||||
|
@ -1,2 +1,2 @@
|
||||
61a498229c8878a97d729cfdd215e5f3960f87ac
|
||||
GP_zEjhlWk
|
||||
eN1Qv2ATB-C
|
||||
|
@ -23,7 +23,7 @@ func f1(arg int) (int, error) {
|
||||
|
||||
}
|
||||
|
||||
// A nil value in the error position indicates that
|
||||
// A `nil` value in the error position indicates that
|
||||
// there was no error.
|
||||
return arg + 3, nil
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
07cffb3d4e37162ab7e9e0a192561ddc8042b81a
|
||||
BmDQXkPPTk
|
||||
210ba0f8196006c0380acaec01655816848ef168
|
||||
mP_ZR1qjUvA
|
||||
|
@ -1,2 +1,2 @@
|
||||
b527bbb76a42dd4bae541b73a7377b7e83e79905
|
||||
neqdJ51KLN
|
||||
bf11ADw-2Ho
|
||||
|
@ -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
|
@ -1,2 +1,2 @@
|
||||
33056d6b36f9894fb6359c9cf2ef8725bbdafa19
|
||||
KNLLSX4Io_
|
||||
lGYfUJwiGfi
|
||||
|
@ -1,2 +1,2 @@
|
||||
ae669923c20e5ebf4a7b4b11b8fdf2972accf9e2
|
||||
9Nky-Dn49f
|
||||
hzGUvK6iJNm
|
||||
|
@ -30,9 +30,8 @@ func main() {
|
||||
|
||||
// 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
|
||||
// to here. This `Scanln` requires we press a key
|
||||
// before the program exits.
|
||||
var input string
|
||||
fmt.Scanln(&input)
|
||||
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
|
||||
|
@ -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
|
@ -1,2 +1,2 @@
|
||||
89b78f3378e1a574ddfd0260a0404a962852eff8
|
||||
g-aqMz0Ivf
|
||||
p6-WKTqEks4
|
||||
|
@ -1,2 +1,2 @@
|
||||
3547b935d1e0322c0fb696726c27cae53a275e0a
|
||||
313UebA3rD
|
||||
0EwsqIn3TTi
|
||||
|
@ -10,11 +10,11 @@ import "os"
|
||||
|
||||
// We'll use these two structs to demonstrate encoding and
|
||||
// decoding of custom types below.
|
||||
type Response1 struct {
|
||||
type response1 struct {
|
||||
Page int
|
||||
Fruits []string
|
||||
}
|
||||
type Response2 struct {
|
||||
type response2 struct {
|
||||
Page int `json:"page"`
|
||||
Fruits []string `json:"fruits"`
|
||||
}
|
||||
@ -50,7 +50,7 @@ func main() {
|
||||
// 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{
|
||||
res1D := &response1{
|
||||
Page: 1,
|
||||
Fruits: []string{"apple", "peach", "pear"}}
|
||||
res1B, _ := json.Marshal(res1D)
|
||||
@ -58,9 +58,9 @@ func main() {
|
||||
|
||||
// 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
|
||||
// definition of `response2` above to see an example
|
||||
// of such tags.
|
||||
res2D := &Response2{
|
||||
res2D := &response2{
|
||||
Page: 1,
|
||||
Fruits: []string{"apple", "peach", "pear"}}
|
||||
res2B, _ := json.Marshal(res2D)
|
||||
@ -85,14 +85,14 @@ func main() {
|
||||
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
|
||||
// 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.
|
||||
// conversions.
|
||||
strs := dat["strs"].([]interface{})
|
||||
str1 := strs[0].(string)
|
||||
fmt.Println(str1)
|
||||
@ -103,7 +103,7 @@ func main() {
|
||||
// need for type assertions when accessing the decoded
|
||||
// data.
|
||||
str := `{"page": 1, "fruits": ["apple", "peach"]}`
|
||||
res := Response2{}
|
||||
res := response2{}
|
||||
json.Unmarshal([]byte(str), &res)
|
||||
fmt.Println(res)
|
||||
fmt.Println(res.Fruits[0])
|
||||
|
@ -1,2 +1,2 @@
|
||||
dee52e022a957b97c53fb2d2835653ef507502be
|
||||
WxRgpycMaH
|
||||
d4dc2281f64061f077d8f1e9687538f41a339b25
|
||||
xC6SHbzGBZC
|
||||
|
@ -1,2 +1,2 @@
|
||||
87f4a67edf741979f8ff6da85947aa177547f9ef
|
||||
mpYwOHj2ma
|
||||
hnaOIaQAjKF
|
||||
|
@ -16,7 +16,7 @@ func main() {
|
||||
m["k1"] = 7
|
||||
m["k2"] = 13
|
||||
|
||||
// Printing a map with e.g. `Println` will show all of
|
||||
// Printing a map with e.g. `fmt.Println` will show all of
|
||||
// its key/value pairs.
|
||||
fmt.Println("map:", m)
|
||||
|
||||
|
@ -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]
|
||||
|
@ -1,2 +1,2 @@
|
||||
24cfb9ad45e43c2d49163149bc55925a4e1b3c7a
|
||||
254m_9Yjwa
|
||||
ffMb0txGnYB
|
||||
|
@ -1,2 +1,2 @@
|
||||
5063ce3d3c70c6bd70f4b709de24bb93d0f24e0c
|
||||
chwFmr5dG1
|
||||
FZoIB5LXQGZ
|
||||
|
@ -1,6 +1,6 @@
|
||||
// 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
|
||||
@ -23,8 +23,8 @@ func main() {
|
||||
|
||||
// We'll keep track of how many read and write
|
||||
// operations we do.
|
||||
var readOps uint64 = 0
|
||||
var writeOps uint64 = 0
|
||||
var readOps uint64
|
||||
var writeOps uint64
|
||||
|
||||
// Here we start 100 goroutines to execute repeated
|
||||
// reads against the state, once per millisecond in
|
||||
|
@ -1,2 +1,2 @@
|
||||
e82356cbb37143862b0a9bbc68856f4b272c4918
|
||||
a9Wky7k-Bw
|
||||
ca257d9594a6219d5803193132e999a32dc8c856
|
||||
IRewFKz2OPN
|
||||
|
@ -22,7 +22,10 @@ func main() {
|
||||
fmt.Println("no message received")
|
||||
}
|
||||
|
||||
// A non-blocking send works similarly.
|
||||
// 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:
|
||||
|
@ -1,2 +1,2 @@
|
||||
119ced4df4f79795b163483b6abfd855e76ef577
|
||||
M972dltae2
|
||||
a6e0a8bb87153c7ed0de4996172f7ad5d89c6814
|
||||
n5ttmOsMrrJ
|
||||
|
@ -1,2 +1,2 @@
|
||||
0d2155e9863a73c098d44637e92403d7f5e8e965
|
||||
N90EppECFk
|
||||
NZh4LjhguvN
|
||||
|
@ -1,2 +1,2 @@
|
||||
91639bbcfcc6ed088295a9ee6b1c36ab35ae402a
|
||||
c86oXzfQOt
|
||||
91HXbZZZopt
|
||||
|
@ -1,2 +1,2 @@
|
||||
85cff3345d2f22b65a5d54eb8f7aa8f508f27887
|
||||
KdE4TBbUL2
|
||||
fnQkHp4hriG
|
||||
|
@ -1,2 +1,2 @@
|
||||
8e97de760147b061dd09939db294c892211b6b80
|
||||
ZdFpbahgC1
|
||||
jiJaIjxL2sP
|
||||
|
@ -1,2 +1,2 @@
|
||||
8b5d8a77e84c34771c5b14af014ecef3f88b2a6c
|
||||
I63ge2ISDs
|
||||
QnARPm-ddFB
|
||||
|
@ -1,2 +1,2 @@
|
||||
ebe328a57f3d34708709ca99d3304af1733592d9
|
||||
SkL_AS-1Jd
|
||||
JTY1VAUjfBw
|
||||
|
@ -1,4 +1,4 @@
|
||||
// _[Rate limiting](http://en.wikipedia.org/wiki/Rate_limiting)_
|
||||
// <em>[Rate limiting](http://en.wikipedia.org/wiki/Rate_limiting)</em>
|
||||
// is an important mechanism for controlling resource
|
||||
// utilization and maintaining quality of service. Go
|
||||
// elegantly supports rate limiting with goroutines,
|
||||
@ -24,7 +24,7 @@ func main() {
|
||||
// This `limiter` channel will receive a value
|
||||
// every 200 milliseconds. This is the regulator in
|
||||
// our rate limiting scheme.
|
||||
limiter := time.Tick(time.Millisecond * 200)
|
||||
limiter := time.Tick(200 * time.Millisecond)
|
||||
|
||||
// By blocking on a receive from the `limiter` channel
|
||||
// before serving each request, we limit ourselves to
|
||||
@ -49,7 +49,7 @@ func main() {
|
||||
// Every 200 milliseconds we'll try to add a new
|
||||
// value to `burstyLimiter`, up to its limit of 3.
|
||||
go func() {
|
||||
for t := range time.Tick(time.Millisecond * 200) {
|
||||
for t := range time.Tick(200 * time.Millisecond) {
|
||||
burstyLimiter <- t
|
||||
}
|
||||
}()
|
||||
|
@ -1,2 +1,2 @@
|
||||
d74aebb12f618f22ec776eb5b4de92985104c197
|
||||
e7yzIk97-p
|
||||
edad78bf3b36ddc9bec30b344b8a72be4de90f3b
|
||||
l4uDE-RCDpa
|
||||
|
@ -40,7 +40,7 @@ func main() {
|
||||
b1 := make([]byte, 5)
|
||||
n1, err := f.Read(b1)
|
||||
check(err)
|
||||
fmt.Printf("%d bytes: %s\n", n1, string(b1))
|
||||
fmt.Printf("%d bytes: %s\n", n1, string(b1[:n1]))
|
||||
|
||||
// You can also `Seek` to a known location in the file
|
||||
// and `Read` from there.
|
||||
@ -49,7 +49,8 @@ func main() {
|
||||
b2 := make([]byte, 2)
|
||||
n2, err := f.Read(b2)
|
||||
check(err)
|
||||
fmt.Printf("%d bytes @ %d: %s\n", n2, o2, string(b2))
|
||||
fmt.Printf("%d bytes @ %d: ", n2, o2)
|
||||
fmt.Printf("%v\n", string(b2[:n2]))
|
||||
|
||||
// The `io` package provides some functions that may
|
||||
// be helpful for file reading. For example, reads
|
||||
@ -80,5 +81,4 @@ func main() {
|
||||
// be scheduled immediately after `Open`ing with
|
||||
// `defer`).
|
||||
f.Close()
|
||||
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
2aa7a2e248065cebfa6f8eece3234b5ffa710273
|
||||
2kEKXq-kUV
|
||||
463a6f2999a023887af6b23c8f79f24978eb8115
|
||||
cocJ6kBH_iZ
|
||||
|
@ -1,2 +1,2 @@
|
||||
5d1ba6b03a50ccae2a0f46865eb72c587e11857c
|
||||
RFn-rf42ap
|
||||
4yUp5wLVyiG
|
||||
|
@ -1,2 +1,2 @@
|
||||
7cde6b9af5cf6c47606001dd54eee468a6c61dbb
|
||||
YeSiBTfhFq
|
||||
qR5gn2l0AGa
|
||||
|
@ -17,11 +17,11 @@ func main() {
|
||||
// of time, to simulate e.g. blocking RPC operations
|
||||
// executing in concurrent goroutines.
|
||||
go func() {
|
||||
time.Sleep(time.Second * 1)
|
||||
time.Sleep(1 * time.Second)
|
||||
c1 <- "one"
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(time.Second * 2)
|
||||
time.Sleep(2 * time.Second)
|
||||
c2 <- "two"
|
||||
}()
|
||||
|
||||
|
@ -1,2 +1,2 @@
|
||||
72503557ab54ef765eeba153fe8a3446541dfc5f
|
||||
Vco7d8Lmhn
|
||||
8d743edffd7de6bf7bccdf4437f45672b6adc75e
|
||||
ZdSOPe1Gj13
|
||||
|
@ -1,2 +1,2 @@
|
||||
6a896270e34f2696b881a8fa7e68bfff57dee51f
|
||||
YUaWWEeB4U
|
||||
1oT-5GBUkLr
|
||||
|
@ -1,2 +1,2 @@
|
||||
9720d747e3ab2893df508a70cbb341c90fdd7ca1
|
||||
BlkqAtKsxo
|
||||
9koJAW1raI5
|
||||
|
@ -30,7 +30,7 @@ func main() {
|
||||
// arrays. One is the builtin `append`, which
|
||||
// returns a slice containing one or more new values.
|
||||
// Note that we need to accept a return value from
|
||||
// append as we may get a new slice value.
|
||||
// `append` as we may get a new slice value.
|
||||
s = append(s, "d")
|
||||
s = append(s, "e", "f")
|
||||
fmt.Println("apd:", s)
|
||||
|
@ -1,2 +1,2 @@
|
||||
d900c3b1cf2bd96591f7ad7ce7fd9e592ec31139
|
||||
dPQErsP6Yc
|
||||
c6fa1627841f199dbf901f88580cb97eb92c5530
|
||||
Z3_U32sn8RF
|
||||
|
@ -10,10 +10,10 @@ import "sort"
|
||||
import "fmt"
|
||||
|
||||
// In order to sort by a custom function in Go, we need a
|
||||
// corresponding type. Here we've created a `ByLength`
|
||||
// corresponding type. Here we've created a `byLength`
|
||||
// type that is just an alias for the builtin `[]string`
|
||||
// type.
|
||||
type ByLength []string
|
||||
type byLength []string
|
||||
|
||||
// We implement `sort.Interface` - `Len`, `Less`, and
|
||||
// `Swap` - on our type so we can use the `sort` package's
|
||||
@ -22,22 +22,22 @@ type ByLength []string
|
||||
// hold the actual custom sorting logic. In our case we
|
||||
// want to sort in order of increasing string length, so
|
||||
// we use `len(s[i])` and `len(s[j])` here.
|
||||
func (s ByLength) Len() int {
|
||||
func (s byLength) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
func (s ByLength) Swap(i, j int) {
|
||||
func (s byLength) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
func (s ByLength) Less(i, j int) bool {
|
||||
func (s byLength) Less(i, j int) bool {
|
||||
return len(s[i]) < len(s[j])
|
||||
}
|
||||
|
||||
// With all of this in place, we can now implement our
|
||||
// custom sort by casting the original `fruits` slice to
|
||||
// `ByLength`, and then use `sort.Sort` on that typed
|
||||
// custom sort by converting the original `fruits` slice
|
||||
// to `byLength`, and then use `sort.Sort` on that typed
|
||||
// slice.
|
||||
func main() {
|
||||
fruits := []string{"peach", "banana", "kiwi"}
|
||||
sort.Sort(ByLength(fruits))
|
||||
sort.Sort(byLength(fruits))
|
||||
fmt.Println(fruits)
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
cec0da0bd98abd7f66fcd38122e4405f757161bc
|
||||
N6GbEgBffd
|
||||
6a04058b564d5741815e523f97f240ee6563cb15
|
||||
y3kuCwIFRYK
|
||||
|
@ -1,2 +1,2 @@
|
||||
4e576421f2bdbd11847c367d223bd30d0e301990
|
||||
roQOJXtqAb
|
||||
e6hp3Rn-oH6
|
||||
|
@ -1,2 +1,2 @@
|
||||
0b676b93e41ac5434003c194bc038d5f3ce76bc8
|
||||
y6SB6Mf2VQ
|
||||
6HRWVK5gPYU
|
||||
|
@ -37,14 +37,14 @@ type writeOp struct {
|
||||
func main() {
|
||||
|
||||
// As before we'll count how many operations we perform.
|
||||
var readOps uint64 = 0
|
||||
var writeOps uint64 = 0
|
||||
var readOps uint64
|
||||
var writeOps uint64
|
||||
|
||||
// The `reads` and `writes` channels will be used by
|
||||
// other goroutines to issue read and write requests,
|
||||
// respectively.
|
||||
reads := make(chan *readOp)
|
||||
writes := make(chan *writeOp)
|
||||
reads := make(chan readOp)
|
||||
writes := make(chan writeOp)
|
||||
|
||||
// Here is the goroutine that owns the `state`, which
|
||||
// is a map as in the previous example but now private
|
||||
@ -76,7 +76,7 @@ func main() {
|
||||
for r := 0; r < 100; r++ {
|
||||
go func() {
|
||||
for {
|
||||
read := &readOp{
|
||||
read := readOp{
|
||||
key: rand.Intn(5),
|
||||
resp: make(chan int)}
|
||||
reads <- read
|
||||
@ -92,7 +92,7 @@ func main() {
|
||||
for w := 0; w < 10; w++ {
|
||||
go func() {
|
||||
for {
|
||||
write := &writeOp{
|
||||
write := writeOp{
|
||||
key: rand.Intn(5),
|
||||
val: rand.Intn(100),
|
||||
resp: make(chan bool)}
|
||||
|
@ -1,2 +1,2 @@
|
||||
c306add52c0752f0b3099eb669364fc4bdb74be1
|
||||
P4SrrlosMp
|
||||
956afe7524b492b2e85f8320c70f180c448a764a
|
||||
saQTLpdIgp2
|
||||
|
@ -1,2 +1,2 @@
|
||||
5f39ae6d8f26d59a688a9a9d7d13a5c1d0f7a08b
|
||||
JJAAFGxHVq
|
||||
CkBQ3CFpHQ9
|
||||
|
@ -1,2 +1,2 @@
|
||||
17aa523bbd606fa0b624fae44b89812d46330755
|
||||
Lf5_Zbg6or
|
||||
Vn4D3y4_711
|
||||
|
@ -12,6 +12,15 @@ type person struct {
|
||||
age int
|
||||
}
|
||||
|
||||
// NewPerson constructs a new person struct with the given name
|
||||
func NewPerson(name string) *person {
|
||||
// You can safely return a pointer to local variable
|
||||
// as a local variable will survive the scope of the function.
|
||||
p := person{name: name}
|
||||
p.age = 42
|
||||
return &p
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
// This syntax creates a new struct.
|
||||
@ -26,6 +35,9 @@ func main() {
|
||||
// An `&` prefix yields a pointer to the struct.
|
||||
fmt.Println(&person{name: "Ann", age: 40})
|
||||
|
||||
// It's idiomatic to encapsulate new struct creation in constructor functions
|
||||
fmt.Println(NewPerson("Jon"))
|
||||
|
||||
// Access struct fields with a dot.
|
||||
s := person{name: "Sean", age: 50}
|
||||
fmt.Println(s.name)
|
||||
|
@ -1,2 +1,2 @@
|
||||
49cad39331ee5e9fb8d8dad99d3aff7f18a4e6d0
|
||||
OMCP5KFC10
|
||||
c5caaf1eefaf084d688afb70d2ee5884a4983182
|
||||
00Yiw6xuICq
|
||||
|
@ -6,3 +6,4 @@ $ go run structs.go
|
||||
Sean
|
||||
50
|
||||
51
|
||||
&{Jon 42}
|
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