Repository: mmcgrana/gobyexample Branch: master Commit: e8ca216d0145 Files: 373 Total size: 1.2 MB Directory structure: gitextract_l93mdm2d/ ├── .gitattributes ├── .github/ │ └── workflows/ │ └── test.yml ├── .gitignore ├── CONTRIBUTING.md ├── README.md ├── examples/ │ ├── arrays/ │ │ ├── arrays.go │ │ ├── arrays.hash │ │ └── arrays.sh │ ├── atomic-counters/ │ │ ├── atomic-counters.go │ │ ├── atomic-counters.hash │ │ └── atomic-counters.sh │ ├── base64-encoding/ │ │ ├── base64-encoding.go │ │ ├── base64-encoding.hash │ │ └── base64-encoding.sh │ ├── channel-buffering/ │ │ ├── channel-buffering.go │ │ ├── channel-buffering.hash │ │ └── channel-buffering.sh │ ├── channel-directions/ │ │ ├── channel-directions.go │ │ ├── channel-directions.hash │ │ └── channel-directions.sh │ ├── channel-synchronization/ │ │ ├── channel-synchronization.go │ │ ├── channel-synchronization.hash │ │ └── channel-synchronization.sh │ ├── channels/ │ │ ├── channels.go │ │ ├── channels.hash │ │ └── channels.sh │ ├── closing-channels/ │ │ ├── closing-channels.go │ │ ├── closing-channels.hash │ │ └── closing-channels.sh │ ├── closures/ │ │ ├── closures.go │ │ ├── closures.hash │ │ └── closures.sh │ ├── command-line-arguments/ │ │ ├── command-line-arguments.go │ │ ├── command-line-arguments.hash │ │ └── command-line-arguments.sh │ ├── command-line-flags/ │ │ ├── command-line-flags.go │ │ ├── command-line-flags.hash │ │ └── command-line-flags.sh │ ├── command-line-subcommands/ │ │ ├── command-line-subcommands.go │ │ ├── command-line-subcommands.hash │ │ └── command-line-subcommands.sh │ ├── constants/ │ │ ├── constants.go │ │ ├── constants.hash │ │ └── constants.sh │ ├── context/ │ │ ├── context.go │ │ ├── context.hash │ │ └── context.sh │ ├── custom-errors/ │ │ ├── custom-errors.go │ │ ├── custom-errors.hash │ │ └── custom-errors.sh │ ├── defer/ │ │ ├── defer.go │ │ ├── defer.hash │ │ └── defer.sh │ ├── directories/ │ │ ├── directories.go │ │ ├── directories.hash │ │ └── directories.sh │ ├── embed-directive/ │ │ ├── embed-directive.go │ │ ├── embed-directive.hash │ │ ├── embed-directive.sh │ │ └── folder/ │ │ ├── file1.hash │ │ ├── file2.hash │ │ └── single_file.txt │ ├── enums/ │ │ ├── enums.go │ │ ├── enums.hash │ │ └── enums.sh │ ├── environment-variables/ │ │ ├── environment-variables.go │ │ ├── environment-variables.hash │ │ └── environment-variables.sh │ ├── epoch/ │ │ ├── epoch.go │ │ ├── epoch.hash │ │ └── epoch.sh │ ├── errors/ │ │ ├── errors.go │ │ ├── errors.hash │ │ └── errors.sh │ ├── execing-processes/ │ │ ├── execing-processes.go │ │ ├── execing-processes.hash │ │ └── execing-processes.sh │ ├── exit/ │ │ ├── exit.go │ │ ├── exit.hash │ │ └── exit.sh │ ├── file-paths/ │ │ ├── file-paths.go │ │ ├── file-paths.hash │ │ └── file-paths.sh │ ├── for/ │ │ ├── for.go │ │ ├── for.hash │ │ └── for.sh │ ├── functions/ │ │ ├── functions.go │ │ ├── functions.hash │ │ └── functions.sh │ ├── generics/ │ │ ├── generics.go │ │ ├── generics.hash │ │ └── generics.sh │ ├── goroutines/ │ │ ├── goroutines.go │ │ ├── goroutines.hash │ │ └── goroutines.sh │ ├── hello-world/ │ │ ├── hello-world.go │ │ ├── hello-world.hash │ │ └── hello-world.sh │ ├── http-client/ │ │ ├── http-client.go │ │ ├── http-client.hash │ │ └── http-client.sh │ ├── http-server/ │ │ ├── http-server.go │ │ ├── http-server.hash │ │ └── http-server.sh │ ├── if-else/ │ │ ├── if-else.go │ │ ├── if-else.hash │ │ └── if-else.sh │ ├── interfaces/ │ │ ├── interfaces.go │ │ ├── interfaces.hash │ │ └── interfaces.sh │ ├── json/ │ │ ├── json.go │ │ ├── json.hash │ │ └── json.sh │ ├── line-filters/ │ │ ├── line-filters.go │ │ ├── line-filters.hash │ │ └── line-filters.sh │ ├── logging/ │ │ ├── logging.go │ │ ├── logging.hash │ │ └── logging.sh │ ├── maps/ │ │ ├── maps.go │ │ ├── maps.hash │ │ └── maps.sh │ ├── methods/ │ │ ├── methods.go │ │ ├── methods.hash │ │ └── methods.sh │ ├── multiple-return-values/ │ │ ├── multiple-return-values.go │ │ ├── multiple-return-values.hash │ │ └── multiple-return-values.sh │ ├── mutexes/ │ │ ├── mutexes.go │ │ ├── mutexes.hash │ │ └── mutexes.sh │ ├── non-blocking-channel-operations/ │ │ ├── non-blocking-channel-operations.go │ │ ├── non-blocking-channel-operations.hash │ │ └── non-blocking-channel-operations.sh │ ├── number-parsing/ │ │ ├── number-parsing.go │ │ ├── number-parsing.hash │ │ └── number-parsing.sh │ ├── panic/ │ │ ├── panic.go │ │ ├── panic.hash │ │ └── panic.sh │ ├── pointers/ │ │ ├── pointers.go │ │ ├── pointers.hash │ │ └── pointers.sh │ ├── random-numbers/ │ │ ├── random-numbers.go │ │ ├── random-numbers.hash │ │ └── random-numbers.sh │ ├── range-over-built-in-types/ │ │ ├── range-over-built-in-types.go │ │ ├── range-over-built-in-types.hash │ │ └── range-over-built-in-types.sh │ ├── range-over-channels/ │ │ ├── range-over-channels.go │ │ ├── range-over-channels.hash │ │ └── range-over-channels.sh │ ├── range-over-iterators/ │ │ ├── range-over-iterators.go │ │ ├── range-over-iterators.hash │ │ └── range-over-iterators.sh │ ├── rate-limiting/ │ │ ├── rate-limiting.go │ │ ├── rate-limiting.hash │ │ └── rate-limiting.sh │ ├── reading-files/ │ │ ├── reading-files.go │ │ ├── reading-files.hash │ │ └── reading-files.sh │ ├── recover/ │ │ ├── recover.go │ │ ├── recover.hash │ │ └── recover.sh │ ├── recursion/ │ │ ├── recursion.go │ │ ├── recursion.hash │ │ └── recursion.sh │ ├── regular-expressions/ │ │ ├── regular-expressions.go │ │ ├── regular-expressions.hash │ │ └── regular-expressions.sh │ ├── select/ │ │ ├── select.go │ │ ├── select.hash │ │ └── select.sh │ ├── sha256-hashes/ │ │ ├── sha256-hashes.go │ │ ├── sha256-hashes.hash │ │ └── sha256-hashes.sh │ ├── signals/ │ │ ├── signals.go │ │ ├── signals.hash │ │ └── signals.sh │ ├── slices/ │ │ ├── slices.go │ │ ├── slices.hash │ │ └── slices.sh │ ├── sorting/ │ │ ├── sorting.go │ │ ├── sorting.hash │ │ └── sorting.sh │ ├── sorting-by-functions/ │ │ ├── sorting-by-functions.go │ │ ├── sorting-by-functions.hash │ │ └── sorting-by-functions.sh │ ├── spawning-processes/ │ │ ├── spawning-processes.go │ │ ├── spawning-processes.hash │ │ └── spawning-processes.sh │ ├── stateful-goroutines/ │ │ ├── stateful-goroutines.go │ │ ├── stateful-goroutines.hash │ │ └── stateful-goroutines.sh │ ├── string-formatting/ │ │ ├── string-formatting.go │ │ ├── string-formatting.hash │ │ └── string-formatting.sh │ ├── string-functions/ │ │ ├── string-functions.go │ │ ├── string-functions.hash │ │ └── string-functions.sh │ ├── strings-and-runes/ │ │ ├── strings-and-runes.go │ │ ├── strings-and-runes.hash │ │ └── strings-and-runes.sh │ ├── struct-embedding/ │ │ ├── struct-embedding.go │ │ ├── struct-embedding.hash │ │ └── struct-embedding.sh │ ├── structs/ │ │ ├── structs.go │ │ ├── structs.hash │ │ └── structs.sh │ ├── switch/ │ │ ├── switch.go │ │ ├── switch.hash │ │ └── switch.sh │ ├── tcp-server/ │ │ ├── tcp-server.go │ │ ├── tcp-server.hash │ │ └── tcp-server.sh │ ├── temporary-files-and-directories/ │ │ ├── temporary-files-and-directories.go │ │ ├── temporary-files-and-directories.hash │ │ └── temporary-files-and-directories.sh │ ├── testing-and-benchmarking/ │ │ ├── main_test.go │ │ ├── main_test.sh │ │ └── testing-and-benchmarking.hash │ ├── text-templates/ │ │ ├── text-templates.go │ │ ├── text-templates.hash │ │ └── text-templates.sh │ ├── tickers/ │ │ ├── tickers.go │ │ ├── tickers.hash │ │ └── tickers.sh │ ├── time/ │ │ ├── time.go │ │ ├── time.hash │ │ └── time.sh │ ├── time-formatting-parsing/ │ │ ├── time-formatting-parsing.go │ │ ├── time-formatting-parsing.hash │ │ └── time-formatting-parsing.sh │ ├── timeouts/ │ │ ├── timeouts.go │ │ ├── timeouts.hash │ │ └── timeouts.sh │ ├── timers/ │ │ ├── timers.go │ │ ├── timers.hash │ │ └── timers.sh │ ├── url-parsing/ │ │ ├── url-parsing.go │ │ ├── url-parsing.hash │ │ └── url-parsing.sh │ ├── values/ │ │ ├── values.go │ │ ├── values.hash │ │ └── values.sh │ ├── variables/ │ │ ├── variables.go │ │ ├── variables.hash │ │ └── variables.sh │ ├── variadic-functions/ │ │ ├── variadic-functions.go │ │ ├── variadic-functions.hash │ │ └── variadic-functions.sh │ ├── waitgroups/ │ │ ├── waitgroups.go │ │ ├── waitgroups.hash │ │ └── waitgroups.sh │ ├── worker-pools/ │ │ ├── worker-pools.go │ │ ├── worker-pools.hash │ │ └── worker-pools.sh │ ├── writing-files/ │ │ ├── writing-files.go │ │ ├── writing-files.hash │ │ └── writing-files.sh │ └── xml/ │ ├── xml.go │ ├── xml.hash │ └── xml.sh ├── examples.txt ├── go.mod ├── go.sum ├── public/ │ ├── 404.html │ ├── arrays │ ├── atomic-counters │ ├── base64-encoding │ ├── channel-buffering │ ├── channel-directions │ ├── channel-synchronization │ ├── channels │ ├── closing-channels │ ├── closures │ ├── command-line-arguments │ ├── command-line-flags │ ├── command-line-subcommands │ ├── constants │ ├── context │ ├── custom-errors │ ├── defer │ ├── directories │ ├── embed-directive │ ├── enums │ ├── environment-variables │ ├── epoch │ ├── errors │ ├── execing-processes │ ├── exit │ ├── file-paths │ ├── for │ ├── functions │ ├── generics │ ├── goroutines │ ├── hello-world │ ├── http-client │ ├── http-server │ ├── if-else │ ├── index.html │ ├── interfaces │ ├── json │ ├── line-filters │ ├── logging │ ├── maps │ ├── methods │ ├── multiple-return-values │ ├── mutexes │ ├── non-blocking-channel-operations │ ├── number-parsing │ ├── panic │ ├── pointers │ ├── random-numbers │ ├── range-over-built-in-types │ ├── range-over-channels │ ├── range-over-iterators │ ├── rate-limiting │ ├── reading-files │ ├── recover │ ├── recursion │ ├── regular-expressions │ ├── select │ ├── sha256-hashes │ ├── signals │ ├── site.css │ ├── site.js │ ├── slices │ ├── sorting │ ├── sorting-by-functions │ ├── spawning-processes │ ├── stateful-goroutines │ ├── string-formatting │ ├── string-functions │ ├── strings-and-runes │ ├── struct-embedding │ ├── structs │ ├── switch │ ├── tcp-server │ ├── temporary-files-and-directories │ ├── testing-and-benchmarking │ ├── text-templates │ ├── tickers │ ├── time │ ├── time-formatting-parsing │ ├── timeouts │ ├── timers │ ├── url-parsing │ ├── values │ ├── variables │ ├── variadic-functions │ ├── waitgroups │ ├── worker-pools │ ├── writing-files │ └── xml ├── templates/ │ ├── 404.tmpl │ ├── example.tmpl │ ├── footer.tmpl │ ├── index.tmpl │ ├── site.css │ └── site.js └── tools/ ├── build ├── build-loop ├── format ├── generate ├── generate.go ├── measure ├── measure.go ├── serve ├── serve.go ├── test ├── upload └── upload.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ public/** linguist-generated ================================================ FILE: .github/workflows/test.yml ================================================ name: test on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: strategy: matrix: os: [ubuntu-latest, macos-latest] go-version: [1.26.0] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - name: Set up Go uses: actions/setup-go@v3 with: go-version: ${{ matrix.go-version }} - name: Test run: tools/build env: VERBOSE: 1 TESTING: 1 ================================================ FILE: .gitignore ================================================ *.pyc .idea .vscode ================================================ FILE: CONTRIBUTING.md ================================================ ## Contributing Thanks for your interest in contributing to Go by Example! * When sending a PR that affects the displayed contents of the site, updating the HTML in the `public` directory by itself is insufficient, since the source of truth for the website is in the `examples` directory. Instead, update the proper source file(s) in the `examples` directory and run `tools/build` locally to regenerate the HTML; include both changes in your PR. If you don't want to deal with getting a proper PR in, feel free to just open an issue and point out the change you suggest. * 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're not going to change the navigation of the site, in particular adding a "previous section" link or an "index" link other than the one on the title text. ================================================ FILE: README.md ================================================ # 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 comments from source files in `examples` and rendering them using `templates` into a static `public` directory. The programs implementing this build process are in `tools`, along with dependencies specified in the `go.mod`file. The built `public` directory can be served by any static content system. The production site uses S3 and CloudFront, for example. ### Building [![test](https://github.com/mmcgrana/gobyexample/actions/workflows/test.yml/badge.svg)](https://github.com/mmcgrana/gobyexample/actions/workflows/test.yml) To build the site you'll need Go installed. Run: ```console $ tools/build ``` To build continuously in a loop: ```console $ tools/build-loop ``` To see the site locally: ```console $ tools/serve ``` and open `http://127.0.0.1:8000/` in your browser. ### Publishing To upload the site: ```console $ export AWS_ACCESS_KEY_ID=... $ export AWS_SECRET_ACCESS_KEY=... $ tools/upload ``` ### License This work is copyright Mark McGranaghan and licensed under a [Creative Commons Attribution 3.0 Unported License](http://creativecommons.org/licenses/by/3.0/). The Go Gopher is copyright [Renée French](https://reneefrench.blogspot.com/) and licensed under a [Creative Commons Attribution 3.0 Unported License](http://creativecommons.org/licenses/by/3.0/). ### Translations Contributor translations of the Go by Example site are available in: * [Chinese](https://gobyexample-cn.github.io/) by [gobyexample-cn](https://github.com/gobyexample-cn) * [French](http://le-go-par-l-exemple.keiruaprod.fr) by [keirua](https://github.com/keirua/gobyexample) * [Italian](https://gobyexampleit.andrearaponi.it/) by [andrearaponi](https://github.com/andrearaponi/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) * [Ukrainian](https://butuzov.github.io/gobyexample/) by [butuzov](https://github.com/butuzov/gobyexample) * [Brazilian Portuguese](https://lcslitx.github.io/GoEmExemplos/) by [lcslitx](https://github.com/LCSLITX) * [Burmese](https://setkyar.github.io/gobyexample) by [Set Kyar Wa Lar](https://github.com/setkyar/gobyexample) ### Thanks Thanks to [Jeremy Ashkenas](https://github.com/jashkenas) for [Docco](http://jashkenas.github.io/docco/), which inspired this project. ### FAQ #### I found a problem with the examples; what do I do? We're very happy to fix problem reports and accept contributions! Please submit [an issue](https://github.com/mmcgrana/gobyexample/issues) or send a Pull Request. See `CONTRIBUTING.md` for more details. #### What version of Go is required to run these examples? Given Go's strong [backwards compatibility guarantees](https://go.dev/doc/go1compat), we expect the vast majority of examples to work on the latest released version of Go as well as many older releases going back years. That said, some examples show off new features added in recent releases; therefore, it's recommended to try running examples with the latest officially released Go version (see Go's [release history](https://go.dev/doc/devel/release) for details). #### I'm getting output in a different order from the example. Is the example wrong? Some of the examples demonstrate concurrent code which has a non-deterministic execution order. It depends on how the Go runtime schedules its goroutines and may vary by operating system, CPU architecture, or even Go version. Similarly, examples that iterate over maps may produce items in a different order from what you're getting on your machine. This is because the order of iteration over maps in Go is [not specified and is not guaranteed to be the same from one iteration to the next](https://go.dev/ref/spec#RangeClause). It doesn't mean anything is wrong with the example. Typically the code in these examples will be insensitive to the actual order of the output; if the code is sensitive to the order - that's probably a bug - so feel free to report it. ================================================ FILE: examples/arrays/arrays.go ================================================ // In Go, an _array_ is a numbered sequence of elements of a // specific length. In typical Go code, [slices](slices) are // much more common; arrays are useful in some special // scenarios. package main 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) // 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)) // Use this syntax to declare and initialize an array // in one line. b := [5]int{1, 2, 3, 4, 5} fmt.Println("dcl:", b) // You can also have the compiler count the number of // elements for you with `...` b = [...]int{1, 2, 3, 4, 5} fmt.Println("dcl:", b) // If you specify the index with `:`, the elements in // between will be zeroed. b = [...]int{100, 3: 400, 500} fmt.Println("idx:", b) // Array types are one-dimensional, but you can // compose types to build multi-dimensional data // structures. var twoD [2][3]int for i := range 2 { for j := range 3 { twoD[i][j] = i + j } } fmt.Println("2d: ", twoD) // You can create and initialize multi-dimensional // arrays at once too. twoD = [2][3]int{ {1, 2, 3}, {1, 2, 3}, } fmt.Println("2d: ", twoD) } ================================================ FILE: examples/arrays/arrays.hash ================================================ 96cbe484a18a0dd8c1839a92963447bed94cc997 -NFSggT7dFH ================================================ FILE: examples/arrays/arrays.sh ================================================ # Note that arrays appear in the form `[v1 v2 v3 ...]` # when printed with `fmt.Println`. $ go run arrays.go emp: [0 0 0 0 0] set: [0 0 0 0 100] get: 100 len: 5 dcl: [1 2 3 4 5] dcl: [1 2 3 4 5] idx: [100 0 0 400 500] 2d: [[0 1 2] [1 2 3]] 2d: [[1 2 3] [1 2 3]] ================================================ FILE: examples/atomic-counters/atomic-counters.go ================================================ // The primary mechanism for managing state in Go is // communication over channels. We saw this for example // with [worker pools](worker-pools). There are a few other // options for managing state though. Here we'll // look at using the `sync/atomic` package for _atomic // counters_ accessed by multiple goroutines. package main import ( "fmt" "sync" "sync/atomic" ) func main() { // We'll use an atomic integer type to represent our // (always-positive) counter. var ops atomic.Uint64 // A WaitGroup will help us wait for all goroutines // to finish their work. var wg sync.WaitGroup // We'll start 50 goroutines that each increment the // counter exactly 1000 times. for range 50 { wg.Go(func() { for range 1000 { // To atomically increment the counter we use `Add`. ops.Add(1) } }) } // Wait until all the goroutines are done. wg.Wait() // Here no goroutines are writing to 'ops', but using // `Load` it's safe to atomically read a value even while // other goroutines are (atomically) updating it. fmt.Println("ops:", ops.Load()) } ================================================ FILE: examples/atomic-counters/atomic-counters.hash ================================================ d898b7b79368f70c6f6d1c181575f7b83c592718 yiGAVfTH49v ================================================ FILE: examples/atomic-counters/atomic-counters.sh ================================================ # We expect to get exactly 50,000 operations. Had we # used a non-atomic integer and incremented it with # `ops++`, we'd likely get a different number, # changing between runs, because the goroutines # would interfere with each other. Moreover, we'd # get data race failures when running with the # `-race` flag. $ go run atomic-counters.go ops: 50000 # Next we'll look at mutexes, another tool for managing # state. ================================================ FILE: examples/base64-encoding/base64-encoding.go ================================================ // Go provides built-in support for [base64 // encoding/decoding](https://en.wikipedia.org/wiki/Base64). package main // This syntax imports the `encoding/base64` package with // the `b64` name instead of the default `base64`. It'll // save us some space below. import ( b64 "encoding/base64" "fmt" ) func main() { // 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 // 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() // 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)) } ================================================ FILE: examples/base64-encoding/base64-encoding.hash ================================================ 47f0317643bc5107af6fae64cb0fdad1260ead37 yztzkirFEvv ================================================ FILE: examples/base64-encoding/base64-encoding.sh ================================================ # The string encodes to slightly different values with the # standard and URL base64 encoders (trailing `+` vs `-`) # but they both decode to the original string as desired. $ go run base64-encoding.go YWJjMTIzIT8kKiYoKSctPUB+ abc123!?$*&()'-=@~ YWJjMTIzIT8kKiYoKSctPUB- abc123!?$*&()'-=@~ ================================================ FILE: examples/channel-buffering/channel-buffering.go ================================================ // By default channels are _unbuffered_, meaning that they // will only accept sends (`chan <-`) if there is a // corresponding receive (`<- chan`) ready to receive the // sent value. _Buffered channels_ accept a limited // number of values without a corresponding receiver for // those values. package main import "fmt" func main() { // 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" // Later we can receive these two values as usual. fmt.Println(<-messages) fmt.Println(<-messages) } ================================================ FILE: examples/channel-buffering/channel-buffering.hash ================================================ 558f4f1140a52e1804636f5720a10de0b37ebddb 3BRCdRnRszb ================================================ FILE: examples/channel-buffering/channel-buffering.sh ================================================ $ go run channel-buffering.go buffered channel ================================================ FILE: examples/channel-directions/channel-directions.go ================================================ // When using channels as function parameters, you can // specify if a channel is meant to only send or receive // values. This specificity increases the type-safety of // the program. package main import "fmt" // This `ping` function only accepts a channel for sending // values. It would be a compile-time error to try to // receive on this channel. func ping(pings chan<- string, msg string) { 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 } func main() { pings := make(chan string, 1) pongs := make(chan string, 1) ping(pings, "passed message") pong(pings, pongs) fmt.Println(<-pongs) } ================================================ FILE: examples/channel-directions/channel-directions.hash ================================================ d1b1580f72c3c101ea46480e6c2361f4f96b049a mjNJDHwUH4R ================================================ FILE: examples/channel-directions/channel-directions.sh ================================================ $ go run channel-directions.go passed message ================================================ FILE: examples/channel-synchronization/channel-synchronization.go ================================================ // 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 import ( "fmt" "time" ) // This is the function we'll run in a goroutine. The // `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") // Send a value to notify that we're done. done <- true } func main() { // Start a worker goroutine, giving it the channel to // notify on. done := make(chan bool, 1) go worker(done) // Block until we receive a notification from the // worker on the channel. <-done } ================================================ FILE: examples/channel-synchronization/channel-synchronization.hash ================================================ aa83d53fdee417727ec9a7cd90172d34c15a28c2 Nw-1DzIGk5f ================================================ FILE: examples/channel-synchronization/channel-synchronization.sh ================================================ $ go run channel-synchronization.go working...done # If you removed the `<- done` line from this program, # the program could exit before the `worker` finished # its work, or in some cases even before it started. ================================================ FILE: examples/channels/channels.go ================================================ // _Channels_ are the pipes that connect concurrent // goroutines. You can send values into channels from one // goroutine and receive those values into another // goroutine. package main 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) // _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) } ================================================ FILE: examples/channels/channels.hash ================================================ 4fa3a8956f7f1ded57e8dc72827329aef8497e18 MaLY7AiAkHM ================================================ FILE: examples/channels/channels.sh ================================================ # When we run the program the `"ping"` message is # successfully passed from one goroutine to another via # our channel. $ go run channels.go ping # By default sends and receives block until both the # sender and receiver are ready. This property allowed # us to wait at the end of our program for the `"ping"` # message without having to use any other synchronization. ================================================ FILE: examples/closing-channels/closing-channels.go ================================================ // _Closing_ a channel indicates that no more values // will be sent on it. This can be useful to communicate // completion to the channel's receivers. package main import "fmt" // In this example we'll use a `jobs` channel to // communicate work to be done from the `main()` goroutine // 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) // 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") // We await the worker using the // [synchronization](channel-synchronization) approach // we saw earlier. <-done // Reading from a closed channel succeeds immediately, // returning the zero value of the underlying type. // The optional second return value is `true` if the // value received was delivered by a successful send // operation to the channel, or `false` if it was a // zero value generated because the channel is closed // and empty. _, ok := <-jobs fmt.Println("received more jobs:", ok) } ================================================ FILE: examples/closing-channels/closing-channels.hash ================================================ 13f0ccf3674db8e9631a424c4070f9d423f7dc11 yZijZHYe22y ================================================ FILE: examples/closing-channels/closing-channels.sh ================================================ $ go run closing-channels.go sent job 1 received job 1 sent job 2 received job 2 sent job 3 received job 3 sent all jobs received all jobs received more jobs: false # The idea of closed channels leads naturally to our next # example: `range` over channels. ================================================ FILE: examples/closures/closures.go ================================================ // Go supports [_anonymous functions_](https://en.wikipedia.org/wiki/Anonymous_function), // which can form closures. // Anonymous functions are useful when you want to define // a function inline without having to name it. package main import "fmt" // This function `intSeq` returns another function, which // we define anonymously in the body of `intSeq`. The // returned function _closes over_ the variable `i` to // form a closure. func intSeq() func() int { 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() // 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()) } ================================================ FILE: examples/closures/closures.hash ================================================ 6514e124c8127250a2eecfadc9708181e51f9603 NpgpzS8ZG8y ================================================ FILE: examples/closures/closures.sh ================================================ $ go run closures.go 1 2 3 1 # The last feature of functions we'll look at for now is # recursion. ================================================ FILE: examples/command-line-arguments/command-line-arguments.go ================================================ // [_Command-line arguments_](https://en.wikipedia.org/wiki/Command-line_interface#Arguments) // are a common way to parameterize execution of programs. // For example, `go run hello.go` uses `run` and // `hello.go` arguments to the `go` program. package main import ( "fmt" "os" ) 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:] // You can get individual args with normal indexing. arg := os.Args[3] fmt.Println(argsWithProg) fmt.Println(argsWithoutProg) fmt.Println(arg) } ================================================ FILE: examples/command-line-arguments/command-line-arguments.hash ================================================ ad871e829d1457d97d0f1c1af77e39f6942ac5a5 UYCEvh9d2Zb ================================================ FILE: examples/command-line-arguments/command-line-arguments.sh ================================================ # To experiment with command-line arguments it's best to # build a binary with `go build` first. $ go build command-line-arguments.go $ ./command-line-arguments a b c d [./command-line-arguments a b c d] [a b c d] c # Next we'll look at more advanced command-line processing # with flags. ================================================ FILE: examples/command-line-flags/command-line-flags.go ================================================ // [_Command-line flags_](https://en.wikipedia.org/wiki/Command-line_interface#Command-line_option) // are a common way to specify options for command-line // programs. For example, in `wc -l` the `-l` is a // command-line flag. package main // Go provides a `flag` package supporting basic // command-line flag parsing. We'll use this package to // implement our example command-line program. import ( "flag" "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") // This declares `numb` and `fork` flags, using a // similar approach to the `word` flag. numbPtr := flag.Int("numb", 42, "an int") forkPtr := flag.Bool("fork", false, "a bool") // It's also possible to declare an option that uses an // existing var declared elsewhere in the program. // Note that we need to pass in a pointer to the flag // declaration function. var svar string flag.StringVar(&svar, "svar", "bar", "a string var") // Once all flags are declared, call `flag.Parse()` // 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:", *forkPtr) fmt.Println("svar:", svar) fmt.Println("tail:", flag.Args()) } ================================================ FILE: examples/command-line-flags/command-line-flags.hash ================================================ 9cca50e58f488570cc8e92dde37582ea5ee04bf3 IUPZlYSigc3 ================================================ FILE: examples/command-line-flags/command-line-flags.sh ================================================ # To experiment with the command-line flags program it's # best to first compile it and then run the resulting # binary directly. $ go build command-line-flags.go # Try out the built program by first giving it values for # all flags. $ ./command-line-flags -word=opt -numb=7 -fork -svar=flag word: opt numb: 7 fork: true svar: flag tail: [] # Note that if you omit flags they automatically take # their default values. $ ./command-line-flags -word=opt word: opt numb: 42 fork: false svar: bar tail: [] # Trailing positional arguments can be provided after # any flags. $ ./command-line-flags -word=opt a1 a2 a3 word: opt ... tail: [a1 a2 a3] # Note that the `flag` package requires all flags to # appear before positional arguments (otherwise the flags # will be interpreted as positional arguments). $ ./command-line-flags -word=opt a1 a2 a3 -numb=7 word: opt numb: 42 fork: false svar: bar tail: [a1 a2 a3 -numb=7] # Use `-h` or `--help` flags to get automatically # generated help text for the command-line program. $ ./command-line-flags -h Usage of ./command-line-flags: -fork=false: a bool -numb=42: an int -svar="bar": a string var -word="foo": a string # If you provide a flag that wasn't specified to the # `flag` package, the program will print an error message # and show the help text again. $ ./command-line-flags -wat flag provided but not defined: -wat Usage of ./command-line-flags: ... ================================================ FILE: examples/command-line-subcommands/command-line-subcommands.go ================================================ // 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) } } ================================================ FILE: examples/command-line-subcommands/command-line-subcommands.hash ================================================ eed015f91ba8a8d5a667dfc4dde745e341fded6e DkvdHKK-XCv ================================================ FILE: examples/command-line-subcommands/command-line-subcommands.sh ================================================ $ 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. ================================================ FILE: examples/constants/constants.go ================================================ // Go supports _constants_ of character, string, boolean, // and numeric values. package main import ( "fmt" "math" ) // `const` declares a constant value. const s string = "constant" func main() { fmt.Println(s) // A `const` statement can also appear inside a // function body. const n = 500000000 // Constant expressions perform arithmetic with // arbitrary precision. const d = 3e20 / n fmt.Println(d) // A numeric constant has no type until it's given // one, such as by an explicit 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)) } ================================================ FILE: examples/constants/constants.hash ================================================ 96fb6ba58e0860f9f6ed3f26db2f1cbdd9a0dbd6 LfvIxHlpomp ================================================ FILE: examples/constants/constants.sh ================================================ $ go run constant.go constant 6e+11 600000000000 -0.28470407323754404 ================================================ FILE: examples/context/context.go ================================================ // In the previous example we looked at setting up a simple // [HTTP server](http-server). HTTP servers are useful for // demonstrating the usage of `context.Context` for // controlling cancellation. A `Context` carries deadlines, // cancellation signals, and other request-scoped values // across API boundaries and goroutines. package main import ( "fmt" "net/http" "time" ) func hello(w http.ResponseWriter, req *http.Request) { // A `context.Context` is created for each request by // the `net/http` machinery, and is available with // the `Context()` method. ctx := req.Context() fmt.Println("server: hello handler started") defer fmt.Println("server: hello handler ended") // Wait for a few seconds before sending a reply to the // client. This could simulate some work the server is // doing. While working, keep an eye on the context's // `Done()` channel for a signal that we should cancel // the work and return as soon as possible. select { case <-time.After(10 * time.Second): fmt.Fprintf(w, "hello\n") case <-ctx.Done(): // The context's `Err()` method returns an error // that explains why the `Done()` channel was // closed. err := ctx.Err() fmt.Println("server:", err) internalError := http.StatusInternalServerError http.Error(w, err.Error(), internalError) } } func main() { // As before, we register our handler on the "/hello" // route, and start serving. http.HandleFunc("/hello", hello) http.ListenAndServe(":8090", nil) } ================================================ FILE: examples/context/context.hash ================================================ 94a35a6172346b56737ed907a2320bd30f98995d 7G1TlQrnbF1 ================================================ FILE: examples/context/context.sh ================================================ # Run the server in the background. $ go run context.go & # Simulate a client request to `/hello`, hitting # Ctrl+C shortly after starting to signal # cancellation. $ curl localhost:8090/hello server: hello handler started ^C server: context canceled server: hello handler ended ================================================ FILE: examples/custom-errors/custom-errors.go ================================================ // It's possible to define custom error types by // implementing the `Error()` method on them. Here's a // variant on the example above that uses a custom type // to explicitly represent an argument error. package main import ( "errors" "fmt" ) // A custom error type usually has the suffix "Error". type argError struct { arg int message string } // Adding this `Error` method makes `argError` implement // the `error` interface. func (e *argError) Error() string { return fmt.Sprintf("%d - %s", e.arg, e.message) } func f(arg int) (int, error) { if arg == 42 { // Return our custom error. return -1, &argError{arg, "can't work with it"} } return arg + 3, nil } func main() { // `errors.AsType` is a more advanced version of `errors.Is`. // It checks that a given error (or any error in its chain) // matches a specific error type and converts to a value // of that type, also returning `true`. If there's no match, the // second return value is `false`. _, err := f(42) if ae, ok := errors.AsType[*argError](err); ok { fmt.Println(ae.arg) fmt.Println(ae.message) } else { fmt.Println("err doesn't match argError") } } ================================================ FILE: examples/custom-errors/custom-errors.hash ================================================ fe71c150846bfa4d8dddfda4903fa8e30077a5b2 0WHh8JFOjQq ================================================ FILE: examples/custom-errors/custom-errors.sh ================================================ $ go run custom-errors.go 42 can't work with it ================================================ FILE: examples/defer/defer.go ================================================ // _Defer_ is used to ensure that a function call is // performed later in a program's execution, usually for // purposes of cleanup. `defer` is often used where e.g. // `ensure` and `finally` would be used in other languages. package main import ( "fmt" "os" "path/filepath" ) // Suppose we wanted to create a file, write to it, // and then close when we're done. Here's how we could // do that with `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. path := filepath.Join(os.TempDir(), "defer.txt") f := createFile(path) defer closeFile(f) writeFile(f) } func createFile(p string) *os.File { fmt.Println("creating") f, err := os.Create(p) if err != nil { panic(err) } return f } func writeFile(f *os.File) { fmt.Println("writing") fmt.Fprintln(f, "data") } func closeFile(f *os.File) { fmt.Println("closing") err := f.Close() // It's important to check for errors when closing a // file, even in a deferred function. if err != nil { panic(err) } } ================================================ FILE: examples/defer/defer.hash ================================================ b3687ac676cbe21ebb53a8d864fc15649c27c4a8 9_sJ5XnikSw ================================================ FILE: examples/defer/defer.sh ================================================ # Running the program confirms that the file is closed # after being written. $ go run defer.go creating writing closing ================================================ FILE: examples/directories/directories.go ================================================ // Go has several useful functions for working with // *directories* in the file system. package main import ( "fmt" "io/fs" "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(os.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.DirEntry` objects. c, err := os.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 = os.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. `WalkDir` accepts // a callback function to handle every file or // directory visited. fmt.Println("Visiting subdir") err = filepath.WalkDir("subdir", visit) } // `visit` is called for every file or directory found // recursively by `filepath.WalkDir`. func visit(path string, d fs.DirEntry, err error) error { if err != nil { return err } fmt.Println(" ", path, d.IsDir()) return nil } ================================================ FILE: examples/directories/directories.hash ================================================ 21e35905e45d7b391823ea761d69199e3712d92c ORNj2BPrLQr ================================================ FILE: examples/directories/directories.sh ================================================ $ 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 ================================================ FILE: examples/embed-directive/embed-directive.go ================================================ // `//go:embed` is a [compiler // directive](https://pkg.go.dev/cmd/compile#hdr-Compiler_Directives) that // allows programs to include arbitrary files and folders in the Go binary at // build time. Read more about the embed directive // [here](https://pkg.go.dev/embed). package main // Import the `embed` package; if you don't use any exported // identifiers from this package, you can do a blank import with `_ "embed"`. import ( "embed" ) // `embed` directives accept paths relative to the directory containing the // Go source file. This directive embeds the contents of the file into the // `string` variable immediately following it. // //go:embed folder/single_file.txt var fileString string // Or embed the contents of the file into a `[]byte`. // //go:embed folder/single_file.txt var fileByte []byte // We can also embed multiple files or even folders with wildcards. This uses // a variable of the [embed.FS type](https://pkg.go.dev/embed#FS), which // implements a simple virtual file system. // //go:embed folder/single_file.txt //go:embed folder/*.hash var folder embed.FS func main() { // Print out the contents of `single_file.txt`. print(fileString) print(string(fileByte)) // Retrieve some files from the embedded folder. content1, _ := folder.ReadFile("folder/file1.hash") print(string(content1)) content2, _ := folder.ReadFile("folder/file2.hash") print(string(content2)) } ================================================ FILE: examples/embed-directive/embed-directive.hash ================================================ 69526bd78ac861c85bb12b96e9f1273e8aecc5a6 6m2ll-D52BB ================================================ FILE: examples/embed-directive/embed-directive.sh ================================================ # Use these commands to run the example. # (Note: due to limitation on go playground, # this example can only be run on your local machine.) $ mkdir -p folder $ echo "hello go" > folder/single_file.txt $ echo "123" > folder/file1.hash $ echo "456" > folder/file2.hash $ go run embed-directive.go hello go hello go 123 456 ================================================ FILE: examples/embed-directive/folder/file1.hash ================================================ 123 ================================================ FILE: examples/embed-directive/folder/file2.hash ================================================ 456 ================================================ FILE: examples/embed-directive/folder/single_file.txt ================================================ hello go ================================================ FILE: examples/enums/enums.go ================================================ // _Enumerated types_ (enums) are a special case of // [sum types](https://en.wikipedia.org/wiki/Algebraic_data_type). // An enum is a type that has a fixed number of possible // values, each with a distinct name. Go doesn't have an // enum type as a distinct language feature, but enums // are simple to implement using existing language idioms. package main import "fmt" // Our enum type `ServerState` has an underlying `int` type. type ServerState int // The possible values for `ServerState` are defined as // constants. The special keyword [iota](https://go.dev/ref/spec#Iota) // generates successive constant values automatically; in this // case 0, 1, 2 and so on. const ( StateIdle ServerState = iota StateConnected StateError StateRetrying ) // By implementing the [fmt.Stringer](https://pkg.go.dev/fmt#Stringer) // interface, values of `ServerState` can be printed out or converted // to strings. // // This can get cumbersome if there are many possible values. In such // cases the [stringer tool](https://pkg.go.dev/golang.org/x/tools/cmd/stringer) // can be used in conjunction with `go:generate` to automate the // process. See [this post](https://eli.thegreenplace.net/2021/a-comprehensive-guide-to-go-generate) // for a longer explanation. var stateName = map[ServerState]string{ StateIdle: "idle", StateConnected: "connected", StateError: "error", StateRetrying: "retrying", } func (ss ServerState) String() string { return stateName[ss] } func main() { ns := transition(StateIdle) fmt.Println(ns) // If we have a value of type `int`, we cannot pass it to `transition` - the // compiler will complain about type mismatch. This provides some degree of // compile-time type safety for enums. ns2 := transition(ns) fmt.Println(ns2) } // transition emulates a state transition for a // server; it takes the existing state and returns // a new state. func transition(s ServerState) ServerState { switch s { case StateIdle: return StateConnected case StateConnected, StateRetrying: // Suppose we check some predicates here to // determine the next state... return StateIdle case StateError: return StateError default: panic(fmt.Errorf("unknown state: %s", s)) } } ================================================ FILE: examples/enums/enums.hash ================================================ ee42927ee1c864794570e23e8dadb2d20d64a4fd prQMptP_p1s ================================================ FILE: examples/enums/enums.sh ================================================ $ go run enums.go connected idle ================================================ FILE: examples/environment-variables/environment-variables.go ================================================ // [Environment variables](https://en.wikipedia.org/wiki/Environment_variable) // are a universal mechanism for [conveying configuration // information to Unix programs](https://www.12factor.net/config). // Let's look at how to set, get, and list environment variables. package main import ( "fmt" "os" "strings" ) 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")) // 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.SplitN` them to // get the key and value. Here we print all the keys. fmt.Println() for _, e := range os.Environ() { pair := strings.SplitN(e, "=", 2) fmt.Println(pair[0]) } } ================================================ FILE: examples/environment-variables/environment-variables.hash ================================================ f480d3803659977183a4bc5c14da26c80b1d31fe 2jmwXM264NC ================================================ FILE: examples/environment-variables/environment-variables.sh ================================================ # Running the program shows that we pick up the value # for `FOO` that we set in the program, but that # `BAR` is empty. $ go run environment-variables.go FOO: 1 BAR: # The list of keys in the environment will depend on your # particular machine. TERM_PROGRAM PATH SHELL ... FOO # If we set `BAR` in the environment first, the running # program picks that value up. $ BAR=2 go run environment-variables.go FOO: 1 BAR: 2 ... ================================================ FILE: examples/epoch/epoch.go ================================================ // A common requirement in programs is getting the number // of seconds, milliseconds, or nanoseconds since the // [Unix epoch](https://en.wikipedia.org/wiki/Unix_time). // Here's how to do it in Go. package main import ( "fmt" "time" ) func main() { // Use `time.Now` with `Unix`, `UnixMilli` or `UnixNano` // to get elapsed time since the Unix epoch in seconds, // milliseconds or nanoseconds, respectively. now := time.Now() fmt.Println(now) fmt.Println(now.Unix()) fmt.Println(now.UnixMilli()) fmt.Println(now.UnixNano()) // You can also convert integer seconds or nanoseconds // since the epoch into the corresponding `time`. fmt.Println(time.Unix(now.Unix(), 0)) fmt.Println(time.Unix(0, now.UnixNano())) } ================================================ FILE: examples/epoch/epoch.hash ================================================ a67ae165a1f00c205a344327d9d638f4eb931b5c lRmD1EWHHPz ================================================ FILE: examples/epoch/epoch.sh ================================================ $ go run epoch.go 2012-10-31 16:13:58.292387 +0000 UTC 1351700038 1351700038292 1351700038292387000 2012-10-31 16:13:58 +0000 UTC 2012-10-31 16:13:58.292387 +0000 UTC # Next we'll look at another time-related task: time # parsing and formatting. ================================================ FILE: examples/errors/errors.go ================================================ // In Go it's idiomatic to communicate errors via an // explicit, separate return value. This contrasts with // the exceptions used in languages like Java, Python and // Ruby and the overloaded single result / error value // sometimes used in C. Go's approach makes it easy to // see which functions return errors and to handle them // using the same language constructs employed for other, // non-error tasks. // // See the documentation of the [errors package](https://pkg.go.dev/errors) // and [this blog post](https://go.dev/blog/go1.13-errors) for additional // details. package main import ( "errors" "fmt" ) // By convention, errors are the last return value and // have type `error`, a built-in interface. func f(arg int) (int, error) { if arg == 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 sentinel error is a predeclared variable that is used to // signify a specific error condition. var ErrOutOfTea = errors.New("no more tea available") var ErrPower = errors.New("can't boil water") func makeTea(arg int) error { if arg == 2 { return ErrOutOfTea } else if arg == 4 { // We can wrap errors with higher-level errors to add // context. The simplest way to do this is with the // `%w` verb in `fmt.Errorf`. Wrapped errors // create a logical chain (A wraps B, which wraps C, etc.) // that can be queried with functions like `errors.Is` // and `errors.AsType`. return fmt.Errorf("making tea: %w", ErrPower) } return nil } func main() { for _, i := range []int{7, 42} { // It's idiomatic to use an inline error check in the `if` // line. if r, e := f(i); e != nil { fmt.Println("f failed:", e) } else { fmt.Println("f worked:", r) } } for i := range 5 { if err := makeTea(i); err != nil { // `errors.Is` checks that a given error (or any error in its chain) // matches a specific error value. This is especially useful with wrapped or // nested errors, allowing you to identify specific error types or sentinel // errors in a chain of errors. if errors.Is(err, ErrOutOfTea) { fmt.Println("We should buy new tea!") } else if errors.Is(err, ErrPower) { fmt.Println("Now it is dark.") } else { fmt.Printf("unknown error: %s\n", err) } continue } fmt.Println("Tea is ready!") } } ================================================ FILE: examples/errors/errors.hash ================================================ b426ab07abec1cf35659afaaa5a810e116d61e8e xWXAMmcrbTs ================================================ FILE: examples/errors/errors.sh ================================================ $ go run errors.go f worked: 10 f failed: can't work with 42 Tea is ready! Tea is ready! We should buy new tea! Tea is ready! Now it is dark. ================================================ FILE: examples/execing-processes/execing-processes.go ================================================ // In the previous example we looked at // [spawning external processes](spawning-processes). We // do this when we need an external process accessible to // a running Go process. Sometimes we just want to // completely replace the current Go process with another // (perhaps non-Go) one. To do this we'll use Go's // implementation of the classic // exec // function. package main import ( "os" "os/exec" "syscall" ) 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) } // `Exec` requires arguments in slice form (as // opposed 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() // 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) } } ================================================ FILE: examples/execing-processes/execing-processes.hash ================================================ 568ae983493addff02d2ce8df57f41daf537f077 s9qg7olf1dM ================================================ FILE: examples/execing-processes/execing-processes.sh ================================================ # When we run our program it is replaced by `ls`. $ go run execing-processes.go total 16 drwxr-xr-x 4 mark 136B Oct 3 16:29 . drwxr-xr-x 91 mark 3.0K Oct 3 12:50 .. -rw-r--r-- 1 mark 1.3K Oct 3 16:28 execing-processes.go # Note that Go does not offer a classic Unix `fork` # function. Usually this isn't an issue though, since # starting goroutines, spawning processes, and exec'ing # processes covers most use cases for `fork`. ================================================ FILE: examples/exit/exit.go ================================================ // Use `os.Exit` to immediately exit with a given // status. package main import ( "fmt" "os" ) func main() { // `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) } // Note that unlike e.g. C, Go does not use an integer // return value from `main` to indicate exit status. If // you'd like to exit with a non-zero status you should // use `os.Exit`. ================================================ FILE: examples/exit/exit.hash ================================================ 16f2c50f58d9d113f2cdd5367ddd95a220d89b19 b9aYzlENkb__R ================================================ FILE: examples/exit/exit.sh ================================================ # If you run `exit.go` using `go run`, the exit # will be picked up by `go` and printed. $ go run exit.go exit status 3 # By building and executing a binary you can see # the status in the terminal. $ go build exit.go $ ./exit $ echo $? 3 # Note that the `!` from our program never got printed. ================================================ FILE: examples/file-paths/file-paths.go ================================================ // 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) } ================================================ FILE: examples/file-paths/file-paths.hash ================================================ 10823f6a3f4daea097a91374efa88c4361932488 5h3lUytvmyO ================================================ FILE: examples/file-paths/file-paths.sh ================================================ $ 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 ================================================ FILE: examples/for/for.go ================================================ // `for` is Go's only looping construct. Here are // some basic types of `for` loops. package main import "fmt" func main() { // The most basic type, with a single condition. i := 1 for i <= 3 { fmt.Println(i) i = i + 1 } // A classic initial/condition/after `for` loop. for j := 0; j < 3; j++ { fmt.Println(j) } // Another way of accomplishing the basic "do this // N times" iteration is `range` over an integer. for i := range 3 { fmt.Println("range", i) } // `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 := range 6 { if n%2 == 0 { continue } fmt.Println(n) } } ================================================ FILE: examples/for/for.hash ================================================ 8eeb5be15c3c5fc3f9d0d8009dfcec771dc5e03d _F2rYHNilKa ================================================ FILE: examples/for/for.sh ================================================ $ go run for.go 1 2 3 0 1 2 range 0 range 1 range 2 loop 1 3 5 # We'll see some other `for` forms later when we look at # `range` statements, channels, and other data # structures. ================================================ FILE: examples/functions/functions.go ================================================ // _Functions_ are central in Go. We'll learn about // functions with a few different examples. package main import "fmt" // Here's a function that takes two `int`s and returns // 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 } // When you have multiple consecutive parameters of // the same type, you may omit the type name for the // like-typed parameters up to the final parameter that // declares the type. func plusPlus(a, b, c int) int { 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) res = plusPlus(1, 2, 3) fmt.Println("1+2+3 =", res) } ================================================ FILE: examples/functions/functions.hash ================================================ 94ade6d23721234a9612c9f77431106308b84953 -o49-dQfGbK ================================================ FILE: examples/functions/functions.sh ================================================ $ go run functions.go 1+2 = 3 1+2+3 = 6 # There are several other features to Go functions. One is # multiple return values, which we'll look at next. ================================================ FILE: examples/generics/generics.go ================================================ // Starting with version 1.18, Go has added support for // _generics_, also known as _type parameters_. package main import "fmt" // As an example of a generic function, `SlicesIndex` takes // a slice of any `comparable` type and an element of that // type and returns the index of the first occurrence of // v in s, or -1 if not present. The `comparable` constraint // means that we can compare values of this type with the // `==` and `!=` operators. For a more thorough explanation // of this type signature, see [this blog post](https://go.dev/blog/deconstructing-type-parameters). // Note that this function exists in the standard library // as [slices.Index](https://pkg.go.dev/slices#Index). func SlicesIndex[S ~[]E, E comparable](s S, v E) int { for i := range s { if v == s[i] { return i } } return -1 } // As an example of a generic type, `List` is a // singly-linked list with values of any type. type List[T any] struct { head, tail *element[T] } type element[T any] struct { next *element[T] val T } // We can define methods on generic types just like we // do on regular types, but we have to keep the type // parameters in place. The type is `List[T]`, not `List`. func (lst *List[T]) Push(v T) { if lst.tail == nil { lst.head = &element[T]{val: v} lst.tail = lst.head } else { lst.tail.next = &element[T]{val: v} lst.tail = lst.tail.next } } // AllElements returns all the List elements as a slice. // In the next example we'll see a more idiomatic way // of iterating over all elements of custom types. func (lst *List[T]) AllElements() []T { var elems []T for e := lst.head; e != nil; e = e.next { elems = append(elems, e.val) } return elems } func main() { var s = []string{"foo", "bar", "zoo"} // When invoking generic functions, we can often rely // on _type inference_. Note that we don't have to // specify the types for `S` and `E` when // calling `SlicesIndex` - the compiler infers them // automatically. fmt.Println("index of zoo:", SlicesIndex(s, "zoo")) // ... though we could also specify them explicitly. _ = SlicesIndex[[]string, string](s, "zoo") lst := List[int]{} lst.Push(10) lst.Push(13) lst.Push(23) fmt.Println("list:", lst.AllElements()) } ================================================ FILE: examples/generics/generics.hash ================================================ 1ad71763360077271687c5e9d147c89c0b580b0a 7v7vElzhAeO ================================================ FILE: examples/generics/generics.sh ================================================ $ go run generics.go index of zoo: 2 list: [10 13 23] ================================================ FILE: examples/goroutines/goroutines.go ================================================ // A _goroutine_ is a lightweight thread of execution. package main import ( "fmt" "time" ) func f(from string) { for i := range 3 { 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") // 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") // Our two function calls are running asynchronously in // separate goroutines now. Wait for them to finish // (for a more robust approach, use a [WaitGroup](waitgroups)). time.Sleep(time.Second) fmt.Println("done") } ================================================ FILE: examples/goroutines/goroutines.hash ================================================ b7455068d7f944d7c1a2764e5ec05bee53296e62 0fx_WokYVFO ================================================ FILE: examples/goroutines/goroutines.sh ================================================ # When we run this program, we see the output of the # blocking call first, then the output of the two # goroutines. The goroutines' output may be interleaved, # because goroutines are being run concurrently by the # Go runtime. $ go run goroutines.go direct : 0 direct : 1 direct : 2 goroutine : 0 going goroutine : 1 goroutine : 2 done # Next we'll look at a complement to goroutines in # concurrent Go programs: channels. ================================================ FILE: examples/hello-world/hello-world.go ================================================ // Our first program will print the classic "hello world" // message. Here's the full source code. package main import "fmt" func main() { fmt.Println("hello world") } ================================================ FILE: examples/hello-world/hello-world.hash ================================================ 3eb6e21f5f89b9a4bf64f267972a24211f0032e7 NeviD0awXjt ================================================ FILE: examples/hello-world/hello-world.sh ================================================ # To run the program, put the code in `hello-world.go` and # use `go run`. $ go run hello-world.go hello world # Sometimes we'll want to build our programs into # binaries. We can do this using `go build`. $ go build hello-world.go $ ls hello-world hello-world.go # We can then execute the built binary directly. $ ./hello-world hello world # Now that we can run and build basic Go programs, let's # learn more about the language. ================================================ FILE: examples/http-client/http-client.go ================================================ // 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("https://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) } } ================================================ FILE: examples/http-client/http-client.hash ================================================ 1497e193431e4740f593039f613773daaf97772e vFW_el7oHMk ================================================ FILE: examples/http-client/http-client.sh ================================================ $ go run http-clients.go Response status: 200 OK Go by Example ================================================ FILE: examples/http-server/http-server.go ================================================ // 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) } ================================================ FILE: examples/http-server/http-server.hash ================================================ 7694e4f5c3907e999331bbab9ead9743b6e9c6b7 s3xMMt9Ytry ================================================ FILE: examples/http-server/http-server.sh ================================================ # Run the server in the background. $ go run http-server.go & # Access the `/hello` route. $ curl localhost:8090/hello hello ================================================ FILE: examples/if-else/if-else.go ================================================ // Branching with `if` and `else` in Go is // straight-forward. package main import "fmt" func main() { // Here's a basic example. if 7%2 == 0 { fmt.Println("7 is even") } else { fmt.Println("7 is odd") } // You can have an `if` statement without an else. if 8%4 == 0 { fmt.Println("8 is divisible by 4") } // Logical operators like `&&` and `||` are often // useful in conditions. if 8%2 == 0 || 7%2 == 0 { fmt.Println("either 8 or 7 are even") } // A statement can precede conditionals; any variables // declared in this statement are available in the current // and all subsequent branches. if num := 9; num < 0 { fmt.Println(num, "is negative") } else if num < 10 { fmt.Println(num, "has 1 digit") } else { fmt.Println(num, "has multiple digits") } } // Note that you don't need parentheses around conditions // in Go, but that the braces are required. ================================================ FILE: examples/if-else/if-else.hash ================================================ fd9e491f9891e6a9593c2c1d640c1df113ce3ccf RKgKzCe7qcF ================================================ FILE: examples/if-else/if-else.sh ================================================ $ go run if-else.go 7 is odd 8 is divisible by 4 either 8 or 7 are even 9 has 1 digit # There is no [ternary if](https://en.wikipedia.org/wiki/%3F:) # in Go, so you'll need to use a full `if` statement even # for basic conditions. ================================================ FILE: examples/interfaces/interfaces.go ================================================ // _Interfaces_ are named collections of method // signatures. package main import ( "fmt" "math" ) // Here's a basic interface for geometric shapes. type geometry interface { area() float64 perim() float64 } // For our example we'll implement this interface on // `rect` and `circle` types. type rect struct { width, height float64 } type circle struct { radius float64 } // To implement an interface in Go, we just need to // implement all the methods in the interface. Here we // implement `geometry` on `rect`s. func (r rect) area() float64 { return r.width * r.height } func (r rect) perim() float64 { return 2*r.width + 2*r.height } // The implementation for `circle`s. func (c circle) area() float64 { return math.Pi * c.radius * c.radius } func (c circle) perim() float64 { return 2 * math.Pi * c.radius } // If a variable has an interface type, then we can call // methods that are in the named interface. Here's a // generic `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()) } // Sometimes it's useful to know the runtime type of an // interface value. One option is using a *type assertion* // as shown here; another is a [type `switch`](switch). func detectCircle(g geometry) { if c, ok := g.(circle); ok { fmt.Println("circle with radius", c.radius) } } func main() { 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) detectCircle(r) detectCircle(c) } ================================================ FILE: examples/interfaces/interfaces.hash ================================================ 6324a4bdb756a0ec2ccc60e13c97d2650e730ed6 xAAbgd7GOKD ================================================ FILE: examples/interfaces/interfaces.sh ================================================ $ go run interfaces.go {3 4} 12 14 {5} 78.53981633974483 31.41592653589793 circle with radius 5 # To understand how Go's interfaces work under the hood, # check out this [blog post](https://research.swtch.com/interfaces). ================================================ FILE: examples/json/json.go ================================================ // Go offers built-in support for JSON encoding and // decoding, including to and from built-in and custom // data types. package main import ( "encoding/json" "fmt" "os" "strings" ) // We'll use these two structs to demonstrate encoding and // decoding of custom types below. type response1 struct { Page int Fruits []string } // Only exported fields will be encoded/decoded in JSON. // Fields must start with capital letters to be exported. type response2 struct { Page int `json:"page"` Fruits []string `json:"fruits"` } func main() { // First we'll look at encoding basic data types to // JSON strings. Here are some examples for atomic // values. bolB, _ := json.Marshal(true) fmt.Println(string(bolB)) intB, _ := json.Marshal(1) fmt.Println(string(intB)) fltB, _ := json.Marshal(2.34) fmt.Println(string(fltB)) strB, _ := json.Marshal("gopher") fmt.Println(string(strB)) // And here are some for slices and maps, which encode // to JSON arrays and objects as you'd expect. slcD := []string{"apple", "peach", "pear"} slcB, _ := json.Marshal(slcD) fmt.Println(string(slcB)) mapD := map[string]int{"apple": 5, "lettuce": 7} mapB, _ := json.Marshal(mapD) fmt.Println(string(mapB)) // The JSON package can automatically encode your // custom data types. It will only include exported // fields in the encoded output and will by default // use those names as the JSON keys. res1D := &response1{ Page: 1, Fruits: []string{"apple", "peach", "pear"}} res1B, _ := json.Marshal(res1D) fmt.Println(string(res1B)) // You can use tags on struct field declarations // to customize the encoded JSON key names. Check the // definition of `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"]}`) // 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) // 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 // 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]) // 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) // Streaming reads from `os.Reader`s like `os.Stdin` // or HTTP request bodies is done with `json.Decoder`. dec := json.NewDecoder(strings.NewReader(str)) res1 := response2{} dec.Decode(&res1) fmt.Println(res1) } ================================================ FILE: examples/json/json.hash ================================================ db25fb3a8b52215441ebe0a5d6a4d4f1a8be5917 zwf9dZ4pUPW ================================================ FILE: examples/json/json.sh ================================================ $ go run json.go true 1 2.34 "gopher" ["apple","peach","pear"] {"apple":5,"lettuce":7} {"Page":1,"Fruits":["apple","peach","pear"]} {"page":1,"fruits":["apple","peach","pear"]} map[num:6.13 strs:[a b]] 6.13 a {1 [apple peach]} apple {"apple":5,"lettuce":7} {1 [apple peach]} # We've covered the basic of JSON in Go here, but check # out the [JSON and Go](https://go.dev/blog/json) # blog post and [JSON package docs](https://pkg.go.dev/encoding/json) # for more. ================================================ FILE: examples/line-filters/line-filters.go ================================================ // A _line filter_ is a common type of program that reads // input on stdin, processes it, and then prints some // derived result to stdout. `grep` and `sed` are common // line filters. // Here's an example line filter in Go that writes a // capitalized version of all input text. You can use this // pattern to write your own Go line filters. package main import ( "bufio" "fmt" "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) 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) } // 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) } } ================================================ FILE: examples/line-filters/line-filters.hash ================================================ 42fd593180c40f71839f05447cc0a70d7cd213d1 kNcupWRsYPP ================================================ FILE: examples/line-filters/line-filters.sh ================================================ # To try out our line filter, first make a file with a few # lowercase lines. $ echo 'hello' > /tmp/lines $ echo 'filter' >> /tmp/lines # Then use the line filter to get uppercase lines. $ cat /tmp/lines | go run line-filters.go HELLO FILTER ================================================ FILE: examples/logging/logging.go ================================================ // The Go standard library provides straightforward // tools for outputting logs from Go programs, with // the [log](https://pkg.go.dev/log) package for // free-form output and the // [log/slog](https://pkg.go.dev/log/slog) package for // structured output. package main import ( "bytes" "fmt" "log" "os" "log/slog" ) func main() { // Simply invoking functions like `Println` from the // `log` package uses the _standard_ logger, which // is already pre-configured for reasonable logging // output to `os.Stderr`. Additional methods like // `Fatal*` or `Panic*` will exit the program after // logging. log.Println("standard logger") // Loggers can be configured with _flags_ to set // their output format. By default, the standard // logger has the `log.Ldate` and `log.Ltime` flags // set, and these are collected in `log.LstdFlags`. // We can change its flags to emit time with // microsecond accuracy, for example. log.SetFlags(log.LstdFlags | log.Lmicroseconds) log.Println("with micro") // It also supports emitting the file name and // line from which the `log` function is called. log.SetFlags(log.LstdFlags | log.Lshortfile) log.Println("with file/line") // It may be useful to create a custom logger and // pass it around. When creating a new logger, we // can set a _prefix_ to distinguish its output // from other loggers. mylog := log.New(os.Stdout, "my:", log.LstdFlags) mylog.Println("from mylog") // We can set the prefix // on existing loggers (including the standard one) // with the `SetPrefix` method. mylog.SetPrefix("ohmy:") mylog.Println("from mylog") // Loggers can have custom output targets; // any `io.Writer` works. var buf bytes.Buffer buflog := log.New(&buf, "buf:", log.LstdFlags) // This call writes the log output into `buf`. buflog.Println("hello") // This will actually show it on standard output. fmt.Print("from buflog:", buf.String()) // The `slog` package provides // _structured_ log output. For example, logging // in JSON format is straightforward. jsonHandler := slog.NewJSONHandler(os.Stderr, nil) myslog := slog.New(jsonHandler) myslog.Info("hi there") // In addition to the message, `slog` output can // contain an arbitrary number of key=value // pairs. myslog.Info("hello again", "key", "val", "age", 25) } ================================================ FILE: examples/logging/logging.hash ================================================ 38a7ef451859bb4c163df938b3a9d0e5ac293bef Qd0uCqBlYUn ================================================ FILE: examples/logging/logging.sh ================================================ # Sample output; the date and time # emitted will depend on when the example ran. $ go run logging.go 2023/08/22 10:45:16 standard logger 2023/08/22 10:45:16.904141 with micro 2023/08/22 10:45:16 logging.go:40: with file/line my:2023/08/22 10:45:16 from mylog ohmy:2023/08/22 10:45:16 from mylog from buflog:buf:2023/08/22 10:45:16 hello # These are wrapped for clarity of presentation # on the website; in reality they are emitted # on a single line. {"time":"2023-08-22T10:45:16.904166391-07:00", "level":"INFO","msg":"hi there"} {"time":"2023-08-22T10:45:16.904178985-07:00", "level":"INFO","msg":"hello again", "key":"val","age":25} ================================================ FILE: examples/maps/maps.go ================================================ // _Maps_ are Go's built-in [associative data type](https://en.wikipedia.org/wiki/Associative_array) // (sometimes called _hashes_ or _dicts_ in other languages). package main import ( "fmt" "maps" ) func main() { // 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 // 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) // If the key doesn't exist, the // [zero value](https://go.dev/ref/spec#The_zero_value) of the // value type is returned. v3 := m["k3"] fmt.Println("v3:", v3) // 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) // To remove *all* key/value pairs from a map, use // the `clear` builtin. clear(m) 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) // 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) // The `maps` package contains a number of useful // utility functions for maps. n2 := map[string]int{"foo": 1, "bar": 2} if maps.Equal(n, n2) { fmt.Println("n == n2") } } ================================================ FILE: examples/maps/maps.hash ================================================ c8435b8cc754213b70c58c9a51dfa824c6f70dd7 5jpkxJ2T0Lv ================================================ FILE: examples/maps/maps.sh ================================================ # Note that maps appear in the form `map[k:v k:v]` when # printed with `fmt.Println`. $ go run maps.go map: map[k1:7 k2:13] v1: 7 v3: 0 len: 2 map: map[k1:7] map: map[] prs: false map: map[bar:2 foo:1] n == n2 ================================================ FILE: examples/methods/methods.go ================================================ // Go supports _methods_ defined on struct types. package main import "fmt" type rect struct { width, height int } // This `area` method has a _receiver type_ of `*rect`. func (r *rect) area() int { return r.width * r.height } // Methods can be defined for either pointer or value // receiver types. Here's an example of a value receiver. func (r rect) perim() int { return 2*r.width + 2*r.height } func main() { r := rect{width: 10, height: 5} // Here we call the 2 methods defined for our struct. fmt.Println("area: ", r.area()) fmt.Println("perim:", r.perim()) // Go automatically handles conversion between values // and pointers for method calls. You may want to use // a pointer receiver type to avoid copying on method // calls or to allow the method to mutate the // receiving struct. rp := &r fmt.Println("area: ", rp.area()) fmt.Println("perim:", rp.perim()) } ================================================ FILE: examples/methods/methods.hash ================================================ 8c5af60ad04b3e9baa62a85924f829711abe94d4 4wmDCAydC1e ================================================ FILE: examples/methods/methods.sh ================================================ $ go run methods.go area: 50 perim: 30 area: 50 perim: 30 # Next we'll look at Go's mechanism for grouping and # naming related sets of methods: interfaces. ================================================ FILE: examples/multiple-return-values/multiple-return-values.go ================================================ // Go has built-in support for _multiple return values_. // This feature is used often in idiomatic Go, for example // to return both result and error values from a function. package main import "fmt" // The `(int, int)` in this function signature shows that // the function returns 2 `int`s. func vals() (int, int) { return 3, 7 } func main() { // Here we use the 2 different return values from the // call with _multiple assignment_. a, b := vals() fmt.Println(a) fmt.Println(b) // If you only want a subset of the returned values, // use the blank identifier `_`. _, c := vals() fmt.Println(c) } ================================================ FILE: examples/multiple-return-values/multiple-return-values.hash ================================================ c6e4f5dd9c55b5d2aaeb7e939c216ec76f042501 vZdUvLB1WbK ================================================ FILE: examples/multiple-return-values/multiple-return-values.sh ================================================ $ go run multiple-return-values.go 3 7 7 # Accepting a variable number of arguments is another nice # feature of Go functions; we'll look at this next. ================================================ FILE: examples/mutexes/mutexes.go ================================================ // 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_](https://en.wikipedia.org/wiki/Mutual_exclusion) // to safely access data across multiple goroutines. package main import ( "fmt" "sync" ) // Container holds a map of counters; since we want to // update it concurrently from multiple goroutines, we // add a `Mutex` to synchronize access. // Note that mutexes must not be copied, so if this // `struct` is passed around, it should be done by // pointer. type Container struct { mu sync.Mutex counters map[string]int } func (c *Container) inc(name string) { // Lock the mutex before accessing `counters`; unlock // it at the end of the function using a [defer](defer) // statement. c.mu.Lock() defer c.mu.Unlock() c.counters[name]++ } func main() { c := Container{ // Note that the zero value of a mutex is usable as-is, so no // initialization is required here. counters: map[string]int{"a": 0, "b": 0}, } var wg sync.WaitGroup // This function increments a named counter // in a loop. doIncrement := func(name string, n int) { for range n { c.inc(name) } } // Run several goroutines concurrently; note // that they all access the same `Container`, // and two of them access the same counter. wg.Go(func() { doIncrement("a", 10000) }) wg.Go(func() { doIncrement("a", 10000) }) wg.Go(func() { doIncrement("b", 10000) }) // Wait for the goroutines to finish wg.Wait() fmt.Println(c.counters) } ================================================ FILE: examples/mutexes/mutexes.hash ================================================ 5271a346794ed097df21fb0f2b2d3d01c9bb361c u0VAVSWRlU0 ================================================ FILE: examples/mutexes/mutexes.sh ================================================ # Running the program shows that the counters # updated as expected. $ go run mutexes.go map[a:20000 b:10000] # Next we'll look at implementing this same state # management task using only goroutines and channels. ================================================ FILE: examples/non-blocking-channel-operations/non-blocking-channel-operations.go ================================================ // Basic sends and receives on channels are blocking. // However, we can use `select` with a `default` clause to // implement _non-blocking_ sends, receives, and even // non-blocking multi-way `select`s. package main import "fmt" func main() { messages := make(chan string) signals := make(chan bool) // Here's a non-blocking receive. If a value is // available on `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. 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") } } ================================================ FILE: examples/non-blocking-channel-operations/non-blocking-channel-operations.hash ================================================ 40588abf859a0280d8c71b79732e869eb2da9291 TFv6-7OVNVq ================================================ FILE: examples/non-blocking-channel-operations/non-blocking-channel-operations.sh ================================================ $ go run non-blocking-channel-operations.go no message received no message sent no activity ================================================ FILE: examples/number-parsing/number-parsing.go ================================================ // Parsing numbers from strings is a basic but common task // in many programs; here's how to do it in Go. package main // The built-in package `strconv` provides the number // parsing. import ( "fmt" "strconv" ) func main() { // 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) // `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) // `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) } ================================================ FILE: examples/number-parsing/number-parsing.hash ================================================ 146b42780ac43135d97f094c6d30db364882ea17 ZAMEid6Fpmu ================================================ FILE: examples/number-parsing/number-parsing.sh ================================================ $ go run number-parsing.go 1.234 123 456 789 135 strconv.ParseInt: parsing "wat": invalid syntax # Next we'll look at another common parsing task: URLs. ================================================ FILE: examples/panic/panic.go ================================================ // A `panic` typically means something went unexpectedly // wrong. Mostly we use it to fail fast on errors that // shouldn't occur during normal operation, or that we // aren't prepared to handle gracefully. package main import ( "os" "path/filepath" ) func main() { // We'll use panic throughout this site to check for // unexpected errors. This is the only program on the // site designed to panic. panic("a problem") // A common use of panic is to abort if a function // returns an error value that we don't know how to // (or want to) handle. Here's an example of // `panic`king if we get an unexpected error when creating a new file. path := filepath.Join(os.TempDir(), "file") _, err := os.Create(path) if err != nil { panic(err) } } ================================================ FILE: examples/panic/panic.hash ================================================ 39f5cb04eb753e4c35cfc6358f24d0b5985c8556 Z57OSC0ASwn ================================================ FILE: examples/panic/panic.sh ================================================ # Running this program will cause it to panic, print # an error message and goroutine traces, and exit with # a non-zero status. # When first panic in `main` fires, the program exits # without reaching the rest of the code. If you'd like # to see the program try to create a temp file, comment # the first panic out. $ go run panic.go panic: a problem goroutine 1 [running]: main.main() /.../panic.go:12 +0x47 ... exit status 2 # Note that unlike some languages which use exceptions # for handling of many errors, in Go it is idiomatic # to use error-indicating return values wherever possible. ================================================ FILE: examples/pointers/pointers.go ================================================ // Go supports pointers, // allowing you to pass references to values and records // within your program. package main import "fmt" // We'll show how pointers work in contrast to values with // 2 functions: `zeroval` and `zeroptr`. `zeroval` has an // `int` parameter, so arguments will be passed to it by // value. `zeroval` will get a copy of `ival` distinct // from the one in the calling function. func zeroval(ival int) { ival = 0 } // `zeroptr` in contrast has an `*int` parameter, meaning // that it takes an `int` pointer. The `*iptr` code in the // function body then _dereferences_ the pointer from its // memory address to the current value at that address. // Assigning a value to a dereferenced pointer changes the // value at the referenced address. func zeroptr(iptr *int) { *iptr = 0 } func main() { i := 1 fmt.Println("initial:", 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) // Pointers can be printed too. fmt.Println("pointer:", &i) } ================================================ FILE: examples/pointers/pointers.hash ================================================ 7f9855cfb983efc07415117e2be734f55a6bb64b OlWCLpxAyBz ================================================ FILE: examples/pointers/pointers.sh ================================================ # `zeroval` doesn't change the `i` in `main`, but # `zeroptr` does because it has a reference to # the memory address for that variable. $ go run pointers.go initial: 1 zeroval: 1 zeroptr: 0 pointer: 0x42131100 ================================================ FILE: examples/random-numbers/random-numbers.go ================================================ // Go's `math/rand/v2` package provides // [pseudorandom number](https://en.wikipedia.org/wiki/Pseudorandom_number_generator) // generation. package main import ( "fmt" "math/rand/v2" ) 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() // `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() // If you want a known seed, create a new // `rand.Source` and pass it into the `New` // constructor. `NewPCG` creates a new // [PCG](https://en.wikipedia.org/wiki/Permuted_congruential_generator) // source that requires a seed of two `uint64` // numbers. s2 := rand.NewPCG(42, 1024) r2 := rand.New(s2) fmt.Print(r2.IntN(100), ",") fmt.Print(r2.IntN(100)) fmt.Println() s3 := rand.NewPCG(42, 1024) r3 := rand.New(s3) fmt.Print(r3.IntN(100), ",") fmt.Print(r3.IntN(100)) fmt.Println() } ================================================ FILE: examples/random-numbers/random-numbers.hash ================================================ 76b8f86171ffcf9e7d5781fdf50c050a824acd9b TkgmNAl8euK ================================================ FILE: examples/random-numbers/random-numbers.sh ================================================ # Some of the generated numbers may be # different when you run the sample. $ go run random-numbers.go 68,56 0.8090228139659177 5.840125017402497,6.937056298890035 94,49 94,49 # See the [`math/rand/v2`](https://pkg.go.dev/math/rand/v2) # package docs for references on other random quantities # that Go can provide. ================================================ FILE: examples/range-over-built-in-types/range-over-built-in-types.go ================================================ // _range_ iterates over elements in a variety of // built-in data structures. Let's see how to // use `range` with some of the data structures // we've already learned. package main import "fmt" func main() { // Here we use `range` to sum the numbers in a slice. // Arrays work like this too. nums := []int{2, 3, 4} sum := 0 for _, num := range nums { sum += num } fmt.Println("sum:", sum) // `range` on arrays and slices provides both the // index and value for each entry. Above we didn't // need the index, so we ignored it with the // blank identifier `_`. Sometimes we actually want // the indexes though. for i, num := range nums { if num == 3 { fmt.Println("index:", i) } } // `range` on map iterates over key/value pairs. kvs := map[string]string{"a": "apple", "b": "banana"} for k, v := range kvs { fmt.Printf("%s -> %s\n", k, v) } // `range` can also iterate over just the keys of a map. for k := range kvs { fmt.Println("key:", k) } // `range` on strings iterates over Unicode code // points. The first value is the starting byte index // of the `rune` and the second the `rune` itself. // See [Strings and Runes](strings-and-runes) for more // details. for i, c := range "go" { fmt.Println(i, c) } } ================================================ FILE: examples/range-over-built-in-types/range-over-built-in-types.hash ================================================ 3d8c61f02f98892be9d3ff25d48da0bfb31bbd25 S171w0PjgsD ================================================ FILE: examples/range-over-built-in-types/range-over-built-in-types.sh ================================================ $ go run range-over-built-in-types.go sum: 9 index: 1 a -> apple b -> banana key: a key: b 0 103 1 111 ================================================ FILE: examples/range-over-channels/range-over-channels.go ================================================ // In a [previous](range-over-built-in-types) example we saw how `for` and // `range` provide iteration over basic data structures. // We can also use this syntax to iterate over // values received from a channel. package main 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) // 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) } } ================================================ FILE: examples/range-over-channels/range-over-channels.hash ================================================ 446dea3a7cb9e16ce3e17a6649c719e764936740 8vAhX6eX1wy ================================================ FILE: examples/range-over-channels/range-over-channels.sh ================================================ $ go run range-over-channels.go one two # This example also showed that it's possible to close # a non-empty channel but still have the remaining # values be received. ================================================ FILE: examples/range-over-iterators/range-over-iterators.go ================================================ // Starting with version 1.23, Go has added support for // [iterators](https://go.dev/blog/range-functions), // which lets us range over pretty much anything! package main import ( "fmt" "iter" "slices" "strings" ) // Let's look at the `List` type from the // [previous example](generics) again. In that example // we had an `AllElements` method that returned a slice // of all elements in the list. With Go iterators, we // can do it better - as shown below. type List[T any] struct { head, tail *element[T] } type element[T any] struct { next *element[T] val T } func (lst *List[T]) Push(v T) { if lst.tail == nil { lst.head = &element[T]{val: v} lst.tail = lst.head } else { lst.tail.next = &element[T]{val: v} lst.tail = lst.tail.next } } // All returns an _iterator_, which in Go is a function // with a [special signature](https://pkg.go.dev/iter#Seq). func (lst *List[T]) All() iter.Seq[T] { return func(yield func(T) bool) { // The iterator function takes another function as // a parameter, called `yield` by convention (but // the name can be arbitrary). It will call `yield` for // every element we want to iterate over, and note `yield`'s // return value for a potential early termination. for e := lst.head; e != nil; e = e.next { if !yield(e.val) { return } } } } // Iteration doesn't require an underlying data structure, // and doesn't even have to be finite! Here's a function // returning an iterator over Fibonacci numbers: it keeps // running as long as `yield` keeps returning `true`. func genFib() iter.Seq[int] { return func(yield func(int) bool) { a, b := 0, 1 for { if !yield(a) { return } a, b = b, a+b } } } func main() { lst := List[int]{} lst.Push(10) lst.Push(13) lst.Push(23) // Since `List.All` returns an iterator, we can use it // in a regular `range` loop. for e := range lst.All() { fmt.Println(e) } // Packages like [slices](https://pkg.go.dev/slices) have // a number of useful functions to work with iterators. // For example, `Collect` takes any iterator and collects // all its values into a slice. all := slices.Collect(lst.All()) fmt.Println("all:", all) // Standard library packages now expose iterator helpers // too. For example, `strings.SplitSeq` iterates over parts // of a byte slice without first building a result slice. for part := range strings.SplitSeq("go-by-example", "-") { fmt.Printf("part: %s\n", part) } for n := range genFib() { // Once the loop hits `break` or an early return, the `yield` function // passed to the iterator will return `false`. if n >= 10 { break } fmt.Println(n) } } ================================================ FILE: examples/range-over-iterators/range-over-iterators.hash ================================================ d7e65d4e52957e845b0e2ca925af9ce5eff43148 lHX3uDqdfUB ================================================ FILE: examples/range-over-iterators/range-over-iterators.sh ================================================ $ go run range-over-iterators.go 10 13 23 all: [10 13 23] part: go part: by part: example 0 1 1 2 3 5 8 ================================================ FILE: examples/rate-limiting/rate-limiting.go ================================================ // [_Rate limiting_](https://en.wikipedia.org/wiki/Rate_limiting) // is an important mechanism for controlling resource // utilization and maintaining quality of service. Go // elegantly supports rate limiting with goroutines, // channels, and [tickers](tickers). package main import ( "fmt" "time" ) func main() { // First we'll look at basic rate limiting. Suppose // we want to limit our handling of incoming requests. // We'll serve these requests off a channel of the // same name. requests := make(chan int, 5) for i := 1; i <= 5; i++ { requests <- i } close(requests) // This `limiter` channel will receive a value // every 200 milliseconds. This is the regulator in // our rate limiting scheme. limiter := time.Tick(200 * time.Millisecond) // By blocking on a receive from the `limiter` channel // before serving each request, we limit ourselves to // 1 request every 200 milliseconds. for req := range requests { <-limiter fmt.Println("request", req, time.Now()) } // We may want to allow short bursts of requests in // our rate limiting scheme while preserving the // overall rate limit. We can accomplish this by // buffering our limiter channel. This `burstyLimiter` // channel will allow bursts of up to 3 events. burstyLimiter := make(chan time.Time, 3) // Fill up the channel to represent allowed bursting. for range 3 { burstyLimiter <- time.Now() } // 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(200 * time.Millisecond) { burstyLimiter <- t } }() // Now simulate 5 more incoming requests. The first // 3 of these will benefit from the burst capability // of `burstyLimiter`. burstyRequests := make(chan int, 5) for i := 1; i <= 5; i++ { burstyRequests <- i } close(burstyRequests) for req := range burstyRequests { <-burstyLimiter fmt.Println("request", req, time.Now()) } } ================================================ FILE: examples/rate-limiting/rate-limiting.hash ================================================ c1eee474067ad718e57df5c55242ba4711e7bcb7 y9V3goQfy5m ================================================ FILE: examples/rate-limiting/rate-limiting.sh ================================================ # Running our program we see the first batch of requests # handled once every ~200 milliseconds as desired. $ go run rate-limiting.go request 1 2012-10-19 00:38:18.687438 +0000 UTC request 2 2012-10-19 00:38:18.887471 +0000 UTC request 3 2012-10-19 00:38:19.087238 +0000 UTC request 4 2012-10-19 00:38:19.287338 +0000 UTC request 5 2012-10-19 00:38:19.487331 +0000 UTC # For the second batch of requests we serve the first # 3 immediately because of the burstable rate limiting, # then serve the remaining 2 with ~200ms delays each. request 1 2012-10-19 00:38:20.487578 +0000 UTC request 2 2012-10-19 00:38:20.487645 +0000 UTC request 3 2012-10-19 00:38:20.487676 +0000 UTC request 4 2012-10-19 00:38:20.687483 +0000 UTC request 5 2012-10-19 00:38:20.887542 +0000 UTC ================================================ FILE: examples/reading-files/reading-files.go ================================================ // Reading and writing files are basic tasks needed for // many Go programs. First we'll look at some examples of // reading files. package main import ( "bufio" "fmt" "io" "os" "path/filepath" ) // Reading files requires checking most calls for errors. // This helper will streamline our error checks below. func check(e error) { if e != nil { panic(e) } } func main() { // Perhaps the most basic file reading task is // slurping a file's entire contents into memory. path := filepath.Join(os.TempDir(), "dat") dat, err := os.ReadFile(path) check(err) fmt.Print(string(dat)) // You'll often want more control over how and what // parts of a file are read. For these tasks, start // by `Open`ing a file to obtain an `os.File` value. f, err := os.Open(path) check(err) // Read some bytes from the beginning of the file. // Allow up to 5 to be read but also note how many // actually were read. b1 := make([]byte, 5) n1, err := f.Read(b1) check(err) fmt.Printf("%d bytes: %s\n", n1, string(b1[:n1])) // You can also `Seek` to a known location in the file // and `Read` from there. o2, err := f.Seek(6, io.SeekStart) check(err) b2 := make([]byte, 2) n2, err := f.Read(b2) check(err) fmt.Printf("%d bytes @ %d: ", n2, o2) fmt.Printf("%v\n", string(b2[:n2])) // Other methods of seeking are relative to the // current cursor position, _, err = f.Seek(2, io.SeekCurrent) check(err) // and relative to the end of the file. _, err = f.Seek(-4, io.SeekEnd) check(err) // The `io` package provides some functions that may // be helpful for file reading. For example, reads // like the ones above can be more robustly // implemented with `ReadAtLeast`. o3, err := f.Seek(6, io.SeekStart) check(err) b3 := make([]byte, 2) n3, err := io.ReadAtLeast(f, b3, 2) check(err) fmt.Printf("%d bytes @ %d: %s\n", n3, o3, string(b3)) // There is no built-in rewind, but // `Seek(0, io.SeekStart)` accomplishes this. _, err = f.Seek(0, io.SeekStart) check(err) // The `bufio` package implements a buffered // reader that may be useful both for its efficiency // with many small reads and because of the additional // reading methods it provides. r4 := bufio.NewReader(f) b4, err := r4.Peek(5) check(err) fmt.Printf("5 bytes: %s\n", string(b4)) // Close the file when you're done (usually this would // be scheduled immediately after `Open`ing with // `defer`). f.Close() } ================================================ FILE: examples/reading-files/reading-files.hash ================================================ f4a5063dc6db22beac95c6b60a8a76e3422e8cf4 J710-KJyC8z ================================================ FILE: examples/reading-files/reading-files.sh ================================================ $ echo "hello" > /tmp/dat $ echo "go" >> /tmp/dat $ go run reading-files.go hello go 5 bytes: hello 2 bytes @ 6: go 2 bytes @ 6: go 5 bytes: hello # Next we'll look at writing files. ================================================ FILE: examples/recover/recover.go ================================================ // Go makes it possible to _recover_ from a panic, by // using the `recover` built-in function. A `recover` can // stop a `panic` from aborting the program and let it // continue with execution instead. // An example of where this can be useful: a server // wouldn't want to crash if one of the client connections // exhibits a critical error. Instead, the server would // want to close that connection and continue serving // other clients. In fact, this is what Go's `net/http` // does by default for HTTP servers. package main import "fmt" // This function panics. func mayPanic() { panic("a problem") } func main() { // `recover` must be called within a deferred function. // When the enclosing function panics, the defer will // activate and a `recover` call within it will catch // the panic. defer func() { if r := recover(); r != nil { // The return value of `recover` is the error raised in // the call to `panic`. fmt.Println("Recovered. Error:\n", r) } }() mayPanic() // This code will not run, because `mayPanic` panics. // The execution of `main` stops at the point of the // panic and resumes in the deferred closure. fmt.Println("After mayPanic()") } ================================================ FILE: examples/recover/recover.hash ================================================ 053ecbddb4f4a1d881aa40086de99da4e78b9990 Sk-SVdofEIZ ================================================ FILE: examples/recover/recover.sh ================================================ $ go run recover.go Recovered. Error: a problem ================================================ FILE: examples/recursion/recursion.go ================================================ // Go supports // recursive functions. // Here's a classic example. package main import "fmt" // This `fact` function calls itself until it reaches the // base case of `fact(0)`. func fact(n int) int { if n == 0 { return 1 } return n * fact(n-1) } func main() { fmt.Println(fact(7)) // Anonymous functions can also be recursive, but this requires // explicitly declaring a variable with `var` to store // the function before it's defined. var fib func(n int) int fib = func(n int) int { if n < 2 { return n } // Since `fib` was previously declared in `main`, Go // knows which function to call with `fib` here. return fib(n-1) + fib(n-2) } fmt.Println(fib(7)) } ================================================ FILE: examples/recursion/recursion.hash ================================================ 5787b4a187dc208dcdae43c7fdc0ba19b821ed94 k4IRATLn9cE ================================================ FILE: examples/recursion/recursion.sh ================================================ $ go run recursion.go 5040 13 ================================================ FILE: examples/regular-expressions/regular-expressions.go ================================================ // Go offers built-in support for [regular expressions](https://en.wikipedia.org/wiki/Regular_expression). // Here are some examples of common regexp-related tasks // in Go. package main import ( "bytes" "fmt" "regexp" ) func main() { // This tests whether a pattern matches a string. match, _ := regexp.MatchString("p([a-z]+)ch", "peach") fmt.Println(match) // Above we used a string pattern directly, but for // other regexp tasks you'll need to `Compile` an // optimized `Regexp` struct. r, _ := regexp.Compile("p([a-z]+)ch") // Many methods are available on these structs. Here's // a match test like we saw earlier. fmt.Println(r.MatchString("peach")) // This finds the match for the regexp. fmt.Println(r.FindString("peach punch")) // This also finds the first match but returns the // start and end indexes for the match instead of the // matching text. fmt.Println("idx:", r.FindStringIndex("peach punch")) // The `Submatch` variants include information about // both the whole-pattern matches and the submatches // within those matches. For example this will return // information for both `p([a-z]+)ch` and `([a-z]+)`. fmt.Println(r.FindStringSubmatch("peach punch")) // Similarly this will return information about the // indexes of matches and submatches. fmt.Println(r.FindStringSubmatchIndex("peach punch")) // The `All` variants of these functions apply to all // matches in the input, not just the first. For // example to find all matches for a regexp. fmt.Println(r.FindAllString("peach punch pinch", -1)) // These `All` variants are available for the other // functions we saw above as well. fmt.Println("all:", r.FindAllStringSubmatchIndex( "peach punch pinch", -1)) // Providing a non-negative integer as the second // argument to these functions will limit the number // of matches. fmt.Println(r.FindAllString("peach punch pinch", 2)) // Our examples above had string arguments and used // names like `MatchString`. We can also provide // `[]byte` arguments and drop `String` from the // function name. fmt.Println(r.Match([]byte("peach"))) // When creating global variables with regular // expressions you can use the `MustCompile` variation // of `Compile`. `MustCompile` panics instead of // returning an error, which makes it safer to use for // global variables. r = regexp.MustCompile("p([a-z]+)ch") fmt.Println("regexp:", r) // The `regexp` package can also be used to replace // subsets of strings with other values. fmt.Println(r.ReplaceAllString("a peach", "")) // The `Func` variant allows you to transform matched // text with a given function. in := []byte("a peach") out := r.ReplaceAllFunc(in, bytes.ToUpper) fmt.Println(string(out)) } ================================================ FILE: examples/regular-expressions/regular-expressions.hash ================================================ 7fd60a9497546cb5c84242276ed79aecbde7e950 fI2YIfYsCaL ================================================ FILE: examples/regular-expressions/regular-expressions.sh ================================================ $ go run regular-expressions.go true true peach idx: [0 5] [peach ea] [0 5 1 3] [peach punch pinch] all: [[0 5 1 3] [6 11 7 9] [12 17 13 15]] [peach punch] true regexp: p([a-z]+)ch a a PEACH # For a complete reference on Go regular expressions check # the [`regexp`](https://pkg.go.dev/regexp) package docs. ================================================ FILE: examples/select/select.go ================================================ // Go's _select_ lets you wait on multiple channel // operations. Combining goroutines and channels with // select is a powerful feature of Go. package main import ( "fmt" "time" ) func main() { // For our example we'll select across two channels. c1 := make(chan string) c2 := make(chan string) // Each channel will receive a value after some amount // of time, to simulate e.g. blocking RPC operations // executing in concurrent goroutines. go func() { time.Sleep(1 * time.Second) c1 <- "one" }() go func() { time.Sleep(2 * time.Second) c2 <- "two" }() // We'll use `select` to await both of these values // simultaneously, printing each one as it arrives. for range 2 { select { case msg1 := <-c1: fmt.Println("received", msg1) case msg2 := <-c2: fmt.Println("received", msg2) } } } ================================================ FILE: examples/select/select.hash ================================================ b10a3f618c232683fba207e56e4b4cda812fd377 dOrjUfgGwB2 ================================================ FILE: examples/select/select.sh ================================================ # We receive the values `"one"` and then `"two"` as # expected. $ time go run select.go received one received two # Note that the total execution time is only ~2 seconds # since both the 1 and 2 second `Sleeps` execute # concurrently. real 0m2.245s ================================================ FILE: examples/sha256-hashes/sha256-hashes.go ================================================ // [_SHA256 hashes_](https://en.wikipedia.org/wiki/SHA-2) are // frequently used to compute short identities for binary // or text blobs. For example, TLS/SSL certificates use SHA256 // to compute a certificate's signature. Here's how to compute // SHA256 hashes in Go. package main // Go implements several hash functions in various // `crypto/*` packages. import ( "crypto/sha256" "fmt" ) func main() { s := "sha256 this string" // Here we start with a new hash. h := sha256.New() // `Write` expects bytes. If you have a string `s`, // use `[]byte(s)` to coerce it to bytes. h.Write([]byte(s)) // This gets the finalized hash result as a byte // slice. The argument to `Sum` can be used to append // to an existing byte slice: it usually isn't needed. bs := h.Sum(nil) fmt.Println(s) fmt.Printf("%x\n", bs) } ================================================ FILE: examples/sha256-hashes/sha256-hashes.hash ================================================ 66d0faf25cd171218b0fc0fc341836a4f0069932 IHM1lZVm_Jm ================================================ FILE: examples/sha256-hashes/sha256-hashes.sh ================================================ # Running the program computes the hash and prints it in # a human-readable hex format. $ go run sha256-hashes.go sha256 this string 1af1dfa857bf1d8814fe1af8983c18080019922e557f15a8a... # You can compute other hashes using a similar pattern to # the one shown above. For example, to compute # SHA512 hashes import `crypto/sha512` and use # `sha512.New()`. # Note that if you need cryptographically secure hashes, # you should carefully research # [hash strength](https://en.wikipedia.org/wiki/Cryptographic_hash_function)! ================================================ FILE: examples/signals/signals.go ================================================ // Sometimes we'd like our Go programs to intelligently // handle [Unix signals](https://en.wikipedia.org/wiki/Unix_signal). // For example, we might want a server to gracefully // shutdown when it receives a `SIGTERM`, or a command-line // tool to stop processing input if it receives a `SIGINT`. // Here's a modern way to handle signals using contexts. package main import ( "context" "fmt" "os/signal" "syscall" ) func main() { // `signal.NotifyContext` returns a context that's canceled // when one of the listed signals arrives. ctx, stop := signal.NotifyContext( context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() // The program will wait here until one of the // configured signals is received. fmt.Println("awaiting signal") <-ctx.Done() // `context.Cause` reports why the context was canceled. // For a signal-triggered cancellation, this includes // the signal value. fmt.Println() fmt.Println(context.Cause(ctx)) fmt.Println("exiting") } ================================================ FILE: examples/signals/signals.hash ================================================ f55b2c52d8888d110d5909023e1dd8bfdf0846a6 F3Wm7JT8wgn ================================================ FILE: examples/signals/signals.sh ================================================ # When we run this program it will block waiting for a # signal. By typing `ctrl-C` (which the # terminal shows as `^C`) we can send a `SIGINT` signal, # causing the program to print the cancellation cause and then exit. $ go run signals.go awaiting signal ^C interrupt signal received exiting ================================================ FILE: examples/slices/slices.go ================================================ // _Slices_ are an important data type in Go, giving // a more powerful interface to sequences than arrays. package main import ( "fmt" "slices" ) func main() { // Unlike arrays, slices are typed only by the // elements they contain (not the number of elements). // An uninitialized slice equals to nil and has // length 0. var s []string fmt.Println("uninit:", s, s == nil, len(s) == 0) // To create a slice with non-zero length, use // the builtin `make`. Here we make a slice of // `string`s of length `3` (initially zero-valued). // By default a new slice's capacity is equal to its // length; if we know the slice is going to grow ahead // of time, it's possible to pass a capacity explicitly // as an additional parameter to `make`. s = make([]string, 3) fmt.Println("emp:", s, "len:", len(s), "cap:", cap(s)) // We can set and get just like with arrays. s[0] = "a" s[1] = "b" s[2] = "c" fmt.Println("set:", s) fmt.Println("get:", s[2]) // `len` returns the length of the slice as expected. fmt.Println("len:", len(s)) // In addition to these basic operations, slices // support several more that make them richer than // arrays. One is the builtin `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. s = append(s, "d") s = append(s, "e", "f") fmt.Println("apd:", s) // Slices can also be `copy`'d. Here we create an // empty slice `c` of the same length as `s` and copy // into `c` from `s`. c := make([]string, len(s)) copy(c, s) fmt.Println("cpy:", c) // Slices support a "slice" operator with the syntax // `slice[low:high]`. For example, this gets a slice // of the elements `s[2]`, `s[3]`, and `s[4]`. l := s[2:5] fmt.Println("sl1:", l) // This slices up to (but excluding) `s[5]`. l = s[:5] fmt.Println("sl2:", l) // And this slices up from (and including) `s[2]`. l = s[2:] fmt.Println("sl3:", l) // We can declare and initialize a variable for slice // in a single line as well. t := []string{"g", "h", "i"} fmt.Println("dcl:", t) // The `slices` package contains a number of useful // utility functions for slices. t2 := []string{"g", "h", "i"} if slices.Equal(t, t2) { fmt.Println("t == t2") } // Slices can be composed into multi-dimensional data // structures. The length of the inner slices can // vary, unlike with multi-dimensional arrays. twoD := make([][]int, 3) for i := range 3 { innerLen := i + 1 twoD[i] = make([]int, innerLen) for j := range innerLen { twoD[i][j] = i + j } } fmt.Println("2d: ", twoD) } ================================================ FILE: examples/slices/slices.hash ================================================ 4db1907fd5137325a1e3c3fc50950d4250ed1a4b 9-U3-8sKQun ================================================ FILE: examples/slices/slices.sh ================================================ # Note that while slices are different types than arrays, # they are rendered similarly by `fmt.Println`. $ go run slices.go uninit: [] true true emp: [ ] len: 3 cap: 3 set: [a b c] get: c len: 3 apd: [a b c d e f] cpy: [a b c d e f] sl1: [c d e] sl2: [a b c d e] sl3: [c d e f] dcl: [g h i] t == t2 2d: [[0] [1 2] [2 3 4]] # Check out this [great blog post](https://go.dev/blog/slices-intro) # by the Go team for more details on the design and # implementation of slices in Go. # Now that we've seen arrays and slices we'll look at # Go's other key builtin data structure: maps. ================================================ FILE: examples/sorting/sorting.go ================================================ // Go's `slices` package implements sorting for builtins // and user-defined types. We'll look at sorting for // builtins first. package main import ( "fmt" "slices" ) func main() { // Sorting functions are generic, and work for any // _ordered_ built-in type. For a list of ordered // types, see [cmp.Ordered](https://pkg.go.dev/cmp#Ordered). strs := []string{"c", "a", "b"} slices.Sort(strs) fmt.Println("Strings:", strs) // An example of sorting `int`s. ints := []int{7, 2, 4} slices.Sort(ints) fmt.Println("Ints: ", ints) // We can also use the `slices` package to check if // a slice is already in sorted order. s := slices.IsSorted(ints) fmt.Println("Sorted: ", s) } ================================================ FILE: examples/sorting/sorting.hash ================================================ 2091224c8d8ac748883215c4dbe9611fb8afacc3 X7iJcIua02T ================================================ FILE: examples/sorting/sorting.sh ================================================ $ go run sorting.go Strings: [a b c] Ints: [2 4 7] Sorted: true ================================================ FILE: examples/sorting-by-functions/sorting-by-functions.go ================================================ // Sometimes we'll want to sort a collection by something // other than its natural order. For example, suppose we // wanted to sort strings by their length instead of // alphabetically. Here's an example of custom sorts // in Go. package main import ( "cmp" "fmt" "slices" ) func main() { fruits := []string{"peach", "banana", "kiwi"} // We implement a comparison function for string // lengths. `cmp.Compare` is helpful for this. lenCmp := func(a, b string) int { return cmp.Compare(len(a), len(b)) } // Now we can call `slices.SortFunc` with this custom // comparison function to sort `fruits` by name length. slices.SortFunc(fruits, lenCmp) fmt.Println(fruits) // We can use the same technique to sort a slice of // values that aren't built-in types. type Person struct { name string age int } people := []Person{ Person{name: "Jax", age: 37}, Person{name: "TJ", age: 25}, Person{name: "Alex", age: 72}, } // Sort `people` by age using `slices.SortFunc`. // // Note: if the `Person` struct is large, // you may want the slice to contain `*Person` instead // and adjust the sorting function accordingly. If in // doubt, [benchmark](testing-and-benchmarking)! slices.SortFunc(people, func(a, b Person) int { return cmp.Compare(a.age, b.age) }) fmt.Println(people) } ================================================ FILE: examples/sorting-by-functions/sorting-by-functions.hash ================================================ 9902d1c5654b64d8b381ea7888c0793ac8ab4a97 3EaTknAZHMu ================================================ FILE: examples/sorting-by-functions/sorting-by-functions.sh ================================================ $ go run sorting-by-functions.go [kiwi peach banana] [{TJ 25} {Jax 37} {Alex 72}] ================================================ FILE: examples/spawning-processes/spawning-processes.go ================================================ // Sometimes our Go programs need to spawn other // processes. package main import ( "errors" "fmt" "io" "os/exec" ) func main() { // We'll start with a simple command that takes no // arguments or input and just prints something to // stdout. The `exec.Command` helper creates an object // to represent this external process. dateCmd := exec.Command("date") // The `Output` method runs the command, waits for it // to finish and collects its standard output. // If there were no errors, `dateOut` will hold bytes // with the date info. dateOut, err := dateCmd.Output() if err != nil { panic(err) } fmt.Println("> date") fmt.Println(string(dateOut)) // `Output` and other methods of `Command` will return // `*exec.Error` if there was a problem executing the // command (e.g. wrong path), and `*exec.ExitError` // if the command ran but exited with a non-zero return // code. _, err = exec.Command("date", "-x").Output() if err != nil { if e, ok := errors.AsType[*exec.Error](err); ok { fmt.Println("failed executing:", e) } else if e, ok := errors.AsType[*exec.ExitError](err); ok { exitCode := e.ExitCode() fmt.Println("command exit rc =", exitCode) } else { panic(err) } } // Next we'll look at a slightly more involved case // where we pipe data to the external process on its // `stdin` and collect the results from its `stdout`. grepCmd := exec.Command("grep", "hello") // Here we explicitly grab input/output pipes, start // the process, write some input to it, read the // resulting output, and finally wait for the process // to exit. grepIn, _ := grepCmd.StdinPipe() grepOut, _ := grepCmd.StdoutPipe() grepCmd.Start() grepIn.Write([]byte("hello grep\ngoodbye grep")) grepIn.Close() grepBytes, _ := io.ReadAll(grepOut) grepCmd.Wait() // We omitted error checks in the above example, but // you could use the usual `if err != nil` pattern for // all of them. We also only collect the `StdoutPipe` // results, but you could collect the `StderrPipe` in // exactly the same way. fmt.Println("> grep hello") fmt.Println(string(grepBytes)) // Note that when spawning commands we need to // provide an explicitly delineated command and // argument array, vs. being able to just pass in one // command-line string. If you want to spawn a full // command with a string, you can use `bash`'s `-c` // option: lsCmd := exec.Command("bash", "-c", "ls -a -l -h") lsOut, err := lsCmd.Output() if err != nil { panic(err) } fmt.Println("> ls -a -l -h") fmt.Println(string(lsOut)) } ================================================ FILE: examples/spawning-processes/spawning-processes.hash ================================================ 4eebcdac2df1c55c6264fe4422a8e6855bcbc2e6 IxGAEXe0I7U ================================================ FILE: examples/spawning-processes/spawning-processes.sh ================================================ # The spawned programs return output that is the same # as if we had run them directly from the command-line. $ go run spawning-processes.go > date Thu 05 May 2022 10:10:12 PM PDT # date doesn't have a `-x` flag so it will exit with # an error message and non-zero return code. command exit rc = 1 > grep hello hello grep > ls -a -l -h drwxr-xr-x 4 mark 136B Oct 3 16:29 . drwxr-xr-x 91 mark 3.0K Oct 3 12:50 .. -rw-r--r-- 1 mark 1.3K Oct 3 16:28 spawning-processes.go ================================================ FILE: examples/stateful-goroutines/stateful-goroutines.go ================================================ // In the previous example we used explicit locking with // [mutexes](mutexes) to synchronize access to shared state // across multiple goroutines. Another option is to use the // built-in synchronization features of goroutines and // channels to achieve the same result. This channel-based // approach aligns with Go's ideas of sharing memory by // communicating and having each piece of data owned // by exactly one goroutine. package main import ( "fmt" "math/rand" "sync/atomic" "time" ) // In this example our state will be owned by a single // goroutine. This will guarantee that the data is never // corrupted with concurrent access. In order to read or // write that state, other goroutines will send messages // to the owning goroutine and receive corresponding // replies. These `readOp` and `writeOp` `struct`s // encapsulate those requests and a way for the owning // goroutine to respond. type readOp struct { key int resp chan int } type writeOp struct { key int val int resp chan bool } func main() { // As before we'll count how many operations we perform. 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) // Here is the goroutine that owns the `state`, which // is a map as in the previous example but now private // to the stateful goroutine. This goroutine repeatedly // selects on the `reads` and `writes` channels, // responding to requests as they arrive. A response // is executed by first performing the requested // operation and then sending a value on the response // channel `resp` to indicate success (and the desired // value in the case of `reads`). go func() { var state = make(map[int]int) for { select { case read := <-reads: read.resp <- state[read.key] case write := <-writes: state[write.key] = write.val write.resp <- true } } }() // This starts 100 goroutines to issue reads to the // state-owning goroutine via the `reads` channel. // Each read requires constructing a `readOp`, sending // it over the `reads` channel, and then receiving the // result over the provided `resp` channel. for range 100 { go func() { for { read := readOp{ key: rand.Intn(5), resp: make(chan int)} reads <- read <-read.resp atomic.AddUint64(&readOps, 1) time.Sleep(time.Millisecond) } }() } // We start 10 writes as well, using a similar // approach. for range 10 { go func() { for { write := writeOp{ key: rand.Intn(5), val: rand.Intn(100), resp: make(chan bool)} writes <- write <-write.resp atomic.AddUint64(&writeOps, 1) time.Sleep(time.Millisecond) } }() } // Let the goroutines work for a second. time.Sleep(time.Second) // Finally, capture and report the op counts. readOpsFinal := atomic.LoadUint64(&readOps) fmt.Println("readOps:", readOpsFinal) writeOpsFinal := atomic.LoadUint64(&writeOps) fmt.Println("writeOps:", writeOpsFinal) } ================================================ FILE: examples/stateful-goroutines/stateful-goroutines.hash ================================================ 0436907e3df175590da08a697c204bc79054798f uwnybdsnOSa ================================================ FILE: examples/stateful-goroutines/stateful-goroutines.sh ================================================ # Running our program shows that the goroutine-based # state management example completes about 80,000 # total operations. $ go run stateful-goroutines.go readOps: 71708 writeOps: 7177 # For this particular case the goroutine-based approach # was a bit more involved than the mutex-based one. It # might be useful in certain cases though, for example # where you have other channels involved or when managing # multiple such mutexes would be error-prone. You should # use whichever approach feels most natural, especially # with respect to understanding the correctness of your # program. ================================================ FILE: examples/string-formatting/string-formatting.go ================================================ // Go offers excellent support for string formatting in // the `printf` tradition. Here are some examples of // common string formatting tasks. package main import ( "fmt" "os" ) type point struct { x, y int } func main() { // Go offers several printing "verbs" designed to // format general Go values. For example, this prints // an instance of our `point` struct. p := point{1, 2} fmt.Printf("struct1: %v\n", p) // If the value is a struct, the `%+v` variant will // include the struct's field names. fmt.Printf("struct2: %+v\n", p) // The `%#v` variant prints a Go syntax representation // of the value, i.e. the source code snippet that // would produce that value. fmt.Printf("struct3: %#v\n", p) // To print the type of a value, use `%T`. fmt.Printf("type: %T\n", p) // Formatting booleans is straight-forward. fmt.Printf("bool: %t\n", true) // There are many options for formatting integers. // Use `%d` for standard, base-10 formatting. fmt.Printf("int: %d\n", 123) // This prints a binary representation. fmt.Printf("bin: %b\n", 14) // This prints the character corresponding to the // given integer. fmt.Printf("char: %c\n", 33) // `%x` provides hex encoding. fmt.Printf("hex: %x\n", 456) // There are also several formatting options for // floats. For basic decimal formatting use `%f`. fmt.Printf("float1: %f\n", 78.9) // `%e` and `%E` format the float in (slightly // different versions of) scientific notation. fmt.Printf("float2: %e\n", 123400000.0) fmt.Printf("float3: %E\n", 123400000.0) // For basic string printing use `%s`. fmt.Printf("str1: %s\n", "\"string\"") // To double-quote strings as in Go source, use `%q`. fmt.Printf("str2: %q\n", "\"string\"") // As with integers seen earlier, `%x` renders // the string in base-16, with two output characters // per byte of input. fmt.Printf("str3: %x\n", "hex this") // To print a representation of a pointer, use `%p`. fmt.Printf("pointer: %p\n", &p) // When formatting numbers you will often want to // control the width and precision of the resulting // figure. To specify the width of an integer, use a // number after the `%` in the verb. By default the // result will be right-justified and padded with // spaces. fmt.Printf("width1: |%6d|%6d|\n", 12, 345) // You can also specify the width of printed floats, // though usually you'll also want to restrict the // decimal precision at the same time with the // width.precision syntax. fmt.Printf("width2: |%6.2f|%6.2f|\n", 1.2, 3.45) // To left-justify, use the `-` flag. fmt.Printf("width3: |%-6.2f|%-6.2f|\n", 1.2, 3.45) // You may also want to control width when formatting // strings, especially to ensure that they align in // table-like output. For basic right-justified width. fmt.Printf("width4: |%6s|%6s|\n", "foo", "b") // To left-justify use the `-` flag as with numbers. fmt.Printf("width5: |%-6s|%-6s|\n", "foo", "b") // So far we've seen `Printf`, which prints the // formatted string to `os.Stdout`. `Sprintf` formats // and returns a string without printing it anywhere. s := fmt.Sprintf("sprintf: a %s", "string") fmt.Println(s) // You can format+print to `io.Writers` other than // `os.Stdout` using `Fprintf`. fmt.Fprintf(os.Stderr, "io: an %s\n", "error") } ================================================ FILE: examples/string-formatting/string-formatting.hash ================================================ 2297335ea52cc0ed960153b891d35252636308b4 EZCZX1Uwp6D ================================================ FILE: examples/string-formatting/string-formatting.sh ================================================ $ go run string-formatting.go struct1: {1 2} struct2: {x:1 y:2} struct3: main.point{x:1, y:2} type: main.point bool: true int: 123 bin: 1110 char: ! hex: 1c8 float1: 78.900000 float2: 1.234000e+08 float3: 1.234000E+08 str1: "string" str2: "\"string\"" str3: 6865782074686973 pointer: 0xc0000ba000 width1: | 12| 345| width2: | 1.20| 3.45| width3: |1.20 |3.45 | width4: | foo| b| width5: |foo |b | sprintf: a string io: an error ================================================ FILE: examples/string-functions/string-functions.go ================================================ // The standard library's `strings` package provides many // useful string-related functions. Here are some examples // to give you a sense of the package. package main import ( "fmt" s "strings" ) // We alias `fmt.Println` to a shorter name as we'll use // it a lot below. var p = fmt.Println func main() { // Here's a sample of the functions available in // `strings`. Since these are functions from the // package, not methods on the string object itself, // we need to pass the string in question as the first // argument to the function. You can find more // functions in the [`strings`](https://pkg.go.dev/strings) // package docs. p("Contains: ", s.Contains("test", "es")) p("Count: ", s.Count("test", "t")) p("HasPrefix: ", s.HasPrefix("test", "te")) p("HasSuffix: ", s.HasSuffix("test", "st")) p("Index: ", s.Index("test", "e")) p("Join: ", s.Join([]string{"a", "b"}, "-")) p("Repeat: ", s.Repeat("a", 5)) p("Replace: ", s.Replace("foo", "o", "0", -1)) p("Replace: ", s.Replace("foo", "o", "0", 1)) p("Split: ", s.Split("a-b-c-d-e", "-")) p("ToLower: ", s.ToLower("TEST")) p("ToUpper: ", s.ToUpper("test")) } ================================================ FILE: examples/string-functions/string-functions.hash ================================================ e1e2ea01b9c79fecebfece602915e9367b02a083 wKSAzxfs96O ================================================ FILE: examples/string-functions/string-functions.sh ================================================ $ go run string-functions.go Contains: true Count: 2 HasPrefix: true HasSuffix: true Index: 1 Join: a-b Repeat: aaaaa Replace: f00 Replace: f0o Split: [a b c d e] ToLower: test ToUpper: TEST ================================================ FILE: examples/strings-and-runes/strings-and-runes.go ================================================ // A Go string is a read-only slice of bytes. The language // and the standard library treat strings specially - as // containers of text encoded in [UTF-8](https://en.wikipedia.org/wiki/UTF-8). // In other languages, strings are made of "characters". // In Go, the concept of a character is called a `rune` - it's // an integer that represents a Unicode code point. // [This Go blog post](https://go.dev/blog/strings) is a good // introduction to the topic. package main import ( "fmt" "unicode/utf8" ) func main() { // `s` is a `string` assigned a literal value // representing the word "hello" in the Thai // language. Go string literals are UTF-8 // encoded text. const s = "สวัสดี" // Since strings are equivalent to `[]byte`, this // will produce the length of the raw bytes stored within. fmt.Println("Len:", len(s)) // Indexing into a string produces the raw byte values at // each index. This loop generates the hex values of all // the bytes that constitute the code points in `s`. for i := 0; i < len(s); i++ { fmt.Printf("%x ", s[i]) } fmt.Println() // To count how many _runes_ are in a string, we can use // the `utf8` package. Note that the run-time of // `RuneCountInString` depends on the size of the string, // because it has to decode each UTF-8 rune sequentially. // Some Thai characters are represented by UTF-8 code points // that can span multiple bytes, so the result of this count // may be surprising. fmt.Println("Rune count:", utf8.RuneCountInString(s)) // A `range` loop handles strings specially and decodes // each `rune` along with its offset in the string. for idx, runeValue := range s { fmt.Printf("%#U starts at %d\n", runeValue, idx) } // We can achieve the same iteration by using the // `utf8.DecodeRuneInString` function explicitly. fmt.Println("\nUsing DecodeRuneInString") for i, w := 0, 0; i < len(s); i += w { runeValue, width := utf8.DecodeRuneInString(s[i:]) fmt.Printf("%#U starts at %d\n", runeValue, i) w = width // This demonstrates passing a `rune` value to a function. examineRune(runeValue) } } func examineRune(r rune) { // Values enclosed in single quotes are _rune literals_. We // can compare a `rune` value to a rune literal directly. if r == 't' { fmt.Println("found tee") } else if r == 'ส' { fmt.Println("found so sua") } } ================================================ FILE: examples/strings-and-runes/strings-and-runes.hash ================================================ ffbc918567cea7cdadcaee87ffc404a1d4f5c62a -iNDXZ9IM3s ================================================ FILE: examples/strings-and-runes/strings-and-runes.sh ================================================ $ go run strings-and-runes.go Len: 18 e0 b8 aa e0 b8 a7 e0 b8 b1 e0 b8 aa e0 b8 94 e0 b8 b5 Rune count: 6 U+0E2A 'ส' starts at 0 U+0E27 'ว' starts at 3 U+0E31 'ั' starts at 6 U+0E2A 'ส' starts at 9 U+0E14 'ด' starts at 12 U+0E35 'ี' starts at 15 Using DecodeRuneInString U+0E2A 'ส' starts at 0 found so sua U+0E27 'ว' starts at 3 U+0E31 'ั' starts at 6 U+0E2A 'ส' starts at 9 found so sua U+0E14 'ด' starts at 12 U+0E35 'ี' starts at 15 ================================================ FILE: examples/struct-embedding/struct-embedding.go ================================================ // Go supports _embedding_ of structs and interfaces // to express a more seamless _composition_ of types. // This is not to be confused with [`//go:embed`](embed-directive) which is // a go directive introduced in Go version 1.16+ to embed // files and folders into the application binary. package main import "fmt" type base struct { num int } func (b base) describe() string { return fmt.Sprintf("base with num=%v", b.num) } // A `container` _embeds_ a `base`. An embedding looks // like a field without a name. type container struct { base str string } func main() { // When creating structs with literals, we have to // initialize the embedding explicitly; here the // embedded type serves as the field name. co := container{ base: base{ num: 1, }, str: "some name", } // We can access the base's fields directly on `co`, // e.g. `co.num`. fmt.Printf("co={num: %v, str: %v}\n", co.num, co.str) // Alternatively, we can spell out the full path using // the embedded type name. fmt.Println("also num:", co.base.num) // Since `container` embeds `base`, the methods of // `base` also become methods of a `container`. Here // we invoke a method that was embedded from `base` // directly on `co`. fmt.Println("describe:", co.describe()) type describer interface { describe() string } // Embedding structs with methods may be used to bestow // interface implementations onto other structs. Here // we see that a `container` now implements the // `describer` interface because it embeds `base`. var d describer = co fmt.Println("describer:", d.describe()) } ================================================ FILE: examples/struct-embedding/struct-embedding.hash ================================================ 7ac6d1889bfc68e8f3f931014c87e05db2ecda95 -LOu1L0i2tR ================================================ FILE: examples/struct-embedding/struct-embedding.sh ================================================ $ go run struct-embedding.go co={num: 1, str: some name} also num: 1 describe: base with num=1 describer: base with num=1 ================================================ FILE: examples/structs/structs.go ================================================ // Go's _structs_ are typed collections of fields. // They're useful for grouping data together to form // records. package main import "fmt" // This `person` struct type has `name` and `age` fields. type person struct { name string age int } // `newPerson` constructs a new person struct with the given name. func newPerson(name string) *person { // Go is a garbage collected language; you can safely // return a pointer to a local variable - it will only // be cleaned up by the garbage collector when there // are no active references to it. p := person{name: name} p.age = 42 return &p } func main() { // This syntax creates a new struct. fmt.Println(person{"Bob", 20}) // You can name the fields when initializing a struct. fmt.Println(person{name: "Alice", age: 30}) // Omitted fields will be zero-valued. fmt.Println(person{name: "Fred"}) // An `&` 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) // You can also use dots with struct pointers - the // pointers are automatically dereferenced. sp := &s fmt.Println(sp.age) // Structs are mutable. sp.age = 51 fmt.Println(sp.age) // If a struct type is only used for a single value, we don't // have to give it a name. The value can have an anonymous // struct type. This technique is commonly used for // [table-driven tests](testing-and-benchmarking). dog := struct { name string isGood bool }{ "Rex", true, } fmt.Println(dog) } ================================================ FILE: examples/structs/structs.hash ================================================ b50c1756bf9a2ea7f8853f7f7cb7a61d5efebfc3 56SPo-L2nMN ================================================ FILE: examples/structs/structs.sh ================================================ $ go run structs.go {Bob 20} {Alice 30} {Fred 0} &{Ann 40} &{Jon 42} Sean 50 51 {Rex true} ================================================ FILE: examples/switch/switch.go ================================================ // _Switch statements_ express conditionals across many // branches. package main import ( "fmt" "time" ) func main() { // Here's a basic `switch`. i := 2 fmt.Print("Write ", i, " as ") switch i { case 1: fmt.Println("one") case 2: fmt.Println("two") case 3: fmt.Println("three") } // You can use commas to separate multiple expressions // in the same `case` statement. We use the optional // `default` case in this example as well. switch time.Now().Weekday() { case time.Saturday, time.Sunday: fmt.Println("It's the weekend") default: fmt.Println("It's a weekday") } // `switch` without an expression is an alternate way // to express if/else logic. Here we also show how the // `case` expressions can be non-constants. t := time.Now() switch { case t.Hour() < 12: fmt.Println("It's before noon") default: fmt.Println("It's after noon") } // A type `switch` compares types instead of values. You // can use this to discover the type of an interface // value. In this example, the variable `t` will have the // type corresponding to its clause. whatAmI := func(i interface{}) { switch t := i.(type) { case bool: fmt.Println("I'm a bool") case int: fmt.Println("I'm an int") default: fmt.Printf("Don't know type %T\n", t) } } whatAmI(true) whatAmI(1) whatAmI("hey") } ================================================ FILE: examples/switch/switch.hash ================================================ 28a8909ee7963cb315f14a3be1607def1d91f3a3 qVDqWoUQ6AI ================================================ FILE: examples/switch/switch.sh ================================================ $ go run switch.go Write 2 as two It's a weekday It's after noon I'm a bool I'm an int Don't know type string ================================================ FILE: examples/tcp-server/tcp-server.go ================================================ // The `net` package provides the tools we need to easily build // TCP socket servers. package main import ( "bufio" "fmt" "log" "net" "strings" ) func main() { // `net.Listen` starts the server on the given network // (TCP) and address (port 8090 on all interfaces). listener, err := net.Listen("tcp", ":8090") if err != nil { log.Fatal("Error listening:", err) } // Close the listener to free the port // when the application exits. defer listener.Close() // Loop indefinitely to accept new client connections. for { // Wait for a connection. conn, err := listener.Accept() if err != nil { log.Println("Error accepting conn:", err) continue } // We use a goroutine here to handle the connection // so that the main loop can continue accepting more // connections. go handleConnection(conn) } } // `handleConnection` handles a single client connection, // reading one line of text from the client and returning a response. func handleConnection(conn net.Conn) { // Closing the connection releases resources when // we are finished interacting with the client. defer conn.Close() // Use `bufio.NewReader` to read one line of data // from the client (terminated by a newline). reader := bufio.NewReader(conn) message, err := reader.ReadString('\n') if err != nil { log.Printf("Read error: %v", err) return } // Create and send a response back to the client, // demonstrating two-way communication. ackMsg := strings.ToUpper(strings.TrimSpace(message)) response := fmt.Sprintf("ACK: %s\n", ackMsg) _, err = conn.Write([]byte(response)) if err != nil { log.Printf("Server write error: %v", err) } } ================================================ FILE: examples/tcp-server/tcp-server.hash ================================================ c356ece464b228046dc123501acc746ae63d58a8 G0k2jHMA6ei ================================================ FILE: examples/tcp-server/tcp-server.sh ================================================ # Run the TCP server in the background. $ go run tcp-server.go & # Send data and capture the response using netcat. $ echo "Hello from netcat" | nc localhost 8090 ACK: HELLO FROM NETCAT ================================================ FILE: examples/temporary-files-and-directories/temporary-files-and-directories.go ================================================ // Throughout program execution, we often want to create // data that isn't needed after the program exits. // *Temporary files and directories* are useful for this // purpose since they don't pollute the file system over // time. package main import ( "fmt" "os" "path/filepath" ) func check(e error) { if e != nil { panic(e) } } func main() { // The easiest way to create a temporary file is by // calling `os.CreateTemp`. It creates a file *and* // opens it for reading and writing. We provide `""` // as the first argument, so `os.CreateTemp` will // create the file in the default location for our OS. f, err := os.CreateTemp("", "sample") check(err) // Display the name of the temporary file. On // Unix-based OSes the directory will likely be `/tmp`. // The file name starts with the prefix given as the // second argument to `os.CreateTemp` and the rest // is chosen automatically to ensure that concurrent // calls will always create different file names. fmt.Println("Temp file name:", f.Name()) // Clean up the file after we're done. The OS is // likely to clean up temporary files by itself after // some time, but it's good practice to do this // explicitly. defer os.Remove(f.Name()) // We can write some data to the file. _, err = f.Write([]byte{1, 2, 3, 4}) check(err) // If we intend to write many temporary files, we may // prefer to create a temporary *directory*. // `os.MkdirTemp`'s arguments are the same as // `CreateTemp`'s, but it returns a directory *name* // rather than an open file. dname, err := os.MkdirTemp("", "sampledir") check(err) fmt.Println("Temp dir name:", dname) defer os.RemoveAll(dname) // Now we can synthesize temporary file names by // prefixing them with our temporary directory. fname := filepath.Join(dname, "file1") err = os.WriteFile(fname, []byte{1, 2}, 0666) check(err) } ================================================ FILE: examples/temporary-files-and-directories/temporary-files-and-directories.hash ================================================ d875fd8f061e895d72c48c360a8ad4b04e406dd4 hVcPg9RH3_V ================================================ FILE: examples/temporary-files-and-directories/temporary-files-and-directories.sh ================================================ $ go run temporary-files-and-directories.go Temp file name: /tmp/sample610887201 Temp dir name: /tmp/sampledir898854668 ================================================ FILE: examples/testing-and-benchmarking/main_test.go ================================================ // Unit testing is an important part of writing // principled Go programs. The `testing` package // provides the tools we need to write unit tests // and the `go test` command runs tests. // For the sake of demonstration, this code is in package // `main`, but it could be any package. Testing code // typically lives in the same package as the code it tests. package main import ( "fmt" "testing" ) // We'll be testing this simple implementation of an // integer minimum. Typically, the code we're testing // would be in a source file named something like // `intutils.go`, and the test file for it would then // be named `intutils_test.go`. func IntMin(a, b int) int { if a < b { return a } return b } // A test is created by writing a function with a name // beginning with `Test`. func TestIntMinBasic(t *testing.T) { ans := IntMin(2, -2) if ans != -2 { // `t.Error*` will report test failures but continue // executing the test. `t.Fatal*` will report test // failures and stop the test immediately. t.Errorf("IntMin(2, -2) = %d; want -2", ans) } } // Writing tests can be repetitive, so it's idiomatic to // use a *table-driven style*, where test inputs and // expected outputs are listed in a table and a single loop // walks over them and performs the test logic. func TestIntMinTableDriven(t *testing.T) { var tests = []struct { a, b int want int }{ {0, 1, 0}, {1, 0, 0}, {2, -2, -2}, {0, -1, -1}, {-1, 0, -1}, } for _, tt := range tests { // `t.Run` enables running "subtests", one for each // table entry. These are shown separately // when executing `go test -v`. testname := fmt.Sprintf("%d,%d", tt.a, tt.b) t.Run(testname, func(t *testing.T) { ans := IntMin(tt.a, tt.b) if ans != tt.want { t.Errorf("got %d, want %d", ans, tt.want) } }) } } // Benchmark tests typically go in `_test.go` files and are // named beginning with `Benchmark`. // Any code that's required for the benchmark to run but should // not be measured goes before this loop. func BenchmarkIntMin(b *testing.B) { for b.Loop() { // The benchmark runner will automatically execute this loop // body many times to determine a reasonable estimate of the // run-time of a single iteration. IntMin(1, 2) } } ================================================ FILE: examples/testing-and-benchmarking/main_test.sh ================================================ # Run all tests in the current project in verbose mode. $ go test -v == RUN TestIntMinBasic --- PASS: TestIntMinBasic (0.00s) === RUN TestIntMinTableDriven === RUN TestIntMinTableDriven/0,1 === RUN TestIntMinTableDriven/1,0 === RUN TestIntMinTableDriven/2,-2 === RUN TestIntMinTableDriven/0,-1 === RUN TestIntMinTableDriven/-1,0 --- PASS: TestIntMinTableDriven (0.00s) --- PASS: TestIntMinTableDriven/0,1 (0.00s) --- PASS: TestIntMinTableDriven/1,0 (0.00s) --- PASS: TestIntMinTableDriven/2,-2 (0.00s) --- PASS: TestIntMinTableDriven/0,-1 (0.00s) --- PASS: TestIntMinTableDriven/-1,0 (0.00s) PASS ok examples/testing-and-benchmarking 0.023s # Run all benchmarks in the current project. All tests # are run prior to benchmarks. The `bench` flag filters # benchmark function names with a regexp. $ go test -bench=. goos: darwin goarch: arm64 pkg: examples/testing BenchmarkIntMin-8 1000000000 0.3136 ns/op PASS ok examples/testing-and-benchmarking 0.351s ================================================ FILE: examples/testing-and-benchmarking/testing-and-benchmarking.hash ================================================ 565e50f23b399cdbca4aedeb8b62b9f6ad097443 osZckbKSkse ================================================ FILE: examples/text-templates/text-templates.go ================================================ // Go offers built-in support for creating dynamic content or showing customized // output to the user with the `text/template` package. A sibling package // named `html/template` provides the same API but has additional security // features and should be used for generating HTML. package main import ( "os" "text/template" ) func main() { // We can create a new template and parse its body from // a string. // Templates are a mix of static text and "actions" enclosed in // `{{...}}` that are used to dynamically insert content. t1 := template.New("t1") t1, err := t1.Parse("Value is {{.}}\n") if err != nil { panic(err) } // Alternatively, we can use the `template.Must` function to // panic in case `Parse` returns an error. This is especially // useful for templates initialized in the global scope. t1 = template.Must(t1.Parse("Value: {{.}}\n")) // By "executing" the template we generate its text with // specific values for its actions. The `{{.}}` action is // replaced by the value passed as a parameter to `Execute`. t1.Execute(os.Stdout, "some text") t1.Execute(os.Stdout, 5) t1.Execute(os.Stdout, []string{ "Go", "Rust", "C++", "C#", }) // Helper function we'll use below. Create := func(name, t string) *template.Template { return template.Must(template.New(name).Parse(t)) } // If the data is a struct we can use the `{{.FieldName}}` action to access // its fields. The fields should be exported to be accessible when a // template is executing. t2 := Create("t2", "Name: {{.Name}}\n") t2.Execute(os.Stdout, struct { Name string }{"Jane Doe"}) // The same applies to maps; with maps there is no restriction on the // case of key names. t2.Execute(os.Stdout, map[string]string{ "Name": "Mickey Mouse", }) // if/else provide conditional execution for templates. A value is considered // false if it's the default value of a type, such as 0, an empty string, // nil pointer, etc. // This sample demonstrates another // feature of templates: using `-` in actions to trim whitespace. t3 := Create("t3", "{{if . -}} yes {{else -}} no {{end}}\n") t3.Execute(os.Stdout, "not empty") t3.Execute(os.Stdout, "") // range blocks let us loop through slices, arrays, maps or channels. Inside // the range block `{{.}}` is set to the current item of the iteration. t4 := Create("t4", "Range: {{range .}}{{.}} {{end}}\n") t4.Execute(os.Stdout, []string{ "Go", "Rust", "C++", "C#", }) } ================================================ FILE: examples/text-templates/text-templates.hash ================================================ c29676a83f4832a77b7a9e300d3fb5fe315de7b8 pDwkw1iMACF ================================================ FILE: examples/text-templates/text-templates.sh ================================================ $ go run templates.go Value: some text Value: 5 Value: [Go Rust C++ C#] Name: Jane Doe Name: Mickey Mouse yes no Range: Go Rust C++ C# ================================================ FILE: examples/tickers/tickers.go ================================================ // [Timers](timers) are for when you want to do // something once in the future - _tickers_ are for when // you want to do something repeatedly at regular // intervals. Here's an example of a ticker that ticks // periodically until we stop it. package main import ( "fmt" "time" ) func main() { // Tickers use a similar mechanism to timers: a // channel that is sent values. Here we'll use the // `select` builtin on the channel to await the // values as they arrive every 500ms. ticker := time.NewTicker(500 * time.Millisecond) done := make(chan bool) go func() { for { select { case <-done: return case t := <-ticker.C: fmt.Println("Tick at", t) } } }() // Tickers can be stopped like timers. Once a ticker // is stopped it won't receive any more values on its // channel. We'll stop ours after 1600ms. time.Sleep(1600 * time.Millisecond) ticker.Stop() done <- true fmt.Println("Ticker stopped") } ================================================ FILE: examples/tickers/tickers.hash ================================================ 432b3be0884cead3f01b9cce0868ac6146e7864e gs6zoJP-Pl9 ================================================ FILE: examples/tickers/tickers.sh ================================================ # When we run this program the ticker should tick 3 times # before we stop it. $ go run tickers.go Tick at 2012-09-23 11:29:56.487625 -0700 PDT Tick at 2012-09-23 11:29:56.988063 -0700 PDT Tick at 2012-09-23 11:29:57.488076 -0700 PDT Ticker stopped ================================================ FILE: examples/time/time.go ================================================ // Go offers extensive support for times and durations; // here are some examples. package main import ( "fmt" "time" ) func main() { p := fmt.Println // We'll start by getting the current time. now := time.Now() p(now) // You can build a `time` struct by providing the // year, month, day, etc. Times are always associated // with a `Location`, i.e. time zone. then := time.Date( 2009, 11, 17, 20, 34, 58, 651387237, time.UTC) p(then) // You can extract the various components of the time // value as expected. p(then.Year()) p(then.Month()) p(then.Day()) p(then.Hour()) p(then.Minute()) p(then.Second()) p(then.Nanosecond()) p(then.Location()) // The Monday-Sunday `Weekday` is also available. p(then.Weekday()) // These methods compare two times, testing if the // first occurs before, after, or at the same time // as the second, respectively. p(then.Before(now)) p(then.After(now)) p(then.Equal(now)) // The `Sub` methods returns a `Duration` representing // the interval between two times. diff := now.Sub(then) p(diff) // We can compute the length of the duration in // various units. p(diff.Hours()) p(diff.Minutes()) p(diff.Seconds()) p(diff.Nanoseconds()) // You can use `Add` to advance a time by a given // duration, or with a `-` to move backwards by a // duration. p(then.Add(diff)) p(then.Add(-diff)) } ================================================ FILE: examples/time/time.hash ================================================ 24eefcc82ee0c70a4678a4952fe2b8c558c7419c YAM3s1KPc8c ================================================ FILE: examples/time/time.sh ================================================ $ go run time.go 2012-10-31 15:50:13.793654 +0000 UTC 2009-11-17 20:34:58.651387237 +0000 UTC 2009 November 17 20 34 58 651387237 UTC Tuesday true false false 25891h15m15.142266763s 25891.25420618521 1.5534752523711128e+06 9.320851514226677e+07 93208515142266763 2012-10-31 15:50:13.793654 +0000 UTC 2006-12-05 01:19:43.509120474 +0000 UTC # Next we'll look at the related idea of time relative to # the Unix epoch. ================================================ FILE: examples/time-formatting-parsing/time-formatting-parsing.go ================================================ // Go supports time formatting and parsing via // pattern-based layouts. package main import ( "fmt" "time" ) func main() { p := fmt.Println // Here's a basic example of formatting a time // according to RFC3339, using the corresponding layout // constant. t := time.Now() p(t.Format(time.RFC3339)) // Time parsing uses the same layout values as `Format`. t1, _ := time.Parse(time.RFC3339, "2012-11-01T22:08:41+00:00") p(t1) // `Format` and `Parse` use example-based layouts. Usually // you'll use a constant from `time` for these layouts, but // you can also supply custom layouts. Layouts must use the // reference time `Mon Jan 2 15:04:05 MST 2006` to show the // pattern with which to format/parse a given time/string. // The example time must be exactly as shown: the year 2006, // 15 for the hour, Monday for the day of the week, etc. p(t.Format("3:04PM")) p(t.Format("Mon Jan _2 15:04:05 2006")) p(t.Format("2006-01-02T15:04:05.999999-07:00")) form := "3 04 PM" t2, _ := time.Parse(form, "8 41 PM") p(t2) // For purely numeric representations you can also // use standard string formatting with the extracted // components of the time value. fmt.Printf("%d-%02d-%02dT%02d:%02d:%02d-00:00\n", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second()) // `Parse` will return an error on malformed input // explaining the parsing problem. _, err := time.Parse("Mon Jan _2 15:04:05 2006", "8:41PM") p(err) } ================================================ FILE: examples/time-formatting-parsing/time-formatting-parsing.hash ================================================ beec3e9eba00fffd88fd1f8b09fa9b38ed76974d 0dgIUsTgEYs ================================================ FILE: examples/time-formatting-parsing/time-formatting-parsing.sh ================================================ $ go run time-formatting-parsing.go 2014-04-15T18:00:15-07:00 2012-11-01 22:08:41 +0000 +0000 6:00PM Tue Apr 15 18:00:15 2014 2014-04-15T18:00:15.161182-07:00 0000-01-01 20:41:00 +0000 UTC 2014-04-15T18:00:15-00:00 parsing time "8:41PM" as "Mon Jan _2 15:04:05 2006": ... ================================================ FILE: examples/timeouts/timeouts.go ================================================ // _Timeouts_ are important for programs that connect to // external resources or that otherwise need to bound // execution time. Implementing timeouts in Go is easy and // elegant thanks to channels and `select`. package main import ( "fmt" "time" ) func main() { // For our example, suppose we're executing an external // call that returns its result on a channel `c1` // after 2s. Note that the channel is buffered, so the // send in the goroutine is nonblocking. This is a // common pattern to prevent goroutine leaks in case the // channel is never read. c1 := make(chan string, 1) go func() { time.Sleep(2 * time.Second) c1 <- "result 1" }() // Here's the `select` implementing a timeout. // `res := <-c1` awaits the result and `<-time.After` // awaits a value to be sent after the timeout of // 1s. Since `select` proceeds with the first // receive that's ready, we'll take the timeout case // if the operation takes more than the allowed 1s. select { case res := <-c1: fmt.Println(res) case <-time.After(1 * time.Second): fmt.Println("timeout 1") } // If we allow a longer timeout of 3s, then the receive // from `c2` will succeed and we'll print the result. c2 := make(chan string, 1) go func() { time.Sleep(2 * time.Second) c2 <- "result 2" }() select { case res := <-c2: fmt.Println(res) case <-time.After(3 * time.Second): fmt.Println("timeout 2") } } ================================================ FILE: examples/timeouts/timeouts.hash ================================================ 79963f30cb3ca93d559a409e3ded40c2bb64df66 gyr0NbVKBVf ================================================ FILE: examples/timeouts/timeouts.sh ================================================ # Running this program shows the first operation timing # out and the second succeeding. $ go run timeouts.go timeout 1 result 2 ================================================ FILE: examples/timers/timers.go ================================================ // We often want to execute Go code at some point in the // future, or repeatedly at some interval. Go's built-in // _timer_ and _ticker_ features make both of these tasks // easy. We'll look first at timers and then // at [tickers](tickers). package main import ( "fmt" "time" ) func main() { // Timers represent a single event in the future. You // tell the timer how long you want to wait, and it // provides a channel that will be notified at that // time. This timer will wait 2 seconds. timer1 := time.NewTimer(2 * time.Second) // The `<-timer1.C` blocks on the timer's channel `C` // until it sends a value indicating that the timer // fired. <-timer1.C fmt.Println("Timer 1 fired") // If you just wanted to wait, you could have used // `time.Sleep`. One reason a timer may be useful is // that you can cancel the timer before it fires. // Here's an example of that. timer2 := time.NewTimer(time.Second) go func() { <-timer2.C fmt.Println("Timer 2 fired") }() stop2 := timer2.Stop() if stop2 { fmt.Println("Timer 2 stopped") } // Give the `timer2` enough time to fire, if it ever // was going to, to show it is in fact stopped. time.Sleep(2 * time.Second) } ================================================ FILE: examples/timers/timers.hash ================================================ 36cae12a3ca529e473d7839e9573c3e0a202c2de gF7VLRz3URM ================================================ FILE: examples/timers/timers.sh ================================================ # The first timer will fire ~2s after we start the # program, but the second should be stopped before it has # a chance to fire. $ go run timers.go Timer 1 fired Timer 2 stopped ================================================ FILE: examples/url-parsing/url-parsing.go ================================================ // URLs provide a [uniform way to locate resources](https://adam.herokuapp.com/past/2010/3/30/urls_are_the_uniform_way_to_locate_resources/). // Here's how to parse URLs in Go. package main import ( "fmt" "net" "net/url" ) func main() { // We'll parse this example URL, which includes a // scheme, authentication info, host, port, path, // query params, and query fragment. s := "postgres://user:pass@host.com:5432/path?k=v#f" // Parse the URL and ensure there are no errors. u, err := url.Parse(s) if err != nil { panic(err) } // Accessing the scheme is straightforward. fmt.Println(u.Scheme) // `User` contains all authentication info; call // `Username` and `Password` on this for individual // values. fmt.Println(u.User) fmt.Println(u.User.Username()) p, _ := u.User.Password() fmt.Println(p) // The `Host` contains both the hostname and the port, // if present. Use `SplitHostPort` to extract them. fmt.Println(u.Host) host, port, _ := net.SplitHostPort(u.Host) fmt.Println(host) fmt.Println(port) // Here we extract the `path` and the fragment after // the `#`. fmt.Println(u.Path) fmt.Println(u.Fragment) // To get query params in a string of `k=v` format, // use `RawQuery`. You can also parse query params // into a map. The parsed query param maps are from // strings to slices of strings, so index into `[0]` // if you only want the first value. fmt.Println(u.RawQuery) m, _ := url.ParseQuery(u.RawQuery) fmt.Println(m) fmt.Println(m["k"][0]) } ================================================ FILE: examples/url-parsing/url-parsing.hash ================================================ 7e77917c98bd88187b4fed2b8c988afdd0b0df7d fHTQn9X7l1B ================================================ FILE: examples/url-parsing/url-parsing.sh ================================================ # Running our URL parsing program shows all the different # pieces that we extracted. $ go run url-parsing.go postgres user:pass user pass host.com:5432 host.com 5432 /path f k=v map[k:[v]] v ================================================ FILE: examples/values/values.go ================================================ // Go has various value types including strings, // integers, floats, booleans, etc. Here are a few // basic examples. package main import "fmt" func main() { // Strings, which can be added together with `+`. fmt.Println("go" + "lang") // Integers and floats. fmt.Println("1+1 =", 1+1) fmt.Println("7.0/3.0 =", 7.0/3.0) // Booleans, with boolean operators as you'd expect. fmt.Println(true && false) fmt.Println(true || false) fmt.Println(!true) } ================================================ FILE: examples/values/values.hash ================================================ 476982956a689418d548148af5f17145de16f063 YnVS3LZr8pk ================================================ FILE: examples/values/values.sh ================================================ $ go run values.go golang 1+1 = 2 7.0/3.0 = 2.3333333333333335 false true false ================================================ FILE: examples/variables/variables.go ================================================ // In Go, _variables_ are explicitly declared and used by // the compiler to e.g. check type-correctness of function // calls. package main import "fmt" func main() { // `var` declares 1 or more variables. var a = "initial" fmt.Println(a) // You can declare multiple variables at once. var b, c int = 1, 2 fmt.Println(b, c) // Go will infer the type of initialized variables. var d = true fmt.Println(d) // Variables declared without a corresponding // initialization are _zero-valued_. For example, the // zero value for an `int` is `0`. var e int fmt.Println(e) // The `:=` syntax is shorthand for declaring and // initializing a variable, e.g. for // `var f string = "apple"` in this case. // This syntax is only available inside functions. f := "apple" fmt.Println(f) } ================================================ FILE: examples/variables/variables.hash ================================================ 9aeef52b289d7ad9b9ac79f129d4e49f956c60ef N5rWndIliJW ================================================ FILE: examples/variables/variables.sh ================================================ $ go run variables.go initial 1 2 true 0 apple ================================================ FILE: examples/variadic-functions/variadic-functions.go ================================================ // [_Variadic functions_](https://en.wikipedia.org/wiki/Variadic_function) // can be called with any number of trailing arguments. // For example, `fmt.Println` is a common variadic // function. package main import "fmt" // Here's a function that will take an arbitrary number // of `int`s as arguments. func sum(nums ...int) { fmt.Print(nums, " ") total := 0 // Within the function, the type of `nums` is // equivalent to `[]int`. We can call `len(nums)`, // iterate over it with `range`, etc. for _, num := range nums { total += num } fmt.Println(total) } func main() { // Variadic functions can be called in the usual way // with individual arguments. sum(1, 2) sum(1, 2, 3) // If you already have multiple args in a slice, // apply them to a variadic function using // `func(slice...)` like this. nums := []int{1, 2, 3, 4} sum(nums...) } ================================================ FILE: examples/variadic-functions/variadic-functions.hash ================================================ 561184169a1b4c3d4970d496b282cc81016583d6 glNdE8aKPNq ================================================ FILE: examples/variadic-functions/variadic-functions.sh ================================================ $ go run variadic-functions.go [1 2] 3 [1 2 3] 6 [1 2 3 4] 10 # Another key aspect of functions in Go is their ability # to form closures, which we'll look at next. ================================================ FILE: examples/waitgroups/waitgroups.go ================================================ // To wait for multiple goroutines to finish, we can // use a *wait group*. package main import ( "fmt" "sync" "time" ) // This is the function we'll run in every goroutine. func worker(id int) { fmt.Printf("Worker %d starting\n", id) // Sleep to simulate an expensive task. time.Sleep(time.Second) fmt.Printf("Worker %d done\n", id) } func main() { // This WaitGroup is used to wait for all the // goroutines launched here to finish. Note: if a WaitGroup is // explicitly passed into functions, it should be done *by pointer*. var wg sync.WaitGroup // Launch several goroutines using `WaitGroup.Go` for i := 1; i <= 5; i++ { wg.Go(func() { worker(i) }) } // Block until all the goroutines started by `wg` are // done. A goroutine is done when the function it invokes // returns. wg.Wait() // Note that this approach has no straightforward way // to propagate errors from workers. For more // advanced use cases, consider using the // [errgroup package](https://pkg.go.dev/golang.org/x/sync/errgroup). } ================================================ FILE: examples/waitgroups/waitgroups.hash ================================================ 97b564243e41a3a86f8c8417268fa942c05d881f csaELahJTWt ================================================ FILE: examples/waitgroups/waitgroups.sh ================================================ $ go run waitgroups.go Worker 5 starting Worker 3 starting Worker 4 starting Worker 1 starting Worker 2 starting Worker 4 done Worker 1 done Worker 2 done Worker 5 done Worker 3 done # The order of workers starting up and finishing # is likely to be different for each invocation. ================================================ FILE: examples/worker-pools/worker-pools.go ================================================ // In this example we'll look at how to implement // a _worker pool_ using goroutines and channels. package main import ( "fmt" "time" ) // Here's the worker, of which we'll run several // concurrent instances. These workers will receive // work on the `jobs` channel and send the corresponding // results on `results`. We'll sleep a second per job to // simulate an expensive task. func worker(id int, jobs <-chan int, results chan<- int) { for j := range jobs { fmt.Println("worker", id, "started job", j) time.Sleep(time.Second) fmt.Println("worker", id, "finished job", j) results <- j * 2 } } func main() { // In order to use our pool of workers we need to send // them work and collect their results. We make 2 // channels for this. const numJobs = 5 jobs := make(chan int, numJobs) results := make(chan int, numJobs) // This starts up 3 workers, initially blocked // because there are no jobs yet. for w := 1; w <= 3; w++ { go worker(w, jobs, results) } // Here we send 5 `jobs` and then `close` that // channel to indicate that's all the work we have. for j := 1; j <= numJobs; j++ { jobs <- j } close(jobs) // Finally we collect all the results of the work. // This also ensures that the worker goroutines have // finished. An alternative way to wait for multiple // goroutines is to use a [WaitGroup](waitgroups). for a := 1; a <= numJobs; a++ { <-results } } ================================================ FILE: examples/worker-pools/worker-pools.hash ================================================ f6fd187061dfd0ae5ae8243efa3a6fcfa0777c84 hiSJJsYZJKL ================================================ FILE: examples/worker-pools/worker-pools.sh ================================================ # Our running program shows the 5 jobs being executed by # various workers. The program only takes about 2 seconds # despite doing about 5 seconds of total work because # there are 3 workers operating concurrently. $ time go run worker-pools.go worker 1 started job 1 worker 2 started job 2 worker 3 started job 3 worker 1 finished job 1 worker 1 started job 4 worker 2 finished job 2 worker 2 started job 5 worker 3 finished job 3 worker 1 finished job 4 worker 2 finished job 5 real 0m2.358s ================================================ FILE: examples/writing-files/writing-files.go ================================================ // Writing files in Go follows similar patterns to the // ones we saw earlier for reading. package main import ( "bufio" "fmt" "os" "path/filepath" ) func check(e error) { if e != nil { panic(e) } } func main() { // To start, here's how to dump a string (or just // bytes) into a file. d1 := []byte("hello\ngo\n") path1 := filepath.Join(os.TempDir(), "dat1") err := os.WriteFile(path1, d1, 0644) check(err) // For more granular writes, open a file for writing. path2 := filepath.Join(os.TempDir(), "dat2") f, err := os.Create(path2) check(err) // It's idiomatic to defer a `Close` immediately // after opening a file. defer f.Close() // You can `Write` byte slices as you'd expect. d2 := []byte{115, 111, 109, 101, 10} n2, err := f.Write(d2) check(err) fmt.Printf("wrote %d bytes\n", n2) // A `WriteString` is also available. n3, err := f.WriteString("writes\n") check(err) fmt.Printf("wrote %d bytes\n", n3) // Issue a `Sync` to flush writes to stable storage. f.Sync() // `bufio` provides buffered writers in addition // to the buffered readers we saw earlier. w := bufio.NewWriter(f) n4, err := w.WriteString("buffered\n") check(err) fmt.Printf("wrote %d bytes\n", n4) // Use `Flush` to ensure all buffered operations have // been applied to the underlying writer. w.Flush() } ================================================ FILE: examples/writing-files/writing-files.hash ================================================ b93857561df33d0ed970d15e26f321627e770655 iuKQDnKfl2T ================================================ FILE: examples/writing-files/writing-files.sh ================================================ # Try running the file-writing code. $ go run writing-files.go wrote 5 bytes wrote 7 bytes wrote 9 bytes # Then check the contents of the written files. $ cat /tmp/dat1 hello go $ cat /tmp/dat2 some writes buffered # Next we'll look at applying some of the file I/O ideas # we've just seen to the `stdin` and `stdout` streams. ================================================ FILE: examples/xml/xml.go ================================================ // Go offers built-in support for XML and XML-like // formats with the `encoding/xml` package. package main import ( "encoding/xml" "fmt" ) // Plant will be mapped to XML. Similarly to the // JSON examples, field tags contain directives for the // encoder and decoder. Here we use some special features // of the XML package: the `XMLName` field name dictates // the name of the XML element representing this struct; // `id,attr` means that the `Id` field is an XML // _attribute_ rather than a nested element. type Plant struct { XMLName xml.Name `xml:"plant"` Id int `xml:"id,attr"` Name string `xml:"name"` Origin []string `xml:"origin"` } func (p Plant) String() string { return fmt.Sprintf("Plant id=%v, name=%v, origin=%v", p.Id, p.Name, p.Origin) } func main() { coffee := &Plant{Id: 27, Name: "Coffee"} coffee.Origin = []string{"Ethiopia", "Brazil"} // Emit XML representing our plant; using // `MarshalIndent` to produce a more // human-readable output. out, _ := xml.MarshalIndent(coffee, " ", " ") fmt.Println(string(out)) // To add a generic XML header to the output, append // it explicitly. fmt.Println(xml.Header + string(out)) // Use `Unmarshal` to parse a stream of bytes with XML // into a data structure. If the XML is malformed or // cannot be mapped onto Plant, a descriptive error // will be returned. var p Plant if err := xml.Unmarshal(out, &p); err != nil { panic(err) } fmt.Println(p) tomato := &Plant{Id: 81, Name: "Tomato"} tomato.Origin = []string{"Mexico", "California"} // The `parent>child>plant` field tag tells the encoder // to nest all `plant`s under `...` type Nesting struct { XMLName xml.Name `xml:"nesting"` Plants []*Plant `xml:"parent>child>plant"` } nesting := &Nesting{} nesting.Plants = []*Plant{coffee, tomato} out, _ = xml.MarshalIndent(nesting, " ", " ") fmt.Println(string(out)) } ================================================ FILE: examples/xml/xml.hash ================================================ d64993474fdf0571436db63a82974d74932ba256 vsP5mIrNJOG ================================================ FILE: examples/xml/xml.sh ================================================ $ go run xml.go Coffee Ethiopia Brazil Coffee Ethiopia Brazil Plant id=27, name=Coffee, origin=[Ethiopia Brazil] Coffee Ethiopia Brazil Tomato Mexico California ================================================ FILE: examples.txt ================================================ Hello World Values Variables Constants For If/Else Switch Arrays Slices Maps Functions Multiple Return Values Variadic Functions Closures Recursion Range over Built-in Types Pointers Strings and Runes Structs Methods Interfaces Enums Struct Embedding Generics Range over Iterators Errors Custom Errors Goroutines Channels Channel Buffering Channel Synchronization Channel Directions Select Timeouts Non-Blocking Channel Operations Closing Channels Range over Channels Timers Tickers Worker Pools WaitGroups Rate Limiting Atomic Counters Mutexes Stateful Goroutines Sorting Sorting by Functions Panic Defer Recover String Functions String Formatting Text Templates Regular Expressions JSON XML Time Epoch Time Formatting / Parsing Random Numbers Number Parsing URL Parsing SHA256 Hashes Base64 Encoding Reading Files Writing Files Line Filters File Paths Directories Temporary Files and Directories Embed Directive Testing and Benchmarking Command-Line Arguments Command-Line Flags Command-Line Subcommands Environment Variables Logging HTTP Client HTTP Server TCP Server Context Spawning Processes Exec'ing Processes Signals Exit ================================================ FILE: go.mod ================================================ module github.com/mmcgrana/gobyexample go 1.26.0 require ( github.com/alecthomas/chroma/v2 v2.10.0 github.com/aws/aws-sdk-go-v2 v1.21.2 github.com/aws/aws-sdk-go-v2/config v1.19.1 github.com/aws/aws-sdk-go-v2/service/s3 v1.40.2 github.com/russross/blackfriday/v2 v2.1.0 ) require ( github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.14 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.13.43 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.13 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.43 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.37 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.3.45 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.6 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.15 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.38 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.37 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.6 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.15.2 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.3 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.23.2 // indirect github.com/aws/smithy-go v1.15.0 // indirect github.com/dlclark/regexp2 v1.10.0 // indirect ) ================================================ FILE: go.sum ================================================ github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink= github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= github.com/alecthomas/chroma/v2 v2.10.0 h1:T2iQOCCt4pRmRMfL55gTodMtc7cU0y7lc1Jb8/mK/64= github.com/alecthomas/chroma/v2 v2.10.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw= github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/aws/aws-sdk-go-v2 v1.21.2 h1:+LXZ0sgo8quN9UOKXXzAWRT3FWd4NxeXWOZom9pE7GA= github.com/aws/aws-sdk-go-v2 v1.21.2/go.mod h1:ErQhvNuEMhJjweavOYhxVkn2RUx7kQXVATHrjKtxIpM= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.14 h1:Sc82v7tDQ/vdU1WtuSyzZ1I7y/68j//HJ6uozND1IDs= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.14/go.mod h1:9NCTOURS8OpxvoAVHq79LK81/zC78hfRWFn+aL0SPcY= github.com/aws/aws-sdk-go-v2/config v1.19.1 h1:oe3vqcGftyk40icfLymhhhNysAwk0NfiwkDi2GTPMXs= github.com/aws/aws-sdk-go-v2/config v1.19.1/go.mod h1:ZwDUgFnQgsazQTnWfeLWk5GjeqTQTL8lMkoE1UXzxdE= github.com/aws/aws-sdk-go-v2/credentials v1.13.43 h1:LU8vo40zBlo3R7bAvBVy/ku4nxGEyZe9N8MqAeFTzF8= github.com/aws/aws-sdk-go-v2/credentials v1.13.43/go.mod h1:zWJBz1Yf1ZtX5NGax9ZdNjhhI4rgjfgsyk6vTY1yfVg= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.13 h1:PIktER+hwIG286DqXyvVENjgLTAwGgoeriLDD5C+YlQ= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.13/go.mod h1:f/Ib/qYjhV2/qdsf79H3QP/eRE4AkVyEf6sk7XfZ1tg= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.43 h1:nFBQlGtkbPzp/NjZLuFxRqmT91rLJkgvsEQs68h962Y= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.43/go.mod h1:auo+PiyLl0n1l8A0e8RIeR8tOzYPfZZH/JNlrJ8igTQ= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.37 h1:JRVhO25+r3ar2mKGP7E0LDl8K9/G36gjlqca5iQbaqc= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.37/go.mod h1:Qe+2KtKml+FEsQF/DHmDV+xjtche/hwoF75EG4UlHW8= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.45 h1:hze8YsjSh8Wl1rYa1CJpRmXP21BvOBuc76YhW0HsuQ4= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.45/go.mod h1:lD5M20o09/LCuQ2mE62Mb/iSdSlCNuj6H5ci7tW7OsE= github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.6 h1:wmGLw2i8ZTlHLw7a9ULGfQbuccw8uIiNr6sol5bFzc8= github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.6/go.mod h1:Q0Hq2X/NuL7z8b1Dww8rmOFl+jzusKEcyvkKspwdpyc= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.15 h1:7R8uRYyXzdD71KWVCL78lJZltah6VVznXBazvKjfH58= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.15/go.mod h1:26SQUPcTNgV1Tapwdt4a1rOsYRsnBsJHLMPoxK2b0d8= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.38 h1:skaFGzv+3kA+v2BPKhuekeb1Hbb105+44r8ASC+q5SE= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.38/go.mod h1:epIZoRSSbRIwLPJU5F+OldHhwZPBdpDeQkRdCeY3+00= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.37 h1:WWZA/I2K4ptBS1kg0kV1JbBtG/umed0vwHRrmcr9z7k= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.37/go.mod h1:vBmDnwWXWxNPFRMmG2m/3MKOe+xEcMDo1tanpaWCcck= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.6 h1:9ulSU5ClouoPIYhDQdg9tpl83d5Yb91PXTKK+17q+ow= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.6/go.mod h1:lnc2taBsR9nTlz9meD+lhFZZ9EWY712QHrRflWpTcOA= github.com/aws/aws-sdk-go-v2/service/s3 v1.40.2 h1:Ll5/YVCOzRB+gxPqs2uD0R7/MyATC0w85626glSKmp4= github.com/aws/aws-sdk-go-v2/service/s3 v1.40.2/go.mod h1:Zjfqt7KhQK+PO1bbOsFNzKgaq7TcxzmEoDWN8lM0qzQ= github.com/aws/aws-sdk-go-v2/service/sso v1.15.2 h1:JuPGc7IkOP4AaqcZSIcyqLpFSqBWK32rM9+a1g6u73k= github.com/aws/aws-sdk-go-v2/service/sso v1.15.2/go.mod h1:gsL4keucRCgW+xA85ALBpRFfdSLH4kHOVSnLMSuBECo= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.3 h1:HFiiRkf1SdaAmV3/BHOFZ9DjFynPHj8G/UIO1lQS+fk= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.3/go.mod h1:a7bHA82fyUXOm+ZSWKU6PIoBxrjSprdLoM8xPYvzYVg= github.com/aws/aws-sdk-go-v2/service/sts v1.23.2 h1:0BkLfgeDjfZnZ+MhB3ONb01u9pwFYTCZVhlsSSBvlbU= github.com/aws/aws-sdk-go-v2/service/sts v1.23.2/go.mod h1:Eows6e1uQEsc4ZaHANmsPRzAKcVDrcmjjWiih2+HUUQ= github.com/aws/smithy-go v1.15.0 h1:PS/durmlzvAFpQHDs4wi4sNNP9ExsqZh6IlfdHXgKK8= github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= ================================================ FILE: public/404.html ================================================ Go by Example: Not Found

Go by Example

Sorry, we couldn't find that! Check out the home page?

================================================ FILE: public/arrays ================================================ Go by Example: Arrays

Go by Example: Arrays

In Go, an array is a numbered sequence of elements of a specific length. In typical Go code, slices are much more common; arrays are useful in some special scenarios.

package main
import "fmt"
func main() {

Here we create an array a that will hold exactly 5 ints. The type of elements and length are both part of the array’s type. By default an array is zero-valued, which for ints means 0s.

    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])

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)

You can also have the compiler count the number of elements for you with ...

    b = [...]int{1, 2, 3, 4, 5}
    fmt.Println("dcl:", b)

If you specify the index with :, the elements in between will be zeroed.

    b = [...]int{100, 3: 400, 500}
    fmt.Println("idx:", b)

Array types are one-dimensional, but you can compose types to build multi-dimensional data structures.

    var twoD [2][3]int
    for i := range 2 {
        for j := range 3 {
            twoD[i][j] = i + j
        }
    }
    fmt.Println("2d: ", twoD)

You can create and initialize multi-dimensional arrays at once too.

    twoD = [2][3]int{
        {1, 2, 3},
        {1, 2, 3},
    }
    fmt.Println("2d: ", twoD)
}

Note that arrays appear in the form [v1 v2 v3 ...] when printed with fmt.Println.

$ go run arrays.go
emp: [0 0 0 0 0]
set: [0 0 0 0 100]
get: 100
len: 5
dcl: [1 2 3 4 5]
dcl: [1 2 3 4 5]
idx: [100 0 0 400 500]
2d:  [[0 1 2] [1 2 3]]
2d:  [[1 2 3] [1 2 3]]

Next example: .

================================================ FILE: public/atomic-counters ================================================ Go by Example: Atomic Counters

Go by Example: Atomic Counters

The primary mechanism for managing state in Go is communication over channels. We saw this for example with worker pools. There are a few other options for managing state though. Here we’ll look at using the sync/atomic package for atomic counters accessed by multiple goroutines.

package main
import (
    "fmt"
    "sync"
    "sync/atomic"
)
func main() {

We’ll use an atomic integer type to represent our (always-positive) counter.

    var ops atomic.Uint64

A WaitGroup will help us wait for all goroutines to finish their work.

    var wg sync.WaitGroup

We’ll start 50 goroutines that each increment the counter exactly 1000 times.

    for range 50 {
        wg.Go(func() {
            for range 1000 {

To atomically increment the counter we use Add.

                ops.Add(1)
            }
        })
    }

Wait until all the goroutines are done.

    wg.Wait()

Here no goroutines are writing to ‘ops’, but using Load it’s safe to atomically read a value even while other goroutines are (atomically) updating it.

    fmt.Println("ops:", ops.Load())
}

We expect to get exactly 50,000 operations. Had we used a non-atomic integer and incremented it with ops++, we’d likely get a different number, changing between runs, because the goroutines would interfere with each other. Moreover, we’d get data race failures when running with the -race flag.

$ go run atomic-counters.go
ops: 50000

Next we’ll look at mutexes, another tool for managing state.

Next example: .

================================================ FILE: public/base64-encoding ================================================ Go by Example: Base64 Encoding

Go by Example: Base64 Encoding

Go provides built-in support for base64 encoding/decoding.

package main

This syntax imports the encoding/base64 package with the b64 name instead of the default base64. It’ll save us some space below.

import (
    b64 "encoding/base64"
    "fmt"
)
func main() {

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 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()

This encodes/decodes using a URL-compatible base64 format.

    uEnc := b64.URLEncoding.EncodeToString([]byte(data))
    fmt.Println(uEnc)
    uDec, _ := b64.URLEncoding.DecodeString(uEnc)
    fmt.Println(string(uDec))
}

The string encodes to slightly different values with the standard and URL base64 encoders (trailing + vs -) but they both decode to the original string as desired.

$ go run base64-encoding.go
YWJjMTIzIT8kKiYoKSctPUB+
abc123!?$*&()'-=@~
YWJjMTIzIT8kKiYoKSctPUB-
abc123!?$*&()'-=@~

Next example: .

================================================ FILE: public/channel-buffering ================================================ Go by Example: Channel Buffering

Go by Example: Channel Buffering

By default channels are unbuffered, meaning that they will only accept sends (chan <-) if there is a corresponding receive (<- chan) ready to receive the sent value. Buffered channels accept a limited number of values without a corresponding receiver for those values.

package main
import "fmt"
func main() {

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"

Later we can receive these two values as usual.

    fmt.Println(<-messages)
    fmt.Println(<-messages)
}
$ go run channel-buffering.go 
buffered
channel

Next example: .

================================================ FILE: public/channel-directions ================================================ Go by Example: Channel Directions

Go by Example: Channel Directions

When using channels as function parameters, you can specify if a channel is meant to only send or receive values. This specificity increases the type-safety of the program.

package main
import "fmt"

This ping function only accepts a channel for sending values. It would be a compile-time error to try to receive on this channel.

func ping(pings chan<- string, msg string) {
    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
}
func main() {
    pings := make(chan string, 1)
    pongs := make(chan string, 1)
    ping(pings, "passed message")
    pong(pings, pongs)
    fmt.Println(<-pongs)
}
$ go run channel-directions.go
passed message

Next example: .

================================================ FILE: public/channel-synchronization ================================================ Go by Example: Channel Synchronization

Go by Example: Channel Synchronization

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.

package main
import (
    "fmt"
    "time"
)

This is the function we’ll run in a goroutine. The 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")

Send a value to notify that we’re done.

    done <- true
}
func main() {

Start a worker goroutine, giving it the channel to notify on.

    done := make(chan bool, 1)
    go worker(done)

Block until we receive a notification from the worker on the channel.

    <-done
}
$ go run channel-synchronization.go      
working...done                  

If you removed the <- done line from this program, the program could exit before the worker finished its work, or in some cases even before it started.

Next example: .

================================================ FILE: public/channels ================================================ Go by Example: Channels

Go by Example: Channels

Channels are the pipes that connect concurrent goroutines. You can send values into channels from one goroutine and receive those values into another goroutine.

package main
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)

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)
}

When we run the program the "ping" message is successfully passed from one goroutine to another via our channel.

$ go run channels.go 
ping

By default sends and receives block until both the sender and receiver are ready. This property allowed us to wait at the end of our program for the "ping" message without having to use any other synchronization.

Next example: .

================================================ FILE: public/closing-channels ================================================ Go by Example: Closing Channels

Go by Example: Closing Channels

Closing a channel indicates that no more values will be sent on it. This can be useful to communicate completion to the channel’s receivers.

package main
import "fmt"

In this example we’ll use a jobs channel to communicate work to be done from the main() goroutine 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)

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 closed 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")

We await the worker using the synchronization approach we saw earlier.

    <-done

Reading from a closed channel succeeds immediately, returning the zero value of the underlying type. The optional second return value is true if the value received was delivered by a successful send operation to the channel, or false if it was a zero value generated because the channel is closed and empty.

    _, ok := <-jobs
    fmt.Println("received more jobs:", ok)
}
$ go run closing-channels.go 
sent job 1
received job 1
sent job 2
received job 2
sent job 3
received job 3
sent all jobs
received all jobs
received more jobs: false

The idea of closed channels leads naturally to our next example: range over channels.

Next example: .

================================================ FILE: public/closures ================================================ Go by Example: Closures

Go by Example: Closures

Go supports anonymous functions, which can form closures. Anonymous functions are useful when you want to define a function inline without having to name it.

package main
import "fmt"

This function intSeq returns another function, which we define anonymously in the body of intSeq. The returned function closes over the variable i to form a closure.

func intSeq() func() int {
    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()

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())
}
$ go run closures.go
1
2
3
1

The last feature of functions we’ll look at for now is recursion.

Next example: .

================================================ FILE: public/command-line-arguments ================================================ Go by Example: Command-Line Arguments

Go by Example: Command-Line Arguments

Command-line arguments are a common way to parameterize execution of programs. For example, go run hello.go uses run and hello.go arguments to the go program.

package main
import (
    "fmt"
    "os"
)
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:]

You can get individual args with normal indexing.

    arg := os.Args[3]
    fmt.Println(argsWithProg)
    fmt.Println(argsWithoutProg)
    fmt.Println(arg)
}

To experiment with command-line arguments it’s best to build a binary with go build first.

$ go build command-line-arguments.go
$ ./command-line-arguments a b c d
[./command-line-arguments a b c d]       
[a b c d]
c

Next we’ll look at more advanced command-line processing with flags.

Next example: .

================================================ FILE: public/command-line-flags ================================================ Go by Example: Command-Line Flags

Go by Example: Command-Line Flags

Command-line flags are a common way to specify options for command-line programs. For example, in wc -l the -l is a command-line flag.

package main

Go provides a flag package supporting basic command-line flag parsing. We’ll use this package to implement our example command-line program.

import (
    "flag"
    "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")

This declares numb and fork flags, using a similar approach to the word flag.

    numbPtr := flag.Int("numb", 42, "an int")
    forkPtr := flag.Bool("fork", false, "a bool")

It’s also possible to declare an option that uses an existing var declared elsewhere in the program. Note that we need to pass in a pointer to the flag declaration function.

    var svar string
    flag.StringVar(&svar, "svar", "bar", "a string var")

Once all flags are declared, call flag.Parse() 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:", *forkPtr)
    fmt.Println("svar:", svar)
    fmt.Println("tail:", flag.Args())
}

To experiment with the command-line flags program it’s best to first compile it and then run the resulting binary directly.

$ go build command-line-flags.go

Try out the built program by first giving it values for all flags.

$ ./command-line-flags -word=opt -numb=7 -fork -svar=flag
word: opt
numb: 7
fork: true
svar: flag
tail: []

Note that if you omit flags they automatically take their default values.

$ ./command-line-flags -word=opt
word: opt
numb: 42
fork: false
svar: bar
tail: []

Trailing positional arguments can be provided after any flags.

$ ./command-line-flags -word=opt a1 a2 a3
word: opt
...
tail: [a1 a2 a3]

Note that the flag package requires all flags to appear before positional arguments (otherwise the flags will be interpreted as positional arguments).

$ ./command-line-flags -word=opt a1 a2 a3 -numb=7
word: opt
numb: 42
fork: false
svar: bar
tail: [a1 a2 a3 -numb=7]

Use -h or --help flags to get automatically generated help text for the command-line program.

$ ./command-line-flags -h
Usage of ./command-line-flags:
  -fork=false: a bool
  -numb=42: an int
  -svar="bar": a string var
  -word="foo": a string

If you provide a flag that wasn’t specified to the flag package, the program will print an error message and show the help text again.

$ ./command-line-flags -wat
flag provided but not defined: -wat
Usage of ./command-line-flags:
...

Next example: .

================================================ FILE: public/command-line-subcommands ================================================ Go by Example: Command-Line Subcommands

Go by Example: Command-Line Subcommands

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)
    }
}
$ 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.

Next example: .

================================================ FILE: public/constants ================================================ Go by Example: Constants

Go by Example: Constants

Go supports constants of character, string, boolean, and numeric values.

package main
import (
    "fmt"
    "math"
)

const declares a constant value.

const s string = "constant"
func main() {
    fmt.Println(s)

A const statement can also appear inside a function body.

    const n = 500000000

Constant expressions perform arithmetic with arbitrary precision.

    const d = 3e20 / n
    fmt.Println(d)

A numeric constant has no type until it’s given one, such as by an explicit 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))
}
$ go run constant.go 
constant
6e+11
600000000000
-0.28470407323754404

Next example: .

================================================ FILE: public/context ================================================ Go by Example: Context

Go by Example: Context

In the previous example we looked at setting up a simple HTTP server. HTTP servers are useful for demonstrating the usage of context.Context for controlling cancellation. A Context carries deadlines, cancellation signals, and other request-scoped values across API boundaries and goroutines.

package main
import (
    "fmt"
    "net/http"
    "time"
)
func hello(w http.ResponseWriter, req *http.Request) {

A context.Context is created for each request by the net/http machinery, and is available with the Context() method.

    ctx := req.Context()
    fmt.Println("server: hello handler started")
    defer fmt.Println("server: hello handler ended")

Wait for a few seconds before sending a reply to the client. This could simulate some work the server is doing. While working, keep an eye on the context’s Done() channel for a signal that we should cancel the work and return as soon as possible.

    select {
    case <-time.After(10 * time.Second):
        fmt.Fprintf(w, "hello\n")
    case <-ctx.Done():

The context’s Err() method returns an error that explains why the Done() channel was closed.

        err := ctx.Err()
        fmt.Println("server:", err)
        internalError := http.StatusInternalServerError
        http.Error(w, err.Error(), internalError)
    }
}
func main() {

As before, we register our handler on the “/hello” route, and start serving.

    http.HandleFunc("/hello", hello)
    http.ListenAndServe(":8090", nil)
}

Run the server in the background.

$ go run context.go &

Simulate a client request to /hello, hitting Ctrl+C shortly after starting to signal cancellation.

$ curl localhost:8090/hello
server: hello handler started
^C
server: context canceled
server: hello handler ended

Next example: .

================================================ FILE: public/custom-errors ================================================ Go by Example: Custom Errors

Go by Example: Custom Errors

It’s possible to define custom error types by implementing the Error() method on them. Here’s a variant on the example above that uses a custom type to explicitly represent an argument error.

package main
import (
    "errors"
    "fmt"
)

A custom error type usually has the suffix “Error”.

type argError struct {
    arg     int
    message string
}

Adding this Error method makes argError implement the error interface.

func (e *argError) Error() string {
    return fmt.Sprintf("%d - %s", e.arg, e.message)
}
func f(arg int) (int, error) {
    if arg == 42 {

Return our custom error.

        return -1, &argError{arg, "can't work with it"}
    }
    return arg + 3, nil
}
func main() {

errors.AsType is a more advanced version of errors.Is. It checks that a given error (or any error in its chain) matches a specific error type and converts to a value of that type, also returning true. If there’s no match, the second return value is false.

    _, err := f(42)
    if ae, ok := errors.AsType[*argError](err); ok {
        fmt.Println(ae.arg)
        fmt.Println(ae.message)
    } else {
        fmt.Println("err doesn't match argError")
    }
}
$ go run custom-errors.go
42
can't work with it

Next example: .

================================================ FILE: public/defer ================================================ Go by Example: Defer

Go by Example: Defer

Defer is used to ensure that a function call is performed later in a program’s execution, usually for purposes of cleanup. defer is often used where e.g. ensure and finally would be used in other languages.

package main
import (
    "fmt"
    "os"
    "path/filepath"
)

Suppose we wanted to create a file, write to it, and then close when we’re done. Here’s how we could do that with 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.

    path := filepath.Join(os.TempDir(), "defer.txt")
    f := createFile(path)
    defer closeFile(f)
    writeFile(f)
}
func createFile(p string) *os.File {
    fmt.Println("creating")
    f, err := os.Create(p)
    if err != nil {
        panic(err)
    }
    return f
}
func writeFile(f *os.File) {
    fmt.Println("writing")
    fmt.Fprintln(f, "data")
}

It’s important to check for errors when closing a file, even in a deferred function.

func closeFile(f *os.File) {
    fmt.Println("closing")
    err := f.Close()
    if err != nil {
        panic(err)
    }
}

Running the program confirms that the file is closed after being written.

$ go run defer.go
creating
writing
closing

Next example: .

================================================ FILE: public/directories ================================================ Go by Example: Directories

Go by Example: Directories

Go has several useful functions for working with directories in the file system.

package main
import (
    "fmt"
    "io/fs"
    "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(os.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.DirEntry objects.

    c, err := os.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 = os.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. WalkDir accepts a callback function to handle every file or directory visited.

    fmt.Println("Visiting subdir")
    err = filepath.WalkDir("subdir", visit)
}

visit is called for every file or directory found recursively by filepath.WalkDir.

func visit(path string, d fs.DirEntry, err error) error {
    if err != nil {
        return err
    }
    fmt.Println(" ", path, d.IsDir())
    return nil
}
$ 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

Next example: .

================================================ FILE: public/embed-directive ================================================ Go by Example: Embed Directive

Go by Example: Embed Directive

//go:embed is a compiler directive that allows programs to include arbitrary files and folders in the Go binary at build time. Read more about the embed directive here.

package main

Import the embed package; if you don’t use any exported identifiers from this package, you can do a blank import with _ "embed".

import (
    "embed"
)

embed directives accept paths relative to the directory containing the Go source file. This directive embeds the contents of the file into the string variable immediately following it.

//go:embed folder/single_file.txt
var fileString string

Or embed the contents of the file into a []byte.

//go:embed folder/single_file.txt
var fileByte []byte

We can also embed multiple files or even folders with wildcards. This uses a variable of the embed.FS type, which implements a simple virtual file system.

//go:embed folder/single_file.txt
//go:embed folder/*.hash
var folder embed.FS
func main() {

Print out the contents of single_file.txt.

    print(fileString)
    print(string(fileByte))

Retrieve some files from the embedded folder.

    content1, _ := folder.ReadFile("folder/file1.hash")
    print(string(content1))
    content2, _ := folder.ReadFile("folder/file2.hash")
    print(string(content2))
}

Use these commands to run the example. (Note: due to limitation on go playground, this example can only be run on your local machine.)

$ mkdir -p folder
$ echo "hello go" > folder/single_file.txt
$ echo "123" > folder/file1.hash
$ echo "456" > folder/file2.hash
$ go run embed-directive.go
hello go
hello go
123
456

Next example: .

================================================ FILE: public/enums ================================================ Go by Example: Enums

Go by Example: Enums

Enumerated types (enums) are a special case of sum types. An enum is a type that has a fixed number of possible values, each with a distinct name. Go doesn’t have an enum type as a distinct language feature, but enums are simple to implement using existing language idioms.

package main
import "fmt"

Our enum type ServerState has an underlying int type.

type ServerState int

The possible values for ServerState are defined as constants. The special keyword iota generates successive constant values automatically; in this case 0, 1, 2 and so on.

const (
    StateIdle ServerState = iota
    StateConnected
    StateError
    StateRetrying
)

By implementing the fmt.Stringer interface, values of ServerState can be printed out or converted to strings.

This can get cumbersome if there are many possible values. In such cases the stringer tool can be used in conjunction with go:generate to automate the process. See this post for a longer explanation.

var stateName = map[ServerState]string{
    StateIdle:      "idle",
    StateConnected: "connected",
    StateError:     "error",
    StateRetrying:  "retrying",
}
func (ss ServerState) String() string {
    return stateName[ss]
}

If we have a value of type int, we cannot pass it to transition - the compiler will complain about type mismatch. This provides some degree of compile-time type safety for enums.

func main() {
    ns := transition(StateIdle)
    fmt.Println(ns)
    ns2 := transition(ns)
    fmt.Println(ns2)
}

transition emulates a state transition for a server; it takes the existing state and returns a new state.

func transition(s ServerState) ServerState {
    switch s {
    case StateIdle:
        return StateConnected
    case StateConnected, StateRetrying:

Suppose we check some predicates here to determine the next state…

        return StateIdle
    case StateError:
        return StateError
    default:
        panic(fmt.Errorf("unknown state: %s", s))
    }
}
$ go run enums.go
connected
idle

Next example: .

================================================ FILE: public/environment-variables ================================================ Go by Example: Environment Variables

Go by Example: Environment Variables

Environment variables are a universal mechanism for conveying configuration information to Unix programs. Let’s look at how to set, get, and list environment variables.

package main
import (
    "fmt"
    "os"
    "strings"
)
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"))

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.SplitN them to get the key and value. Here we print all the keys.

    fmt.Println()
    for _, e := range os.Environ() {
        pair := strings.SplitN(e, "=", 2)
        fmt.Println(pair[0])
    }
}

Running the program shows that we pick up the value for FOO that we set in the program, but that BAR is empty.

$ go run environment-variables.go
FOO: 1
BAR: 

The list of keys in the environment will depend on your particular machine.

TERM_PROGRAM
PATH
SHELL
...
FOO

If we set BAR in the environment first, the running program picks that value up.

$ BAR=2 go run environment-variables.go
FOO: 1
BAR: 2
...

Next example: .

================================================ FILE: public/epoch ================================================ Go by Example: Epoch

Go by Example: Epoch

A common requirement in programs is getting the number of seconds, milliseconds, or nanoseconds since the Unix epoch. Here’s how to do it in Go.

package main
import (
    "fmt"
    "time"
)
func main() {

Use time.Now with Unix, UnixMilli or UnixNano to get elapsed time since the Unix epoch in seconds, milliseconds or nanoseconds, respectively.

    now := time.Now()
    fmt.Println(now)
    fmt.Println(now.Unix())
    fmt.Println(now.UnixMilli())
    fmt.Println(now.UnixNano())

You can also convert integer seconds or nanoseconds since the epoch into the corresponding time.

    fmt.Println(time.Unix(now.Unix(), 0))
    fmt.Println(time.Unix(0, now.UnixNano()))
}
$ go run epoch.go 
2012-10-31 16:13:58.292387 +0000 UTC
1351700038
1351700038292
1351700038292387000
2012-10-31 16:13:58 +0000 UTC
2012-10-31 16:13:58.292387 +0000 UTC

Next we’ll look at another time-related task: time parsing and formatting.

Next example: .

================================================ FILE: public/errors ================================================ Go by Example: Errors

Go by Example: Errors

In Go it’s idiomatic to communicate errors via an explicit, separate return value. This contrasts with the exceptions used in languages like Java, Python and Ruby and the overloaded single result / error value sometimes used in C. Go’s approach makes it easy to see which functions return errors and to handle them using the same language constructs employed for other, non-error tasks.

See the documentation of the errors package and this blog post for additional details.

package main
import (
    "errors"
    "fmt"
)

By convention, errors are the last return value and have type error, a built-in interface.

func f(arg int) (int, error) {
    if arg == 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 sentinel error is a predeclared variable that is used to signify a specific error condition.

var ErrOutOfTea = errors.New("no more tea available")
var ErrPower = errors.New("can't boil water")
func makeTea(arg int) error {
    if arg == 2 {
        return ErrOutOfTea
    } else if arg == 4 {

We can wrap errors with higher-level errors to add context. The simplest way to do this is with the %w verb in fmt.Errorf. Wrapped errors create a logical chain (A wraps B, which wraps C, etc.) that can be queried with functions like errors.Is and errors.AsType.

        return fmt.Errorf("making tea: %w", ErrPower)
    }
    return nil
}
func main() {
    for _, i := range []int{7, 42} {

It’s idiomatic to use an inline error check in the if line.

        if r, e := f(i); e != nil {
            fmt.Println("f failed:", e)
        } else {
            fmt.Println("f worked:", r)
        }
    }
    for i := range 5 {
        if err := makeTea(i); err != nil {

errors.Is checks that a given error (or any error in its chain) matches a specific error value. This is especially useful with wrapped or nested errors, allowing you to identify specific error types or sentinel errors in a chain of errors.

            if errors.Is(err, ErrOutOfTea) {
                fmt.Println("We should buy new tea!")
            } else if errors.Is(err, ErrPower) {
                fmt.Println("Now it is dark.")
            } else {
                fmt.Printf("unknown error: %s\n", err)
            }
            continue
        }
        fmt.Println("Tea is ready!")
    }
}
$ go run errors.go
f worked: 10
f failed: can't work with 42
Tea is ready!
Tea is ready!
We should buy new tea!
Tea is ready!
Now it is dark.

Next example: .

================================================ FILE: public/execing-processes ================================================ Go by Example: Exec'ing Processes

Go by Example: Exec'ing Processes

In the previous example we looked at spawning external processes. We do this when we need an external process accessible to a running Go process. Sometimes we just want to completely replace the current Go process with another (perhaps non-Go) one. To do this we’ll use Go’s implementation of the classic exec function.

package main
import (
    "os"
    "os/exec"
    "syscall"
)
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)
    }

Exec requires arguments in slice form (as opposed 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 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)
    }
}

When we run our program it is replaced by ls.

$ go run execing-processes.go
total 16
drwxr-xr-x  4 mark 136B Oct 3 16:29 .
drwxr-xr-x 91 mark 3.0K Oct 3 12:50 ..
-rw-r--r--  1 mark 1.3K Oct 3 16:28 execing-processes.go

Note that Go does not offer a classic Unix fork function. Usually this isn’t an issue though, since starting goroutines, spawning processes, and exec’ing processes covers most use cases for fork.

Next example: .

================================================ FILE: public/exit ================================================ Go by Example: Exit

Go by Example: Exit

Use os.Exit to immediately exit with a given status.

package main
import (
    "fmt"
    "os"
)
func main() {

defers 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)
}

Note that unlike e.g. C, Go does not use an integer return value from main to indicate exit status. If you’d like to exit with a non-zero status you should use os.Exit.

If you run exit.go using go run, the exit will be picked up by go and printed.

$ go run exit.go
exit status 3

By building and executing a binary you can see the status in the terminal.

$ go build exit.go
$ ./exit
$ echo $?
3

Note that the ! from our program never got printed.

================================================ FILE: public/file-paths ================================================ Go by Example: File Paths

Go by Example: File Paths

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)
}
$ 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

Next example: .

================================================ FILE: public/for ================================================ Go by Example: For

Go by Example: For

for is Go’s only looping construct. Here are some basic types of for loops.

package main
import "fmt"
func main() {

The most basic type, with a single condition.

    i := 1
    for i <= 3 {
        fmt.Println(i)
        i = i + 1
    }

A classic initial/condition/after for loop.

    for j := 0; j < 3; j++ {
        fmt.Println(j)
    }

Another way of accomplishing the basic “do this N times” iteration is range over an integer.

    for i := range 3 {
        fmt.Println("range", i)
    }

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 := range 6 {
        if n%2 == 0 {
            continue
        }
        fmt.Println(n)
    }
}
$ go run for.go
1
2
3
0
1
2
range 0
range 1
range 2
loop
1
3
5

We’ll see some other for forms later when we look at range statements, channels, and other data structures.

Next example: .

================================================ FILE: public/functions ================================================ Go by Example: Functions

Go by Example: Functions

Functions are central in Go. We’ll learn about functions with a few different examples.

package main
import "fmt"

Here’s a function that takes two ints and returns 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
}

When you have multiple consecutive parameters of the same type, you may omit the type name for the like-typed parameters up to the final parameter that declares the type.

func plusPlus(a, b, c int) int {
    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)
    res = plusPlus(1, 2, 3)
    fmt.Println("1+2+3 =", res)
}
$ go run functions.go 
1+2 = 3
1+2+3 = 6

There are several other features to Go functions. One is multiple return values, which we’ll look at next.

Next example: .

================================================ FILE: public/generics ================================================ Go by Example: Generics

Go by Example: Generics

Starting with version 1.18, Go has added support for generics, also known as type parameters.

package main
import "fmt"

As an example of a generic function, SlicesIndex takes a slice of any comparable type and an element of that type and returns the index of the first occurrence of v in s, or -1 if not present. The comparable constraint means that we can compare values of this type with the == and != operators. For a more thorough explanation of this type signature, see this blog post. Note that this function exists in the standard library as slices.Index.

func SlicesIndex[S ~[]E, E comparable](s S, v E) int {
    for i := range s {
        if v == s[i] {
            return i
        }
    }
    return -1
}

As an example of a generic type, List is a singly-linked list with values of any type.

type List[T any] struct {
    head, tail *element[T]
}
type element[T any] struct {
    next *element[T]
    val  T
}

We can define methods on generic types just like we do on regular types, but we have to keep the type parameters in place. The type is List[T], not List.

func (lst *List[T]) Push(v T) {
    if lst.tail == nil {
        lst.head = &element[T]{val: v}
        lst.tail = lst.head
    } else {
        lst.tail.next = &element[T]{val: v}
        lst.tail = lst.tail.next
    }
}

AllElements returns all the List elements as a slice. In the next example we’ll see a more idiomatic way of iterating over all elements of custom types.

func (lst *List[T]) AllElements() []T {
    var elems []T
    for e := lst.head; e != nil; e = e.next {
        elems = append(elems, e.val)
    }
    return elems
}
func main() {
    var s = []string{"foo", "bar", "zoo"}

When invoking generic functions, we can often rely on type inference. Note that we don’t have to specify the types for S and E when calling SlicesIndex - the compiler infers them automatically.

    fmt.Println("index of zoo:", SlicesIndex(s, "zoo"))

… though we could also specify them explicitly.

    _ = SlicesIndex[[]string, string](s, "zoo")
    lst := List[int]{}
    lst.Push(10)
    lst.Push(13)
    lst.Push(23)
    fmt.Println("list:", lst.AllElements())
}
$ go run generics.go
index of zoo: 2
list: [10 13 23]

Next example: .

================================================ FILE: public/goroutines ================================================ Go by Example: Goroutines

Go by Example: Goroutines

A goroutine is a lightweight thread of execution.

package main
import (
    "fmt"
    "time"
)
func f(from string) {
    for i := range 3 {
        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")

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")

Our two function calls are running asynchronously in separate goroutines now. Wait for them to finish (for a more robust approach, use a WaitGroup).

    time.Sleep(time.Second)
    fmt.Println("done")
}

When we run this program, we see the output of the blocking call first, then the output of the two goroutines. The goroutines’ output may be interleaved, because goroutines are being run concurrently by the Go runtime.

$ go run goroutines.go
direct : 0
direct : 1
direct : 2
goroutine : 0
going
goroutine : 1
goroutine : 2
done

Next we’ll look at a complement to goroutines in concurrent Go programs: channels.

Next example: .

================================================ FILE: public/hello-world ================================================ Go by Example: Hello World

Go by Example: Hello World

Our first program will print the classic “hello world” message. Here’s the full source code.

package main
import "fmt"
func main() {
    fmt.Println("hello world")
}

To run the program, put the code in hello-world.go and use go run.

$ go run hello-world.go
hello world

Sometimes we’ll want to build our programs into binaries. We can do this using go build.

$ go build hello-world.go
$ ls
hello-world    hello-world.go

We can then execute the built binary directly.

$ ./hello-world
hello world

Now that we can run and build basic Go programs, let’s learn more about the language.

Next example: .

================================================ FILE: public/http-client ================================================ Go by Example: HTTP Client

Go by Example: HTTP Client

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("https://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)
    }
}
$ go run http-clients.go
Response status: 200 OK
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Go by Example</title>

Next example: .

================================================ FILE: public/http-server ================================================ Go by Example: HTTP Server

Go by Example: HTTP Server

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)
}

Run the server in the background.

$ go run http-server.go &

Access the /hello route.

$ curl localhost:8090/hello
hello

Next example: .

================================================ FILE: public/if-else ================================================ Go by Example: If/Else

Go by Example: If/Else

Branching with if and else in Go is straight-forward.

package main
import "fmt"
func main() {

Here’s a basic example.

    if 7%2 == 0 {
        fmt.Println("7 is even")
    } else {
        fmt.Println("7 is odd")
    }

You can have an if statement without an else.

    if 8%4 == 0 {
        fmt.Println("8 is divisible by 4")
    }

Logical operators like && and || are often useful in conditions.

    if 8%2 == 0 || 7%2 == 0 {
        fmt.Println("either 8 or 7 are even")
    }

A statement can precede conditionals; any variables declared in this statement are available in the current and all subsequent branches.

    if num := 9; num < 0 {
        fmt.Println(num, "is negative")
    } else if num < 10 {
        fmt.Println(num, "has 1 digit")
    } else {
        fmt.Println(num, "has multiple digits")
    }
}

Note that you don’t need parentheses around conditions in Go, but that the braces are required.

$ go run if-else.go
7 is odd
8 is divisible by 4
either 8 or 7 are even
9 has 1 digit

There is no ternary if in Go, so you’ll need to use a full if statement even for basic conditions.

Next example: .

================================================ FILE: public/index.html ================================================ Go by Example

Go by Example

Go is an open source programming language designed for building scalable, secure and reliable software. Please read the official documentation to learn more.

Go by Example is a hands-on introduction to Go using annotated example programs. Check out the first example or browse the full list below.

Unless stated otherwise, examples here assume the latest major release Go and may use new language features. Try to upgrade to the latest version if something isn't working.

================================================ FILE: public/interfaces ================================================ Go by Example: Interfaces

Go by Example: Interfaces

Interfaces are named collections of method signatures.

package main
import (
    "fmt"
    "math"
)

Here’s a basic interface for geometric shapes.

type geometry interface {
    area() float64
    perim() float64
}

For our example we’ll implement this interface on rect and circle types.

type rect struct {
    width, height float64
}
type circle struct {
    radius float64
}

To implement an interface in Go, we just need to implement all the methods in the interface. Here we implement geometry on rects.

func (r rect) area() float64 {
    return r.width * r.height
}
func (r rect) perim() float64 {
    return 2*r.width + 2*r.height
}

The implementation for circles.

func (c circle) area() float64 {
    return math.Pi * c.radius * c.radius
}
func (c circle) perim() float64 {
    return 2 * math.Pi * c.radius
}

If a variable has an interface type, then we can call methods that are in the named interface. Here’s a generic 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())
}

Sometimes it’s useful to know the runtime type of an interface value. One option is using a type assertion as shown here; another is a type switch.

func detectCircle(g geometry) {
    if c, ok := g.(circle); ok {
        fmt.Println("circle with radius", c.radius)
    }
}
func main() {
    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)
    detectCircle(r)
    detectCircle(c)
}
$ go run interfaces.go
{3 4}
12
14
{5}
78.53981633974483
31.41592653589793
circle with radius 5

To understand how Go’s interfaces work under the hood, check out this blog post.

Next example: .

================================================ FILE: public/json ================================================ Go by Example: JSON

Go by Example: JSON

Go offers built-in support for JSON encoding and decoding, including to and from built-in and custom data types.

package main
import (
    "encoding/json"
    "fmt"
    "os"
    "strings"
)

We’ll use these two structs to demonstrate encoding and decoding of custom types below.

type response1 struct {
    Page   int
    Fruits []string
}

Only exported fields will be encoded/decoded in JSON. Fields must start with capital letters to be exported.

type response2 struct {
    Page   int      `json:"page"`
    Fruits []string `json:"fruits"`
}
func main() {

First we’ll look at encoding basic data types to JSON strings. Here are some examples for atomic values.

    bolB, _ := json.Marshal(true)
    fmt.Println(string(bolB))
    intB, _ := json.Marshal(1)
    fmt.Println(string(intB))
    fltB, _ := json.Marshal(2.34)
    fmt.Println(string(fltB))
    strB, _ := json.Marshal("gopher")
    fmt.Println(string(strB))

And here are some for slices and maps, which encode to JSON arrays and objects as you’d expect.

    slcD := []string{"apple", "peach", "pear"}
    slcB, _ := json.Marshal(slcD)
    fmt.Println(string(slcB))
    mapD := map[string]int{"apple": 5, "lettuce": 7}
    mapB, _ := json.Marshal(mapD)
    fmt.Println(string(mapB))

The JSON package can automatically encode your custom data types. It will only include exported fields in the encoded output and will by default use those names as the JSON keys.

    res1D := &response1{
        Page:   1,
        Fruits: []string{"apple", "peach", "pear"}}
    res1B, _ := json.Marshal(res1D)
    fmt.Println(string(res1B))

You can use tags on struct field declarations to customize the encoded JSON key names. Check the definition of 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"]}`)

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)

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 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])

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.Writers like os.Stdout or even HTTP response bodies.

    enc := json.NewEncoder(os.Stdout)
    d := map[string]int{"apple": 5, "lettuce": 7}
    enc.Encode(d)

Streaming reads from os.Readers like os.Stdin or HTTP request bodies is done with json.Decoder.

    dec := json.NewDecoder(strings.NewReader(str))
    res1 := response2{}
    dec.Decode(&res1)
    fmt.Println(res1)
}
$ go run json.go
true
1
2.34
"gopher"
["apple","peach","pear"]
{"apple":5,"lettuce":7}
{"Page":1,"Fruits":["apple","peach","pear"]}
{"page":1,"fruits":["apple","peach","pear"]}
map[num:6.13 strs:[a b]]
6.13
a
{1 [apple peach]}
apple
{"apple":5,"lettuce":7}
{1 [apple peach]}

We’ve covered the basic of JSON in Go here, but check out the JSON and Go blog post and JSON package docs for more.

Next example: .

================================================ FILE: public/line-filters ================================================ Go by Example: Line Filters

Go by Example: Line Filters

A line filter is a common type of program that reads input on stdin, processes it, and then prints some derived result to stdout. grep and sed are common line filters.

Here’s an example line filter in Go that writes a capitalized version of all input text. You can use this pattern to write your own Go line filters.

package main
import (
    "bufio"
    "fmt"
    "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)

Text returns the current token, here the next line, from the input.

    for scanner.Scan() {
        ucl := strings.ToUpper(scanner.Text())

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)
    }
}

To try out our line filter, first make a file with a few lowercase lines.

$ echo 'hello'   > /tmp/lines
$ echo 'filter' >> /tmp/lines

Then use the line filter to get uppercase lines.

$ cat /tmp/lines | go run line-filters.go
HELLO
FILTER

Next example: .

================================================ FILE: public/logging ================================================ Go by Example: Logging

Go by Example: Logging

The Go standard library provides straightforward tools for outputting logs from Go programs, with the log package for free-form output and the log/slog package for structured output.

package main
import (
    "bytes"
    "fmt"
    "log"
    "os"
    "log/slog"
)
func main() {

Simply invoking functions like Println from the log package uses the standard logger, which is already pre-configured for reasonable logging output to os.Stderr. Additional methods like Fatal* or Panic* will exit the program after logging.

    log.Println("standard logger")

Loggers can be configured with flags to set their output format. By default, the standard logger has the log.Ldate and log.Ltime flags set, and these are collected in log.LstdFlags. We can change its flags to emit time with microsecond accuracy, for example.

    log.SetFlags(log.LstdFlags | log.Lmicroseconds)
    log.Println("with micro")

It also supports emitting the file name and line from which the log function is called.

    log.SetFlags(log.LstdFlags | log.Lshortfile)
    log.Println("with file/line")

It may be useful to create a custom logger and pass it around. When creating a new logger, we can set a prefix to distinguish its output from other loggers.

    mylog := log.New(os.Stdout, "my:", log.LstdFlags)
    mylog.Println("from mylog")

We can set the prefix on existing loggers (including the standard one) with the SetPrefix method.

    mylog.SetPrefix("ohmy:")
    mylog.Println("from mylog")

Loggers can have custom output targets; any io.Writer works.

    var buf bytes.Buffer
    buflog := log.New(&buf, "buf:", log.LstdFlags)

This call writes the log output into buf.

    buflog.Println("hello")

This will actually show it on standard output.

    fmt.Print("from buflog:", buf.String())

The slog package provides structured log output. For example, logging in JSON format is straightforward.

    jsonHandler := slog.NewJSONHandler(os.Stderr, nil)
    myslog := slog.New(jsonHandler)
    myslog.Info("hi there")

In addition to the message, slog output can contain an arbitrary number of key=value pairs.

    myslog.Info("hello again", "key", "val", "age", 25)
}

Sample output; the date and time emitted will depend on when the example ran.

$ go run logging.go
2023/08/22 10:45:16 standard logger
2023/08/22 10:45:16.904141 with micro
2023/08/22 10:45:16 logging.go:40: with file/line
my:2023/08/22 10:45:16 from mylog
ohmy:2023/08/22 10:45:16 from mylog
from buflog:buf:2023/08/22 10:45:16 hello

These are wrapped for clarity of presentation on the website; in reality they are emitted on a single line.

{"time":"2023-08-22T10:45:16.904166391-07:00",
 "level":"INFO","msg":"hi there"}
{"time":"2023-08-22T10:45:16.904178985-07:00",
    "level":"INFO","msg":"hello again",
    "key":"val","age":25}

Next example: .

================================================ FILE: public/maps ================================================ Go by Example: Maps

Go by Example: Maps

Maps are Go’s built-in associative data type (sometimes called hashes or dicts in other languages).

package main
import (
    "fmt"
    "maps"
)
func main() {

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

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)

If the key doesn’t exist, the zero value of the value type is returned.

    v3 := m["k3"]
    fmt.Println("v3:", v3)

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)

To remove all key/value pairs from a map, use the clear builtin.

    clear(m)
    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)

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)

The maps package contains a number of useful utility functions for maps.

    n2 := map[string]int{"foo": 1, "bar": 2}
    if maps.Equal(n, n2) {
        fmt.Println("n == n2")
    }
}

Note that maps appear in the form map[k:v k:v] when printed with fmt.Println.

$ go run maps.go 
map: map[k1:7 k2:13]
v1: 7
v3: 0
len: 2
map: map[k1:7]
map: map[]
prs: false
map: map[bar:2 foo:1]
n == n2

Next example: .

================================================ FILE: public/methods ================================================ Go by Example: Methods

Go by Example: Methods

Go supports methods defined on struct types.

package main
import "fmt"
type rect struct {
    width, height int
}

This area method has a receiver type of *rect.

func (r *rect) area() int {
    return r.width * r.height
}

Methods can be defined for either pointer or value receiver types. Here’s an example of a value receiver.

func (r rect) perim() int {
    return 2*r.width + 2*r.height
}
func main() {
    r := rect{width: 10, height: 5}

Here we call the 2 methods defined for our struct.

    fmt.Println("area: ", r.area())
    fmt.Println("perim:", r.perim())

Go automatically handles conversion between values and pointers for method calls. You may want to use a pointer receiver type to avoid copying on method calls or to allow the method to mutate the receiving struct.

    rp := &r
    fmt.Println("area: ", rp.area())
    fmt.Println("perim:", rp.perim())
}
$ go run methods.go 
area:  50
perim: 30
area:  50
perim: 30

Next we’ll look at Go’s mechanism for grouping and naming related sets of methods: interfaces.

Next example: .

================================================ FILE: public/multiple-return-values ================================================ Go by Example: Multiple Return Values

Go by Example: Multiple Return Values

Go has built-in support for multiple return values. This feature is used often in idiomatic Go, for example to return both result and error values from a function.

package main
import "fmt"

The (int, int) in this function signature shows that the function returns 2 ints.

func vals() (int, int) {
    return 3, 7
}
func main() {

Here we use the 2 different return values from the call with multiple assignment.

    a, b := vals()
    fmt.Println(a)
    fmt.Println(b)

If you only want a subset of the returned values, use the blank identifier _.

    _, c := vals()
    fmt.Println(c)
}
$ go run multiple-return-values.go
3
7
7

Accepting a variable number of arguments is another nice feature of Go functions; we’ll look at this next.

Next example: .

================================================ FILE: public/mutexes ================================================ Go by Example: Mutexes

Go by Example: Mutexes

In the previous example we saw how to manage simple counter state using atomic operations. For more complex state we can use a mutex to safely access data across multiple goroutines.

package main
import (
    "fmt"
    "sync"
)

Container holds a map of counters; since we want to update it concurrently from multiple goroutines, we add a Mutex to synchronize access. Note that mutexes must not be copied, so if this struct is passed around, it should be done by pointer.

type Container struct {
    mu       sync.Mutex
    counters map[string]int
}

Lock the mutex before accessing counters; unlock it at the end of the function using a defer statement.

func (c *Container) inc(name string) {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.counters[name]++
}

Note that the zero value of a mutex is usable as-is, so no initialization is required here.

func main() {
    c := Container{
        counters: map[string]int{"a": 0, "b": 0},
    }
    var wg sync.WaitGroup

This function increments a named counter in a loop.

    doIncrement := func(name string, n int) {
        for range n {
            c.inc(name)
        }
    }

Run several goroutines concurrently; note that they all access the same Container, and two of them access the same counter.

    wg.Go(func() {
        doIncrement("a", 10000)
    })
    wg.Go(func() {
        doIncrement("a", 10000)
    })
    wg.Go(func() {
        doIncrement("b", 10000)
    })

Wait for the goroutines to finish

    wg.Wait()
    fmt.Println(c.counters)
}

Running the program shows that the counters updated as expected.

$ go run mutexes.go
map[a:20000 b:10000]

Next we’ll look at implementing this same state management task using only goroutines and channels.

Next example: .

================================================ FILE: public/non-blocking-channel-operations ================================================ Go by Example: Non-Blocking Channel Operations

Go by Example: Non-Blocking Channel Operations

Basic sends and receives on channels are blocking. However, we can use select with a default clause to implement non-blocking sends, receives, and even non-blocking multi-way selects.

package main
import "fmt"
func main() {
    messages := make(chan string)
    signals := make(chan bool)

Here’s a non-blocking receive. If a value is available on 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. 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 cases 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")
    }
}
$ go run non-blocking-channel-operations.go 
no message received
no message sent
no activity

Next example: .

================================================ FILE: public/number-parsing ================================================ Go by Example: Number Parsing

Go by Example: Number Parsing

Parsing numbers from strings is a basic but common task in many programs; here’s how to do it in Go.

package main

The built-in package strconv provides the number parsing.

import (
    "fmt"
    "strconv"
)
func main() {

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)

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)

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)
}
$ go run number-parsing.go 
1.234
123
456
789
135
strconv.ParseInt: parsing "wat": invalid syntax

Next we’ll look at another common parsing task: URLs.

Next example: .

================================================ FILE: public/panic ================================================ Go by Example: Panic

Go by Example: Panic

A panic typically means something went unexpectedly wrong. Mostly we use it to fail fast on errors that shouldn’t occur during normal operation, or that we aren’t prepared to handle gracefully.

package main
import (
    "os"
    "path/filepath"
)
func main() {

We’ll use panic throughout this site to check for unexpected errors. This is the only program on the site designed to panic.

    panic("a problem")

A common use of panic is to abort if a function returns an error value that we don’t know how to (or want to) handle. Here’s an example of panicking if we get an unexpected error when creating a new file.

    path := filepath.Join(os.TempDir(), "file")
    _, err := os.Create(path)
    if err != nil {
        panic(err)
    }
}

Running this program will cause it to panic, print an error message and goroutine traces, and exit with a non-zero status.

When first panic in main fires, the program exits without reaching the rest of the code. If you’d like to see the program try to create a temp file, comment the first panic out.

$ go run panic.go
panic: a problem
goroutine 1 [running]:
main.main()
    /.../panic.go:12 +0x47
...
exit status 2

Note that unlike some languages which use exceptions for handling of many errors, in Go it is idiomatic to use error-indicating return values wherever possible.

Next example: .

================================================ FILE: public/pointers ================================================ Go by Example: Pointers

Go by Example: Pointers

Go supports pointers, allowing you to pass references to values and records within your program.

package main
import "fmt"

We’ll show how pointers work in contrast to values with 2 functions: zeroval and zeroptr. zeroval has an int parameter, so arguments will be passed to it by value. zeroval will get a copy of ival distinct from the one in the calling function.

func zeroval(ival int) {
    ival = 0
}

zeroptr in contrast has an *int parameter, meaning that it takes an int pointer. The *iptr code in the function body then dereferences the pointer from its memory address to the current value at that address. Assigning a value to a dereferenced pointer changes the value at the referenced address.

func zeroptr(iptr *int) {
    *iptr = 0
}
func main() {
    i := 1
    fmt.Println("initial:", 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)

Pointers can be printed too.

    fmt.Println("pointer:", &i)
}

zeroval doesn’t change the i in main, but zeroptr does because it has a reference to the memory address for that variable.

$ go run pointers.go
initial: 1
zeroval: 1
zeroptr: 0
pointer: 0x42131100

Next example: .

================================================ FILE: public/random-numbers ================================================ Go by Example: Random Numbers

Go by Example: Random Numbers

Go’s math/rand/v2 package provides pseudorandom number generation.

package main
import (
    "fmt"
    "math/rand/v2"
)
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()

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()

If you want a known seed, create a new rand.Source and pass it into the New constructor. NewPCG creates a new PCG source that requires a seed of two uint64 numbers.

    s2 := rand.NewPCG(42, 1024)
    r2 := rand.New(s2)
    fmt.Print(r2.IntN(100), ",")
    fmt.Print(r2.IntN(100))
    fmt.Println()
    s3 := rand.NewPCG(42, 1024)
    r3 := rand.New(s3)
    fmt.Print(r3.IntN(100), ",")
    fmt.Print(r3.IntN(100))
    fmt.Println()
}

Some of the generated numbers may be different when you run the sample.

$ go run random-numbers.go
68,56
0.8090228139659177
5.840125017402497,6.937056298890035
94,49
94,49

See the math/rand/v2 package docs for references on other random quantities that Go can provide.

Next example: .

================================================ FILE: public/range-over-built-in-types ================================================ Go by Example: Range over Built-in Types

Go by Example: Range over Built-in Types

range iterates over elements in a variety of built-in data structures. Let’s see how to use range with some of the data structures we’ve already learned.

package main
import "fmt"
func main() {

Here we use range to sum the numbers in a slice. Arrays work like this too.

    nums := []int{2, 3, 4}
    sum := 0
    for _, num := range nums {
        sum += num
    }
    fmt.Println("sum:", sum)

range on arrays and slices provides both the index and value for each entry. Above we didn’t need the index, so we ignored it with the blank identifier _. Sometimes we actually want the indexes though.

    for i, num := range nums {
        if num == 3 {
            fmt.Println("index:", i)
        }
    }

range on map iterates over key/value pairs.

    kvs := map[string]string{"a": "apple", "b": "banana"}
    for k, v := range kvs {
        fmt.Printf("%s -> %s\n", k, v)
    }

range can also iterate over just the keys of a map.

    for k := range kvs {
        fmt.Println("key:", k)
    }

range on strings iterates over Unicode code points. The first value is the starting byte index of the rune and the second the rune itself. See Strings and Runes for more details.

    for i, c := range "go" {
        fmt.Println(i, c)
    }
}
$ go run range-over-built-in-types.go
sum: 9
index: 1
a -> apple
b -> banana
key: a
key: b
0 103
1 111

Next example: .

================================================ FILE: public/range-over-channels ================================================ Go by Example: Range over Channels

Go by Example: Range over Channels

In a previous example we saw how for and range provide iteration over basic data structures. We can also use this syntax to iterate over values received from a channel.

package main
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)

This range iterates over each element as it’s received from queue. Because we closed the channel above, the iteration terminates after receiving the 2 elements.

    for elem := range queue {
        fmt.Println(elem)
    }
}
$ go run range-over-channels.go
one
two

This example also showed that it’s possible to close a non-empty channel but still have the remaining values be received.

Next example: .

================================================ FILE: public/range-over-iterators ================================================ Go by Example: Range over Iterators

Go by Example: Range over Iterators

Starting with version 1.23, Go has added support for iterators, which lets us range over pretty much anything!

package main
import (
    "fmt"
    "iter"
    "slices"
    "strings"
)

Let’s look at the List type from the previous example again. In that example we had an AllElements method that returned a slice of all elements in the list. With Go iterators, we can do it better - as shown below.

type List[T any] struct {
    head, tail *element[T]
}
type element[T any] struct {
    next *element[T]
    val  T
}
func (lst *List[T]) Push(v T) {
    if lst.tail == nil {
        lst.head = &element[T]{val: v}
        lst.tail = lst.head
    } else {
        lst.tail.next = &element[T]{val: v}
        lst.tail = lst.tail.next
    }
}

All returns an iterator, which in Go is a function with a special signature.

func (lst *List[T]) All() iter.Seq[T] {
    return func(yield func(T) bool) {

The iterator function takes another function as a parameter, called yield by convention (but the name can be arbitrary). It will call yield for every element we want to iterate over, and note yield’s return value for a potential early termination.

        for e := lst.head; e != nil; e = e.next {
            if !yield(e.val) {
                return
            }
        }
    }
}

Iteration doesn’t require an underlying data structure, and doesn’t even have to be finite! Here’s a function returning an iterator over Fibonacci numbers: it keeps running as long as yield keeps returning true.

func genFib() iter.Seq[int] {
    return func(yield func(int) bool) {
        a, b := 0, 1
        for {
            if !yield(a) {
                return
            }
            a, b = b, a+b
        }
    }
}
func main() {
    lst := List[int]{}
    lst.Push(10)
    lst.Push(13)
    lst.Push(23)

Since List.All returns an iterator, we can use it in a regular range loop.

    for e := range lst.All() {
        fmt.Println(e)
    }

Packages like slices have a number of useful functions to work with iterators. For example, Collect takes any iterator and collects all its values into a slice.

    all := slices.Collect(lst.All())
    fmt.Println("all:", all)

Standard library packages now expose iterator helpers too. For example, strings.SplitSeq iterates over parts of a byte slice without first building a result slice.

    for part := range strings.SplitSeq("go-by-example", "-") {
        fmt.Printf("part: %s\n", part)
    }
    for n := range genFib() {

Once the loop hits break or an early return, the yield function passed to the iterator will return false.

        if n >= 10 {
            break
        }
        fmt.Println(n)
    }
}
$ go run range-over-iterators.go
10
13
23
all: [10 13 23]
part: go
part: by
part: example
0
1
1
2
3
5
8

Next example: .

================================================ FILE: public/rate-limiting ================================================ Go by Example: Rate Limiting

Go by Example: Rate Limiting

Rate limiting is an important mechanism for controlling resource utilization and maintaining quality of service. Go elegantly supports rate limiting with goroutines, channels, and tickers.

package main
import (
    "fmt"
    "time"
)
func main() {

First we’ll look at basic rate limiting. Suppose we want to limit our handling of incoming requests. We’ll serve these requests off a channel of the same name.

    requests := make(chan int, 5)
    for i := 1; i <= 5; i++ {
        requests <- i
    }
    close(requests)

This limiter channel will receive a value every 200 milliseconds. This is the regulator in our rate limiting scheme.

    limiter := time.Tick(200 * time.Millisecond)

By blocking on a receive from the limiter channel before serving each request, we limit ourselves to 1 request every 200 milliseconds.

    for req := range requests {
        <-limiter
        fmt.Println("request", req, time.Now())
    }

We may want to allow short bursts of requests in our rate limiting scheme while preserving the overall rate limit. We can accomplish this by buffering our limiter channel. This burstyLimiter channel will allow bursts of up to 3 events.

    burstyLimiter := make(chan time.Time, 3)

Fill up the channel to represent allowed bursting.

    for range 3 {
        burstyLimiter <- time.Now()
    }

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(200 * time.Millisecond) {
            burstyLimiter <- t
        }
    }()

Now simulate 5 more incoming requests. The first 3 of these will benefit from the burst capability of burstyLimiter.

    burstyRequests := make(chan int, 5)
    for i := 1; i <= 5; i++ {
        burstyRequests <- i
    }
    close(burstyRequests)
    for req := range burstyRequests {
        <-burstyLimiter
        fmt.Println("request", req, time.Now())
    }
}

Running our program we see the first batch of requests handled once every ~200 milliseconds as desired.

$ go run rate-limiting.go
request 1 2012-10-19 00:38:18.687438 +0000 UTC
request 2 2012-10-19 00:38:18.887471 +0000 UTC
request 3 2012-10-19 00:38:19.087238 +0000 UTC
request 4 2012-10-19 00:38:19.287338 +0000 UTC
request 5 2012-10-19 00:38:19.487331 +0000 UTC

For the second batch of requests we serve the first 3 immediately because of the burstable rate limiting, then serve the remaining 2 with ~200ms delays each.

request 1 2012-10-19 00:38:20.487578 +0000 UTC
request 2 2012-10-19 00:38:20.487645 +0000 UTC
request 3 2012-10-19 00:38:20.487676 +0000 UTC
request 4 2012-10-19 00:38:20.687483 +0000 UTC
request 5 2012-10-19 00:38:20.887542 +0000 UTC

Next example: .

================================================ FILE: public/reading-files ================================================ Go by Example: Reading Files

Go by Example: Reading Files

Reading and writing files are basic tasks needed for many Go programs. First we’ll look at some examples of reading files.

package main
import (
    "bufio"
    "fmt"
    "io"
    "os"
    "path/filepath"
)

Reading files requires checking most calls for errors. This helper will streamline our error checks below.

func check(e error) {
    if e != nil {
        panic(e)
    }
}
func main() {

Perhaps the most basic file reading task is slurping a file’s entire contents into memory.

    path := filepath.Join(os.TempDir(), "dat")
    dat, err := os.ReadFile(path)
    check(err)
    fmt.Print(string(dat))

You’ll often want more control over how and what parts of a file are read. For these tasks, start by Opening a file to obtain an os.File value.

    f, err := os.Open(path)
    check(err)

Read some bytes from the beginning of the file. Allow up to 5 to be read but also note how many actually were read.

    b1 := make([]byte, 5)
    n1, err := f.Read(b1)
    check(err)
    fmt.Printf("%d bytes: %s\n", n1, string(b1[:n1]))

You can also Seek to a known location in the file and Read from there.

    o2, err := f.Seek(6, io.SeekStart)
    check(err)
    b2 := make([]byte, 2)
    n2, err := f.Read(b2)
    check(err)
    fmt.Printf("%d bytes @ %d: ", n2, o2)
    fmt.Printf("%v\n", string(b2[:n2]))

Other methods of seeking are relative to the current cursor position,

    _, err = f.Seek(2, io.SeekCurrent)
    check(err)

and relative to the end of the file.

    _, err = f.Seek(-4, io.SeekEnd)
    check(err)

The io package provides some functions that may be helpful for file reading. For example, reads like the ones above can be more robustly implemented with ReadAtLeast.

    o3, err := f.Seek(6, io.SeekStart)
    check(err)
    b3 := make([]byte, 2)
    n3, err := io.ReadAtLeast(f, b3, 2)
    check(err)
    fmt.Printf("%d bytes @ %d: %s\n", n3, o3, string(b3))

There is no built-in rewind, but Seek(0, io.SeekStart) accomplishes this.

    _, err = f.Seek(0, io.SeekStart)
    check(err)

The bufio package implements a buffered reader that may be useful both for its efficiency with many small reads and because of the additional reading methods it provides.

    r4 := bufio.NewReader(f)
    b4, err := r4.Peek(5)
    check(err)
    fmt.Printf("5 bytes: %s\n", string(b4))

Close the file when you’re done (usually this would be scheduled immediately after Opening with defer).

    f.Close()
}
$ echo "hello" > /tmp/dat
$ echo "go" >>   /tmp/dat
$ go run reading-files.go
hello
go
5 bytes: hello
2 bytes @ 6: go
2 bytes @ 6: go
5 bytes: hello

Next we’ll look at writing files.

Next example: .

================================================ FILE: public/recover ================================================ Go by Example: Recover

Go by Example: Recover

Go makes it possible to recover from a panic, by using the recover built-in function. A recover can stop a panic from aborting the program and let it continue with execution instead.

An example of where this can be useful: a server wouldn’t want to crash if one of the client connections exhibits a critical error. Instead, the server would want to close that connection and continue serving other clients. In fact, this is what Go’s net/http does by default for HTTP servers.

package main
import "fmt"

This function panics.

func mayPanic() {
    panic("a problem")
}

recover must be called within a deferred function. When the enclosing function panics, the defer will activate and a recover call within it will catch the panic.

func main() {

The return value of recover is the error raised in the call to panic.

    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered. Error:\n", r)
        }
    }()
    mayPanic()

This code will not run, because mayPanic panics. The execution of main stops at the point of the panic and resumes in the deferred closure.

    fmt.Println("After mayPanic()")
}
$ go run recover.go
Recovered. Error:
 a problem

Next example: .

================================================ FILE: public/recursion ================================================ Go by Example: Recursion

Go by Example: Recursion

Go supports recursive functions. Here’s a classic example.

package main
import "fmt"

This fact function calls itself until it reaches the base case of fact(0).

func fact(n int) int {
    if n == 0 {
        return 1
    }
    return n * fact(n-1)
}
func main() {
    fmt.Println(fact(7))

Anonymous functions can also be recursive, but this requires explicitly declaring a variable with var to store the function before it’s defined.

    var fib func(n int) int
    fib = func(n int) int {
        if n < 2 {
            return n
        }

Since fib was previously declared in main, Go knows which function to call with fib here.

        return fib(n-1) + fib(n-2)
    }
    fmt.Println(fib(7))
}
$ go run recursion.go 
5040
13

Next example: .

================================================ FILE: public/regular-expressions ================================================ Go by Example: Regular Expressions

Go by Example: Regular Expressions

Go offers built-in support for regular expressions. Here are some examples of common regexp-related tasks in Go.

package main
import (
    "bytes"
    "fmt"
    "regexp"
)
func main() {

This tests whether a pattern matches a string.

    match, _ := regexp.MatchString("p([a-z]+)ch", "peach")
    fmt.Println(match)

Above we used a string pattern directly, but for other regexp tasks you’ll need to Compile an optimized Regexp struct.

    r, _ := regexp.Compile("p([a-z]+)ch")

Many methods are available on these structs. Here’s a match test like we saw earlier.

    fmt.Println(r.MatchString("peach"))

This finds the match for the regexp.

    fmt.Println(r.FindString("peach punch"))

This also finds the first match but returns the start and end indexes for the match instead of the matching text.

    fmt.Println("idx:", r.FindStringIndex("peach punch"))

The Submatch variants include information about both the whole-pattern matches and the submatches within those matches. For example this will return information for both p([a-z]+)ch and ([a-z]+).

    fmt.Println(r.FindStringSubmatch("peach punch"))

Similarly this will return information about the indexes of matches and submatches.

    fmt.Println(r.FindStringSubmatchIndex("peach punch"))

The All variants of these functions apply to all matches in the input, not just the first. For example to find all matches for a regexp.

    fmt.Println(r.FindAllString("peach punch pinch", -1))

These All variants are available for the other functions we saw above as well.

    fmt.Println("all:", r.FindAllStringSubmatchIndex(
        "peach punch pinch", -1))

Providing a non-negative integer as the second argument to these functions will limit the number of matches.

    fmt.Println(r.FindAllString("peach punch pinch", 2))

Our examples above had string arguments and used names like MatchString. We can also provide []byte arguments and drop String from the function name.

    fmt.Println(r.Match([]byte("peach")))

When creating global variables with regular expressions you can use the MustCompile variation of Compile. MustCompile panics instead of returning an error, which makes it safer to use for global variables.

    r = regexp.MustCompile("p([a-z]+)ch")
    fmt.Println("regexp:", r)

The regexp package can also be used to replace subsets of strings with other values.

    fmt.Println(r.ReplaceAllString("a peach", "<fruit>"))

The Func variant allows you to transform matched text with a given function.

    in := []byte("a peach")
    out := r.ReplaceAllFunc(in, bytes.ToUpper)
    fmt.Println(string(out))
}
$ go run regular-expressions.go
true
true
peach
idx: [0 5]
[peach ea]
[0 5 1 3]
[peach punch pinch]
all: [[0 5 1 3] [6 11 7 9] [12 17 13 15]]
[peach punch]
true
regexp: p([a-z]+)ch
a <fruit>
a PEACH

For a complete reference on Go regular expressions check the regexp package docs.

Next example: .

================================================ FILE: public/select ================================================ Go by Example: Select

Go by Example: Select

Go’s select lets you wait on multiple channel operations. Combining goroutines and channels with select is a powerful feature of Go.

package main
import (
    "fmt"
    "time"
)
func main() {

For our example we’ll select across two channels.

    c1 := make(chan string)
    c2 := make(chan string)

Each channel will receive a value after some amount of time, to simulate e.g. blocking RPC operations executing in concurrent goroutines.

    go func() {
        time.Sleep(1 * time.Second)
        c1 <- "one"
    }()
    go func() {
        time.Sleep(2 * time.Second)
        c2 <- "two"
    }()

We’ll use select to await both of these values simultaneously, printing each one as it arrives.

    for range 2 {
        select {
        case msg1 := <-c1:
            fmt.Println("received", msg1)
        case msg2 := <-c2:
            fmt.Println("received", msg2)
        }
    }
}

We receive the values "one" and then "two" as expected.

$ time go run select.go 
received one
received two

Note that the total execution time is only ~2 seconds since both the 1 and 2 second Sleeps execute concurrently.

real    0m2.245s

Next example: .

================================================ FILE: public/sha256-hashes ================================================ Go by Example: SHA256 Hashes

Go by Example: SHA256 Hashes

SHA256 hashes are frequently used to compute short identities for binary or text blobs. For example, TLS/SSL certificates use SHA256 to compute a certificate’s signature. Here’s how to compute SHA256 hashes in Go.

package main

Go implements several hash functions in various crypto/* packages.

import (
    "crypto/sha256"
    "fmt"
)
func main() {
    s := "sha256 this string"

Here we start with a new hash.

    h := sha256.New()

Write expects bytes. If you have a string s, use []byte(s) to coerce it to bytes.

    h.Write([]byte(s))

This gets the finalized hash result as a byte slice. The argument to Sum can be used to append to an existing byte slice: it usually isn’t needed.

    bs := h.Sum(nil)
    fmt.Println(s)
    fmt.Printf("%x\n", bs)
}

Running the program computes the hash and prints it in a human-readable hex format.

$ go run sha256-hashes.go
sha256 this string
1af1dfa857bf1d8814fe1af8983c18080019922e557f15a8a...

You can compute other hashes using a similar pattern to the one shown above. For example, to compute SHA512 hashes import crypto/sha512 and use sha512.New().

Note that if you need cryptographically secure hashes, you should carefully research hash strength!

Next example: .

================================================ FILE: public/signals ================================================ Go by Example: Signals

Go by Example: Signals

Sometimes we’d like our Go programs to intelligently handle Unix signals. For example, we might want a server to gracefully shutdown when it receives a SIGTERM, or a command-line tool to stop processing input if it receives a SIGINT. Here’s a modern way to handle signals using contexts.

package main
import (
    "context"
    "fmt"
    "os/signal"
    "syscall"
)

signal.NotifyContext returns a context that’s canceled when one of the listed signals arrives.

func main() {
    ctx, stop := signal.NotifyContext(
        context.Background(), syscall.SIGINT, syscall.SIGTERM)
    defer stop()

The program will wait here until one of the configured signals is received.

    fmt.Println("awaiting signal")
    <-ctx.Done()

context.Cause reports why the context was canceled. For a signal-triggered cancellation, this includes the signal value.

    fmt.Println()
    fmt.Println(context.Cause(ctx))
    fmt.Println("exiting")
}

When we run this program it will block waiting for a signal. By typing ctrl-C (which the terminal shows as ^C) we can send a SIGINT signal, causing the program to print the cancellation cause and then exit.

$ go run signals.go
awaiting signal
^C
interrupt signal received
exiting

Next example: .

================================================ FILE: public/site.css ================================================ /* CSS reset: https://meyerweb.com/eric/tools/css/reset/ */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; } article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } body { line-height: 1; } ol, ul { list-style: none; } blockquote, q { quotes: none; } blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } table { border-collapse: collapse; border-spacing: 0; } /* Layout and typography */ body { font-family: 'Georgia', serif; font-size: 16px; line-height: 20px; } em { font-style: italic; } h2 { font-size: 32px; line-height: 40px; margin-top: 40px; } h2 a { text-decoration: none; } div.example { width: 995px; max-width: calc(100% - 24px); margin-left: auto; margin-right: auto; margin-bottom: 120px; } div.example table { margin-top: 15px; margin-bottom: 20px; } p.next { margin-bottom: 20px; } p.footer { font-size: 75%; } div#intro { width: 420px; min-width: 420px; max-width: 420px; margin-left: auto; margin-right: auto; margin-bottom: 120px; } div#intro p { padding-top: 20px; } div#intro ul { padding-top: 20px; } table td { border: 0; outline: 0; } td.docs { width: 420px; max-width: 420px; min-width: 420px; min-height: 5px; vertical-align: top; text-align: left; } td.docs p { padding-right: 5px; padding-top: 5px; padding-bottom: 15px; } td.code { width: 575px; max-width: 575px; min-width: 575px; padding-top: 5px; padding-right: 5px; padding-left: 5px; padding-bottom: 5px; vertical-align: top; } td.code.leading { padding-bottom: 11px; } pre, code { font-size: 14px; line-height: 18px; font-family: 'Menlo', 'Monaco', 'Consolas', 'Lucida Console', monospace; } img.copy, img.run { height: 16px; width: 16px; float: right } img.copy, img.run { cursor: pointer; } img.copy { margin-right: 4px; } /* Colors: light mode */ body { background-color: #ffffff; color: #252519; } td.code.empty { background: #ffffff; } a, a:visited { color: #261a3b; } p.footer { color: #808080; } p.footer a, p.footer a:visited { color: #808080; } td.code { background: #f0f0f0; } /* Syntax highlighting: light mode */ body .nx { } /* Name.Other: package, variable, struct, param, generic type names, etc. */ body .nf { } /* Name.Function: function names (def and call) */ body .o { } /* Operator: :=, &, *, +, &&, <, etc. */ body .p { } /* Plain: = , . : [ ( { etc. */ body .k { color: #954121 } /* Keyword: if, for, range, return, defer, etc. */ body .kc { color: #954121 } /* Keyword.Constant: nil, true, false */ body .kd { color: #954121 } /* Keyword.Declaration: func, var, type, struct, map, chan, etc. */ body .kn { color: #954121 } /* Keyword.Namespace: package, import */ body .nb { color: #954121 } /* Name.Builtin: make, len, delete, append, etc. */ body .kt { color: #b00040 } /* Keyword.Type: string, int, byte, error, etc. */ body .m { color: #666666 } /* Literal.Number */ body .mf { color: #666666 } /* Literal.Number.Float */ body .mh { color: #666666 } /* Literal.Number.Hex */ body .mi { color: #666666 } /* Literal.Number.Integer */ body .mo { color: #666666 } /* Literal.Number.Oct */ body .s { color: #219161 } /* Literal.String */ body .sc { color: #219161 } /* Literal.String.Char */ body .gp { color: #000080 } /* Generic.Prompt: shell prompt */ body .go { color: #808080 } /* Generic.Output: shell output */ body .c1 { color: #808080 } /* Comment.Single */ @media (prefers-color-scheme: dark) { /* Colors: dark mode */ body { background-color: #1f1f1f; color: #dadada; } td.code.empty { background: #1f1f1f; } a, a:visited { color: #e4e4e4; } p.footer { color: #898e98; } p.footer a, p.footer a:visited { color: #898e98; } td.code { background: #282828; } /* Syntax highlighting: dark mode */ body .nx { } /* Name.Other */ body .nf { } /* Name.Function */ body .o { } /* Operator */ body .p { } /* Plain */ body .k { color: #af5a54 } /* Keyword */ body .kc { color: #af5a54 } /* Keyword.Constant */ body .kd { color: #af5a54 } /* Keyword.Declaration */ body .kn { color: #af5a54 } /* Keyword.Namespace */ body .nb { color: #af5a54 } /* Name.Builtin */ body .kt { color: #b64343 } /* Keyword.Type */ body .m { color: #688ec8 } /* Literal.Number */ body .mf { color: #688ec8 } /* Literal.Number.Float */ body .mh { color: #688ec8 } /* Literal.Number.Hex */ body .mi { color: #688ec8 } /* Literal.Number.Integer */ body .mo { color: #688ec8 } /* Literal.Number.Oct */ body .s { color: #718e72 } /* Literal.String */ body .sc { color: #718e72 } /* Literal.String.Char */ body .gp { color: #8a6ab1 } /* Generic.Prompt */ body .go { color: #868686 } /* Generic.Output */ body .c1 { color: #868686 } /* Comment.Single */ } ================================================ FILE: public/site.js ================================================ /*! * clipboard.js v1.5.13 * https://zenorocha.github.io/clipboard.js * * Licensed MIT © Zeno Rocha */ !function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.Clipboard=t()}}(function(){var t,e,n;return function t(e,n,o){function r(c,a){if(!n[c]){if(!e[c]){var l="function"==typeof require&&require;if(!a&&l)return l(c,!0);if(i)return i(c,!0);var s=new Error("Cannot find module '"+c+"'");throw s.code="MODULE_NOT_FOUND",s}var u=n[c]={exports:{}};e[c][0].call(u.exports,function(t){var n=e[c][1][t];return r(n?n:t)},u,u.exports,t,e,n,o)}return n[c].exports}for(var i="function"==typeof require&&require,c=0;c Go by Example: Slices

Go by Example: Slices

Slices are an important data type in Go, giving a more powerful interface to sequences than arrays.

package main
import (
    "fmt"
    "slices"
)
func main() {

Unlike arrays, slices are typed only by the elements they contain (not the number of elements). An uninitialized slice equals to nil and has length 0.

    var s []string
    fmt.Println("uninit:", s, s == nil, len(s) == 0)

To create a slice with non-zero length, use the builtin make. Here we make a slice of strings of length 3 (initially zero-valued). By default a new slice’s capacity is equal to its length; if we know the slice is going to grow ahead of time, it’s possible to pass a capacity explicitly as an additional parameter to make.

    s = make([]string, 3)
    fmt.Println("emp:", s, "len:", len(s), "cap:", cap(s))

We can set and get just like with arrays.

    s[0] = "a"
    s[1] = "b"
    s[2] = "c"
    fmt.Println("set:", s)
    fmt.Println("get:", s[2])

len returns the length of the slice as expected.

    fmt.Println("len:", len(s))

In addition to these basic operations, slices support several more that make them richer than arrays. One is the builtin 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.

    s = append(s, "d")
    s = append(s, "e", "f")
    fmt.Println("apd:", s)

Slices can also be copy’d. Here we create an empty slice c of the same length as s and copy into c from s.

    c := make([]string, len(s))
    copy(c, s)
    fmt.Println("cpy:", c)

Slices support a “slice” operator with the syntax slice[low:high]. For example, this gets a slice of the elements s[2], s[3], and s[4].

    l := s[2:5]
    fmt.Println("sl1:", l)

This slices up to (but excluding) s[5].

    l = s[:5]
    fmt.Println("sl2:", l)

And this slices up from (and including) s[2].

    l = s[2:]
    fmt.Println("sl3:", l)

We can declare and initialize a variable for slice in a single line as well.

    t := []string{"g", "h", "i"}
    fmt.Println("dcl:", t)

The slices package contains a number of useful utility functions for slices.

    t2 := []string{"g", "h", "i"}
    if slices.Equal(t, t2) {
        fmt.Println("t == t2")
    }

Slices can be composed into multi-dimensional data structures. The length of the inner slices can vary, unlike with multi-dimensional arrays.

    twoD := make([][]int, 3)
    for i := range 3 {
        innerLen := i + 1
        twoD[i] = make([]int, innerLen)
        for j := range innerLen {
            twoD[i][j] = i + j
        }
    }
    fmt.Println("2d: ", twoD)
}

Note that while slices are different types than arrays, they are rendered similarly by fmt.Println.

$ go run slices.go
uninit: [] true true
emp: [  ] len: 3 cap: 3
set: [a b c]
get: c
len: 3
apd: [a b c d e f]
cpy: [a b c d e f]
sl1: [c d e]
sl2: [a b c d e]
sl3: [c d e f]
dcl: [g h i]
t == t2
2d:  [[0] [1 2] [2 3 4]]

Check out this great blog post by the Go team for more details on the design and implementation of slices in Go.

Now that we’ve seen arrays and slices we’ll look at Go’s other key builtin data structure: maps.

Next example: .

================================================ FILE: public/sorting ================================================ Go by Example: Sorting

Go by Example: Sorting

Go’s slices package implements sorting for builtins and user-defined types. We’ll look at sorting for builtins first.

package main
import (
    "fmt"
    "slices"
)
func main() {

Sorting functions are generic, and work for any ordered built-in type. For a list of ordered types, see cmp.Ordered.

    strs := []string{"c", "a", "b"}
    slices.Sort(strs)
    fmt.Println("Strings:", strs)

An example of sorting ints.

    ints := []int{7, 2, 4}
    slices.Sort(ints)
    fmt.Println("Ints:   ", ints)

We can also use the slices package to check if a slice is already in sorted order.

    s := slices.IsSorted(ints)
    fmt.Println("Sorted: ", s)
}
$ go run sorting.go
Strings: [a b c]
Ints:    [2 4 7]
Sorted:  true

Next example: .

================================================ FILE: public/sorting-by-functions ================================================ Go by Example: Sorting by Functions

Go by Example: Sorting by Functions

Sometimes we’ll want to sort a collection by something other than its natural order. For example, suppose we wanted to sort strings by their length instead of alphabetically. Here’s an example of custom sorts in Go.

package main
import (
    "cmp"
    "fmt"
    "slices"
)
func main() {
    fruits := []string{"peach", "banana", "kiwi"}

We implement a comparison function for string lengths. cmp.Compare is helpful for this.

    lenCmp := func(a, b string) int {
        return cmp.Compare(len(a), len(b))
    }

Now we can call slices.SortFunc with this custom comparison function to sort fruits by name length.

    slices.SortFunc(fruits, lenCmp)
    fmt.Println(fruits)

We can use the same technique to sort a slice of values that aren’t built-in types.

    type Person struct {
        name string
        age  int
    }
    people := []Person{
        Person{name: "Jax", age: 37},
        Person{name: "TJ", age: 25},
        Person{name: "Alex", age: 72},
    }

Sort people by age using slices.SortFunc.

Note: if the Person struct is large, you may want the slice to contain *Person instead and adjust the sorting function accordingly. If in doubt, benchmark!

    slices.SortFunc(people,
        func(a, b Person) int {
            return cmp.Compare(a.age, b.age)
        })
    fmt.Println(people)
}
$ go run sorting-by-functions.go 
[kiwi peach banana]
[{TJ 25} {Jax 37} {Alex 72}]

Next example: .

================================================ FILE: public/spawning-processes ================================================ Go by Example: Spawning Processes

Go by Example: Spawning Processes

Sometimes our Go programs need to spawn other processes.

package main
import (
    "errors"
    "fmt"
    "io"
    "os/exec"
)
func main() {

We’ll start with a simple command that takes no arguments or input and just prints something to stdout. The exec.Command helper creates an object to represent this external process.

    dateCmd := exec.Command("date")

The Output method runs the command, waits for it to finish and collects its standard output. If there were no errors, dateOut will hold bytes with the date info.

    dateOut, err := dateCmd.Output()
    if err != nil {
        panic(err)
    }
    fmt.Println("> date")
    fmt.Println(string(dateOut))

Output and other methods of Command will return *exec.Error if there was a problem executing the command (e.g. wrong path), and *exec.ExitError if the command ran but exited with a non-zero return code.

    _, err = exec.Command("date", "-x").Output()
    if err != nil {
        if e, ok := errors.AsType[*exec.Error](err); ok {
            fmt.Println("failed executing:", e)
        } else if e, ok := errors.AsType[*exec.ExitError](err); ok {
            exitCode := e.ExitCode()
            fmt.Println("command exit rc =", exitCode)
        } else {
            panic(err)
        }
    }

Next we’ll look at a slightly more involved case where we pipe data to the external process on its stdin and collect the results from its stdout.

    grepCmd := exec.Command("grep", "hello")

Here we explicitly grab input/output pipes, start the process, write some input to it, read the resulting output, and finally wait for the process to exit.

    grepIn, _ := grepCmd.StdinPipe()
    grepOut, _ := grepCmd.StdoutPipe()
    grepCmd.Start()
    grepIn.Write([]byte("hello grep\ngoodbye grep"))
    grepIn.Close()
    grepBytes, _ := io.ReadAll(grepOut)
    grepCmd.Wait()

We omitted error checks in the above example, but you could use the usual if err != nil pattern for all of them. We also only collect the StdoutPipe results, but you could collect the StderrPipe in exactly the same way.

    fmt.Println("> grep hello")
    fmt.Println(string(grepBytes))

Note that when spawning commands we need to provide an explicitly delineated command and argument array, vs. being able to just pass in one command-line string. If you want to spawn a full command with a string, you can use bash’s -c option:

    lsCmd := exec.Command("bash", "-c", "ls -a -l -h")
    lsOut, err := lsCmd.Output()
    if err != nil {
        panic(err)
    }
    fmt.Println("> ls -a -l -h")
    fmt.Println(string(lsOut))
}

The spawned programs return output that is the same as if we had run them directly from the command-line.

$ go run spawning-processes.go 
> date
Thu 05 May 2022 10:10:12 PM PDT

date doesn’t have a -x flag so it will exit with an error message and non-zero return code.

command exit rc = 1
> grep hello
hello grep
> ls -a -l -h
drwxr-xr-x  4 mark 136B Oct 3 16:29 .
drwxr-xr-x 91 mark 3.0K Oct 3 12:50 ..
-rw-r--r--  1 mark 1.3K Oct 3 16:28 spawning-processes.go

Next example: .

================================================ FILE: public/stateful-goroutines ================================================ Go by Example: Stateful Goroutines

Go by Example: Stateful Goroutines

In the previous example we used explicit locking with mutexes to synchronize access to shared state across multiple goroutines. Another option is to use the built-in synchronization features of goroutines and channels to achieve the same result. This channel-based approach aligns with Go’s ideas of sharing memory by communicating and having each piece of data owned by exactly one goroutine.

package main
import (
    "fmt"
    "math/rand"
    "sync/atomic"
    "time"
)

In this example our state will be owned by a single goroutine. This will guarantee that the data is never corrupted with concurrent access. In order to read or write that state, other goroutines will send messages to the owning goroutine and receive corresponding replies. These readOp and writeOp structs encapsulate those requests and a way for the owning goroutine to respond.

type readOp struct {
    key  int
    resp chan int
}
type writeOp struct {
    key  int
    val  int
    resp chan bool
}
func main() {

As before we’ll count how many operations we perform.

    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)

Here is the goroutine that owns the state, which is a map as in the previous example but now private to the stateful goroutine. This goroutine repeatedly selects on the reads and writes channels, responding to requests as they arrive. A response is executed by first performing the requested operation and then sending a value on the response channel resp to indicate success (and the desired value in the case of reads).

    go func() {
        var state = make(map[int]int)
        for {
            select {
            case read := <-reads:
                read.resp <- state[read.key]
            case write := <-writes:
                state[write.key] = write.val
                write.resp <- true
            }
        }
    }()

This starts 100 goroutines to issue reads to the state-owning goroutine via the reads channel. Each read requires constructing a readOp, sending it over the reads channel, and then receiving the result over the provided resp channel.

    for range 100 {
        go func() {
            for {
                read := readOp{
                    key:  rand.Intn(5),
                    resp: make(chan int)}
                reads <- read
                <-read.resp
                atomic.AddUint64(&readOps, 1)
                time.Sleep(time.Millisecond)
            }
        }()
    }

We start 10 writes as well, using a similar approach.

    for range 10 {
        go func() {
            for {
                write := writeOp{
                    key:  rand.Intn(5),
                    val:  rand.Intn(100),
                    resp: make(chan bool)}
                writes <- write
                <-write.resp
                atomic.AddUint64(&writeOps, 1)
                time.Sleep(time.Millisecond)
            }
        }()
    }

Let the goroutines work for a second.

    time.Sleep(time.Second)

Finally, capture and report the op counts.

    readOpsFinal := atomic.LoadUint64(&readOps)
    fmt.Println("readOps:", readOpsFinal)
    writeOpsFinal := atomic.LoadUint64(&writeOps)
    fmt.Println("writeOps:", writeOpsFinal)
}

Running our program shows that the goroutine-based state management example completes about 80,000 total operations.

$ go run stateful-goroutines.go
readOps: 71708
writeOps: 7177

For this particular case the goroutine-based approach was a bit more involved than the mutex-based one. It might be useful in certain cases though, for example where you have other channels involved or when managing multiple such mutexes would be error-prone. You should use whichever approach feels most natural, especially with respect to understanding the correctness of your program.

Next example: .

================================================ FILE: public/string-formatting ================================================ Go by Example: String Formatting

Go by Example: String Formatting

Go offers excellent support for string formatting in the printf tradition. Here are some examples of common string formatting tasks.

package main
import (
    "fmt"
    "os"
)
type point struct {
    x, y int
}
func main() {

Go offers several printing “verbs” designed to format general Go values. For example, this prints an instance of our point struct.

    p := point{1, 2}
    fmt.Printf("struct1: %v\n", p)

If the value is a struct, the %+v variant will include the struct’s field names.

    fmt.Printf("struct2: %+v\n", p)

The %#v variant prints a Go syntax representation of the value, i.e. the source code snippet that would produce that value.

    fmt.Printf("struct3: %#v\n", p)

To print the type of a value, use %T.

    fmt.Printf("type: %T\n", p)

Formatting booleans is straight-forward.

    fmt.Printf("bool: %t\n", true)

There are many options for formatting integers. Use %d for standard, base-10 formatting.

    fmt.Printf("int: %d\n", 123)

This prints a binary representation.

    fmt.Printf("bin: %b\n", 14)

This prints the character corresponding to the given integer.

    fmt.Printf("char: %c\n", 33)

%x provides hex encoding.

    fmt.Printf("hex: %x\n", 456)

There are also several formatting options for floats. For basic decimal formatting use %f.

    fmt.Printf("float1: %f\n", 78.9)

%e and %E format the float in (slightly different versions of) scientific notation.

    fmt.Printf("float2: %e\n", 123400000.0)
    fmt.Printf("float3: %E\n", 123400000.0)

For basic string printing use %s.

    fmt.Printf("str1: %s\n", "\"string\"")

To double-quote strings as in Go source, use %q.

    fmt.Printf("str2: %q\n", "\"string\"")

As with integers seen earlier, %x renders the string in base-16, with two output characters per byte of input.

    fmt.Printf("str3: %x\n", "hex this")

To print a representation of a pointer, use %p.

    fmt.Printf("pointer: %p\n", &p)

When formatting numbers you will often want to control the width and precision of the resulting figure. To specify the width of an integer, use a number after the % in the verb. By default the result will be right-justified and padded with spaces.

    fmt.Printf("width1: |%6d|%6d|\n", 12, 345)

You can also specify the width of printed floats, though usually you’ll also want to restrict the decimal precision at the same time with the width.precision syntax.

    fmt.Printf("width2: |%6.2f|%6.2f|\n", 1.2, 3.45)

To left-justify, use the - flag.

    fmt.Printf("width3: |%-6.2f|%-6.2f|\n", 1.2, 3.45)

You may also want to control width when formatting strings, especially to ensure that they align in table-like output. For basic right-justified width.

    fmt.Printf("width4: |%6s|%6s|\n", "foo", "b")

To left-justify use the - flag as with numbers.

    fmt.Printf("width5: |%-6s|%-6s|\n", "foo", "b")

So far we’ve seen Printf, which prints the formatted string to os.Stdout. Sprintf formats and returns a string without printing it anywhere.

    s := fmt.Sprintf("sprintf: a %s", "string")
    fmt.Println(s)

You can format+print to io.Writers other than os.Stdout using Fprintf.

    fmt.Fprintf(os.Stderr, "io: an %s\n", "error")
}
$ go run string-formatting.go
struct1: {1 2}
struct2: {x:1 y:2}
struct3: main.point{x:1, y:2}
type: main.point
bool: true
int: 123
bin: 1110
char: !
hex: 1c8
float1: 78.900000
float2: 1.234000e+08
float3: 1.234000E+08
str1: "string"
str2: "\"string\""
str3: 6865782074686973
pointer: 0xc0000ba000
width1: |    12|   345|
width2: |  1.20|  3.45|
width3: |1.20  |3.45  |
width4: |   foo|     b|
width5: |foo   |b     |
sprintf: a string
io: an error

Next example: .

================================================ FILE: public/string-functions ================================================ Go by Example: String Functions

Go by Example: String Functions

The standard library’s strings package provides many useful string-related functions. Here are some examples to give you a sense of the package.

package main
import (
    "fmt"
    s "strings"
)

We alias fmt.Println to a shorter name as we’ll use it a lot below.

var p = fmt.Println
func main() {

Here’s a sample of the functions available in strings. Since these are functions from the package, not methods on the string object itself, we need to pass the string in question as the first argument to the function. You can find more functions in the strings package docs.

    p("Contains:  ", s.Contains("test", "es"))
    p("Count:     ", s.Count("test", "t"))
    p("HasPrefix: ", s.HasPrefix("test", "te"))
    p("HasSuffix: ", s.HasSuffix("test", "st"))
    p("Index:     ", s.Index("test", "e"))
    p("Join:      ", s.Join([]string{"a", "b"}, "-"))
    p("Repeat:    ", s.Repeat("a", 5))
    p("Replace:   ", s.Replace("foo", "o", "0", -1))
    p("Replace:   ", s.Replace("foo", "o", "0", 1))
    p("Split:     ", s.Split("a-b-c-d-e", "-"))
    p("ToLower:   ", s.ToLower("TEST"))
    p("ToUpper:   ", s.ToUpper("test"))
}
$ go run string-functions.go
Contains:   true
Count:      2
HasPrefix:  true
HasSuffix:  true
Index:      1
Join:       a-b
Repeat:     aaaaa
Replace:    f00
Replace:    f0o
Split:      [a b c d e]
ToLower:    test
ToUpper:    TEST

Next example: .

================================================ FILE: public/strings-and-runes ================================================ Go by Example: Strings and Runes

Go by Example: Strings and Runes

A Go string is a read-only slice of bytes. The language and the standard library treat strings specially - as containers of text encoded in UTF-8. In other languages, strings are made of “characters”. In Go, the concept of a character is called a rune - it’s an integer that represents a Unicode code point. This Go blog post is a good introduction to the topic.

package main
import (
    "fmt"
    "unicode/utf8"
)
func main() {

s is a string assigned a literal value representing the word “hello” in the Thai language. Go string literals are UTF-8 encoded text.

    const s = "สวัสดี"

Since strings are equivalent to []byte, this will produce the length of the raw bytes stored within.

    fmt.Println("Len:", len(s))

Indexing into a string produces the raw byte values at each index. This loop generates the hex values of all the bytes that constitute the code points in s.

    for i := 0; i < len(s); i++ {
        fmt.Printf("%x ", s[i])
    }
    fmt.Println()

To count how many runes are in a string, we can use the utf8 package. Note that the run-time of RuneCountInString depends on the size of the string, because it has to decode each UTF-8 rune sequentially. Some Thai characters are represented by UTF-8 code points that can span multiple bytes, so the result of this count may be surprising.

    fmt.Println("Rune count:", utf8.RuneCountInString(s))

A range loop handles strings specially and decodes each rune along with its offset in the string.

    for idx, runeValue := range s {
        fmt.Printf("%#U starts at %d\n", runeValue, idx)
    }

We can achieve the same iteration by using the utf8.DecodeRuneInString function explicitly.

    fmt.Println("\nUsing DecodeRuneInString")
    for i, w := 0, 0; i < len(s); i += w {
        runeValue, width := utf8.DecodeRuneInString(s[i:])
        fmt.Printf("%#U starts at %d\n", runeValue, i)
        w = width

This demonstrates passing a rune value to a function.

        examineRune(runeValue)
    }
}
func examineRune(r rune) {

Values enclosed in single quotes are rune literals. We can compare a rune value to a rune literal directly.

    if r == 't' {
        fmt.Println("found tee")
    } else if r == 'ส' {
        fmt.Println("found so sua")
    }
}
$ go run strings-and-runes.go
Len: 18
e0 b8 aa e0 b8 a7 e0 b8 b1 e0 b8 aa e0 b8 94 e0 b8 b5 
Rune count: 6
U+0E2A 'ส' starts at 0
U+0E27 'ว' starts at 3
U+0E31 'ั' starts at 6
U+0E2A 'ส' starts at 9
U+0E14 'ด' starts at 12
U+0E35 'ี' starts at 15
Using DecodeRuneInString
U+0E2A 'ส' starts at 0
found so sua
U+0E27 'ว' starts at 3
U+0E31 'ั' starts at 6
U+0E2A 'ส' starts at 9
found so sua
U+0E14 'ด' starts at 12
U+0E35 'ี' starts at 15

Next example: .

================================================ FILE: public/struct-embedding ================================================ Go by Example: Struct Embedding

Go by Example: Struct Embedding

Go supports embedding of structs and interfaces to express a more seamless composition of types. This is not to be confused with //go:embed which is a go directive introduced in Go version 1.16+ to embed files and folders into the application binary.

package main
import "fmt"
type base struct {
    num int
}
func (b base) describe() string {
    return fmt.Sprintf("base with num=%v", b.num)
}

A container embeds a base. An embedding looks like a field without a name.

type container struct {
    base
    str string
}
func main() {

When creating structs with literals, we have to initialize the embedding explicitly; here the embedded type serves as the field name.

    co := container{
        base: base{
            num: 1,
        },
        str: "some name",
    }

We can access the base’s fields directly on co, e.g. co.num.

    fmt.Printf("co={num: %v, str: %v}\n", co.num, co.str)

Alternatively, we can spell out the full path using the embedded type name.

    fmt.Println("also num:", co.base.num)

Since container embeds base, the methods of base also become methods of a container. Here we invoke a method that was embedded from base directly on co.

    fmt.Println("describe:", co.describe())
    type describer interface {
        describe() string
    }

Embedding structs with methods may be used to bestow interface implementations onto other structs. Here we see that a container now implements the describer interface because it embeds base.

    var d describer = co
    fmt.Println("describer:", d.describe())
}
$ go run struct-embedding.go
co={num: 1, str: some name}
also num: 1
describe: base with num=1
describer: base with num=1

Next example: .

================================================ FILE: public/structs ================================================ Go by Example: Structs

Go by Example: Structs

Go’s structs are typed collections of fields. They’re useful for grouping data together to form records.

package main
import "fmt"

This person struct type has name and age fields.

type person struct {
    name string
    age  int
}

newPerson constructs a new person struct with the given name.

func newPerson(name string) *person {

Go is a garbage collected language; you can safely return a pointer to a local variable - it will only be cleaned up by the garbage collector when there are no active references to it.

    p := person{name: name}
    p.age = 42
    return &p
}
func main() {

This syntax creates a new struct.

    fmt.Println(person{"Bob", 20})

You can name the fields when initializing a struct.

    fmt.Println(person{name: "Alice", age: 30})

Omitted fields will be zero-valued.

    fmt.Println(person{name: "Fred"})

An & 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)

You can also use dots with struct pointers - the pointers are automatically dereferenced.

    sp := &s
    fmt.Println(sp.age)

Structs are mutable.

    sp.age = 51
    fmt.Println(sp.age)

If a struct type is only used for a single value, we don’t have to give it a name. The value can have an anonymous struct type. This technique is commonly used for table-driven tests.

    dog := struct {
        name   string
        isGood bool
    }{
        "Rex",
        true,
    }
    fmt.Println(dog)
}
$ go run structs.go
{Bob 20}
{Alice 30}
{Fred 0}
&{Ann 40}
&{Jon 42}
Sean
50
51
{Rex true}

Next example: .

================================================ FILE: public/switch ================================================ Go by Example: Switch

Go by Example: Switch

Switch statements express conditionals across many branches.

package main
import (
    "fmt"
    "time"
)
func main() {

Here’s a basic switch.

    i := 2
    fmt.Print("Write ", i, " as ")
    switch i {
    case 1:
        fmt.Println("one")
    case 2:
        fmt.Println("two")
    case 3:
        fmt.Println("three")
    }

You can use commas to separate multiple expressions in the same case statement. We use the optional default case in this example as well.

    switch time.Now().Weekday() {
    case time.Saturday, time.Sunday:
        fmt.Println("It's the weekend")
    default:
        fmt.Println("It's a weekday")
    }

switch without an expression is an alternate way to express if/else logic. Here we also show how the case expressions can be non-constants.

    t := time.Now()
    switch {
    case t.Hour() < 12:
        fmt.Println("It's before noon")
    default:
        fmt.Println("It's after noon")
    }

A type switch compares types instead of values. You can use this to discover the type of an interface value. In this example, the variable t will have the type corresponding to its clause.

    whatAmI := func(i interface{}) {
        switch t := i.(type) {
        case bool:
            fmt.Println("I'm a bool")
        case int:
            fmt.Println("I'm an int")
        default:
            fmt.Printf("Don't know type %T\n", t)
        }
    }
    whatAmI(true)
    whatAmI(1)
    whatAmI("hey")
}
$ go run switch.go 
Write 2 as two
It's a weekday
It's after noon
I'm a bool
I'm an int
Don't know type string

Next example: .

================================================ FILE: public/tcp-server ================================================ Go by Example: TCP Server

Go by Example: TCP Server

The net package provides the tools we need to easily build TCP socket servers.

package main
import (
    "bufio"
    "fmt"
    "log"
    "net"
    "strings"
)
func main() {

net.Listen starts the server on the given network (TCP) and address (port 8090 on all interfaces).

    listener, err := net.Listen("tcp", ":8090")
    if err != nil {
        log.Fatal("Error listening:", err)
    }

Close the listener to free the port when the application exits.

    defer listener.Close()

Loop indefinitely to accept new client connections.

    for {

Wait for a connection.

        conn, err := listener.Accept()
        if err != nil {
            log.Println("Error accepting conn:", err)
            continue
        }

We use a goroutine here to handle the connection so that the main loop can continue accepting more connections.

        go handleConnection(conn)
    }
}

handleConnection handles a single client connection, reading one line of text from the client and returning a response.

func handleConnection(conn net.Conn) {

Closing the connection releases resources when we are finished interacting with the client.

    defer conn.Close()

Use bufio.NewReader to read one line of data from the client (terminated by a newline).

    reader := bufio.NewReader(conn)
    message, err := reader.ReadString('\n')
    if err != nil {
        log.Printf("Read error: %v", err)
        return
    }

Create and send a response back to the client, demonstrating two-way communication.

    ackMsg := strings.ToUpper(strings.TrimSpace(message))
    response := fmt.Sprintf("ACK: %s\n", ackMsg)
    _, err = conn.Write([]byte(response))
    if err != nil {
        log.Printf("Server write error: %v", err)
    }
}

Run the TCP server in the background.

$ go run tcp-server.go &

Send data and capture the response using netcat.

$ echo "Hello from netcat" | nc localhost 8090
ACK: HELLO FROM NETCAT

Next example: .

================================================ FILE: public/temporary-files-and-directories ================================================ Go by Example: Temporary Files and Directories

Go by Example: Temporary Files and Directories

Throughout program execution, we often want to create data that isn’t needed after the program exits. Temporary files and directories are useful for this purpose since they don’t pollute the file system over time.

package main
import (
    "fmt"
    "os"
    "path/filepath"
)
func check(e error) {
    if e != nil {
        panic(e)
    }
}
func main() {

The easiest way to create a temporary file is by calling os.CreateTemp. It creates a file and opens it for reading and writing. We provide "" as the first argument, so os.CreateTemp will create the file in the default location for our OS.

    f, err := os.CreateTemp("", "sample")
    check(err)

Display the name of the temporary file. On Unix-based OSes the directory will likely be /tmp. The file name starts with the prefix given as the second argument to os.CreateTemp and the rest is chosen automatically to ensure that concurrent calls will always create different file names.

    fmt.Println("Temp file name:", f.Name())

Clean up the file after we’re done. The OS is likely to clean up temporary files by itself after some time, but it’s good practice to do this explicitly.

    defer os.Remove(f.Name())

We can write some data to the file.

    _, err = f.Write([]byte{1, 2, 3, 4})
    check(err)

If we intend to write many temporary files, we may prefer to create a temporary directory. os.MkdirTemp’s arguments are the same as CreateTemp’s, but it returns a directory name rather than an open file.

    dname, err := os.MkdirTemp("", "sampledir")
    check(err)
    fmt.Println("Temp dir name:", dname)
    defer os.RemoveAll(dname)

Now we can synthesize temporary file names by prefixing them with our temporary directory.

    fname := filepath.Join(dname, "file1")
    err = os.WriteFile(fname, []byte{1, 2}, 0666)
    check(err)
}
$ go run temporary-files-and-directories.go
Temp file name: /tmp/sample610887201
Temp dir name: /tmp/sampledir898854668

Next example: .

================================================ FILE: public/testing-and-benchmarking ================================================ Go by Example: Testing and Benchmarking

Go by Example: Testing and Benchmarking

Unit testing is an important part of writing principled Go programs. The testing package provides the tools we need to write unit tests and the go test command runs tests.

For the sake of demonstration, this code is in package main, but it could be any package. Testing code typically lives in the same package as the code it tests.

package main
import (
    "fmt"
    "testing"
)

We’ll be testing this simple implementation of an integer minimum. Typically, the code we’re testing would be in a source file named something like intutils.go, and the test file for it would then be named intutils_test.go.

func IntMin(a, b int) int {
    if a < b {
        return a
    }
    return b
}

A test is created by writing a function with a name beginning with Test.

func TestIntMinBasic(t *testing.T) {
    ans := IntMin(2, -2)
    if ans != -2 {

t.Error* will report test failures but continue executing the test. t.Fatal* will report test failures and stop the test immediately.

        t.Errorf("IntMin(2, -2) = %d; want -2", ans)
    }
}

Writing tests can be repetitive, so it’s idiomatic to use a table-driven style, where test inputs and expected outputs are listed in a table and a single loop walks over them and performs the test logic.

func TestIntMinTableDriven(t *testing.T) {
    var tests = []struct {
        a, b int
        want int
    }{
        {0, 1, 0},
        {1, 0, 0},
        {2, -2, -2},
        {0, -1, -1},
        {-1, 0, -1},
    }

t.Run enables running “subtests”, one for each table entry. These are shown separately when executing go test -v.

    for _, tt := range tests {
        testname := fmt.Sprintf("%d,%d", tt.a, tt.b)
        t.Run(testname, func(t *testing.T) {
            ans := IntMin(tt.a, tt.b)
            if ans != tt.want {
                t.Errorf("got %d, want %d", ans, tt.want)
            }
        })
    }
}

Benchmark tests typically go in _test.go files and are named beginning with Benchmark. Any code that’s required for the benchmark to run but should not be measured goes before this loop.

func BenchmarkIntMin(b *testing.B) {
    for b.Loop() {

The benchmark runner will automatically execute this loop body many times to determine a reasonable estimate of the run-time of a single iteration.

        IntMin(1, 2)
    }
}

Run all tests in the current project in verbose mode.

$ go test -v
== RUN   TestIntMinBasic
--- PASS: TestIntMinBasic (0.00s)
=== RUN   TestIntMinTableDriven
=== RUN   TestIntMinTableDriven/0,1
=== RUN   TestIntMinTableDriven/1,0
=== RUN   TestIntMinTableDriven/2,-2
=== RUN   TestIntMinTableDriven/0,-1
=== RUN   TestIntMinTableDriven/-1,0
--- PASS: TestIntMinTableDriven (0.00s)
    --- PASS: TestIntMinTableDriven/0,1 (0.00s)
    --- PASS: TestIntMinTableDriven/1,0 (0.00s)
    --- PASS: TestIntMinTableDriven/2,-2 (0.00s)
    --- PASS: TestIntMinTableDriven/0,-1 (0.00s)
    --- PASS: TestIntMinTableDriven/-1,0 (0.00s)
PASS
ok      examples/testing-and-benchmarking    0.023s

Run all benchmarks in the current project. All tests are run prior to benchmarks. The bench flag filters benchmark function names with a regexp.

$ go test -bench=.
goos: darwin
goarch: arm64
pkg: examples/testing
BenchmarkIntMin-8 1000000000 0.3136 ns/op
PASS
ok      examples/testing-and-benchmarking    0.351s

Next example: .

================================================ FILE: public/text-templates ================================================ Go by Example: Text Templates

Go by Example: Text Templates

Go offers built-in support for creating dynamic content or showing customized output to the user with the text/template package. A sibling package named html/template provides the same API but has additional security features and should be used for generating HTML.

package main
import (
    "os"
    "text/template"
)
func main() {

We can create a new template and parse its body from a string. Templates are a mix of static text and “actions” enclosed in {{...}} that are used to dynamically insert content.

    t1 := template.New("t1")
    t1, err := t1.Parse("Value is {{.}}\n")
    if err != nil {
        panic(err)
    }

Alternatively, we can use the template.Must function to panic in case Parse returns an error. This is especially useful for templates initialized in the global scope.

    t1 = template.Must(t1.Parse("Value: {{.}}\n"))

By “executing” the template we generate its text with specific values for its actions. The {{.}} action is replaced by the value passed as a parameter to Execute.

    t1.Execute(os.Stdout, "some text")
    t1.Execute(os.Stdout, 5)
    t1.Execute(os.Stdout, []string{
        "Go",
        "Rust",
        "C++",
        "C#",
    })

Helper function we’ll use below.

    Create := func(name, t string) *template.Template {
        return template.Must(template.New(name).Parse(t))
    }

If the data is a struct we can use the {{.FieldName}} action to access its fields. The fields should be exported to be accessible when a template is executing.

    t2 := Create("t2", "Name: {{.Name}}\n")
    t2.Execute(os.Stdout, struct {
        Name string
    }{"Jane Doe"})

The same applies to maps; with maps there is no restriction on the case of key names.

    t2.Execute(os.Stdout, map[string]string{
        "Name": "Mickey Mouse",
    })

if/else provide conditional execution for templates. A value is considered false if it’s the default value of a type, such as 0, an empty string, nil pointer, etc. This sample demonstrates another feature of templates: using - in actions to trim whitespace.

    t3 := Create("t3",
        "{{if . -}} yes {{else -}} no {{end}}\n")
    t3.Execute(os.Stdout, "not empty")
    t3.Execute(os.Stdout, "")

range blocks let us loop through slices, arrays, maps or channels. Inside the range block {{.}} is set to the current item of the iteration.

    t4 := Create("t4",
        "Range: {{range .}}{{.}} {{end}}\n")
    t4.Execute(os.Stdout,
        []string{
            "Go",
            "Rust",
            "C++",
            "C#",
        })
}
$ go run templates.go 
Value: some text
Value: 5
Value: [Go Rust C++ C#]
Name: Jane Doe
Name: Mickey Mouse
yes 
no 
Range: Go Rust C++ C# 

Next example: .

================================================ FILE: public/tickers ================================================ Go by Example: Tickers

Go by Example: Tickers

Timers are for when you want to do something once in the future - tickers are for when you want to do something repeatedly at regular intervals. Here’s an example of a ticker that ticks periodically until we stop it.

package main
import (
    "fmt"
    "time"
)
func main() {

Tickers use a similar mechanism to timers: a channel that is sent values. Here we’ll use the select builtin on the channel to await the values as they arrive every 500ms.

    ticker := time.NewTicker(500 * time.Millisecond)
    done := make(chan bool)
    go func() {
        for {
            select {
            case <-done:
                return
            case t := <-ticker.C:
                fmt.Println("Tick at", t)
            }
        }
    }()

Tickers can be stopped like timers. Once a ticker is stopped it won’t receive any more values on its channel. We’ll stop ours after 1600ms.

    time.Sleep(1600 * time.Millisecond)
    ticker.Stop()
    done <- true
    fmt.Println("Ticker stopped")
}

When we run this program the ticker should tick 3 times before we stop it.

$ go run tickers.go
Tick at 2012-09-23 11:29:56.487625 -0700 PDT
Tick at 2012-09-23 11:29:56.988063 -0700 PDT
Tick at 2012-09-23 11:29:57.488076 -0700 PDT
Ticker stopped

Next example: .

================================================ FILE: public/time ================================================ Go by Example: Time

Go by Example: Time

Go offers extensive support for times and durations; here are some examples.

package main
import (
    "fmt"
    "time"
)
func main() {
    p := fmt.Println

We’ll start by getting the current time.

    now := time.Now()
    p(now)

You can build a time struct by providing the year, month, day, etc. Times are always associated with a Location, i.e. time zone.

    then := time.Date(
        2009, 11, 17, 20, 34, 58, 651387237, time.UTC)
    p(then)

You can extract the various components of the time value as expected.

    p(then.Year())
    p(then.Month())
    p(then.Day())
    p(then.Hour())
    p(then.Minute())
    p(then.Second())
    p(then.Nanosecond())
    p(then.Location())

The Monday-Sunday Weekday is also available.

    p(then.Weekday())

These methods compare two times, testing if the first occurs before, after, or at the same time as the second, respectively.

    p(then.Before(now))
    p(then.After(now))
    p(then.Equal(now))

The Sub methods returns a Duration representing the interval between two times.

    diff := now.Sub(then)
    p(diff)

We can compute the length of the duration in various units.

    p(diff.Hours())
    p(diff.Minutes())
    p(diff.Seconds())
    p(diff.Nanoseconds())

You can use Add to advance a time by a given duration, or with a - to move backwards by a duration.

    p(then.Add(diff))
    p(then.Add(-diff))
}
$ go run time.go
2012-10-31 15:50:13.793654 +0000 UTC
2009-11-17 20:34:58.651387237 +0000 UTC
2009
November
17
20
34
58
651387237
UTC
Tuesday
true
false
false
25891h15m15.142266763s
25891.25420618521
1.5534752523711128e+06
9.320851514226677e+07
93208515142266763
2012-10-31 15:50:13.793654 +0000 UTC
2006-12-05 01:19:43.509120474 +0000 UTC

Next we’ll look at the related idea of time relative to the Unix epoch.

Next example: .

================================================ FILE: public/time-formatting-parsing ================================================ Go by Example: Time Formatting / Parsing

Go by Example: Time Formatting / Parsing

Go supports time formatting and parsing via pattern-based layouts.

package main
import (
    "fmt"
    "time"
)
func main() {
    p := fmt.Println

Here’s a basic example of formatting a time according to RFC3339, using the corresponding layout constant.

    t := time.Now()
    p(t.Format(time.RFC3339))

Time parsing uses the same layout values as Format.

    t1, _ := time.Parse(time.RFC3339, "2012-11-01T22:08:41+00:00")
    p(t1)

Format and Parse use example-based layouts. Usually you’ll use a constant from time for these layouts, but you can also supply custom layouts. Layouts must use the reference time Mon Jan 2 15:04:05 MST 2006 to show the pattern with which to format/parse a given time/string. The example time must be exactly as shown: the year 2006, 15 for the hour, Monday for the day of the week, etc.

    p(t.Format("3:04PM"))
    p(t.Format("Mon Jan _2 15:04:05 2006"))
    p(t.Format("2006-01-02T15:04:05.999999-07:00"))
    form := "3 04 PM"
    t2, _ := time.Parse(form, "8 41 PM")
    p(t2)

For purely numeric representations you can also use standard string formatting with the extracted components of the time value.

    fmt.Printf("%d-%02d-%02dT%02d:%02d:%02d-00:00\n",
        t.Year(), t.Month(), t.Day(),
        t.Hour(), t.Minute(), t.Second())

Parse will return an error on malformed input explaining the parsing problem.

    _, err := time.Parse("Mon Jan _2 15:04:05 2006", "8:41PM")
    p(err)
}
$ go run time-formatting-parsing.go 
2014-04-15T18:00:15-07:00
2012-11-01 22:08:41 +0000 +0000
6:00PM
Tue Apr 15 18:00:15 2014
2014-04-15T18:00:15.161182-07:00
0000-01-01 20:41:00 +0000 UTC
2014-04-15T18:00:15-00:00
parsing time "8:41PM" as "Mon Jan _2 15:04:05 2006": ...

Next example: .

================================================ FILE: public/timeouts ================================================ Go by Example: Timeouts

Go by Example: Timeouts

Timeouts are important for programs that connect to external resources or that otherwise need to bound execution time. Implementing timeouts in Go is easy and elegant thanks to channels and select.

package main
import (
    "fmt"
    "time"
)
func main() {

For our example, suppose we’re executing an external call that returns its result on a channel c1 after 2s. Note that the channel is buffered, so the send in the goroutine is nonblocking. This is a common pattern to prevent goroutine leaks in case the channel is never read.

    c1 := make(chan string, 1)
    go func() {
        time.Sleep(2 * time.Second)
        c1 <- "result 1"
    }()

Here’s the select implementing a timeout. res := <-c1 awaits the result and <-time.After awaits a value to be sent after the timeout of 1s. Since select proceeds with the first receive that’s ready, we’ll take the timeout case if the operation takes more than the allowed 1s.

    select {
    case res := <-c1:
        fmt.Println(res)
    case <-time.After(1 * time.Second):
        fmt.Println("timeout 1")
    }

If we allow a longer timeout of 3s, then the receive from c2 will succeed and we’ll print the result.

    c2 := make(chan string, 1)
    go func() {
        time.Sleep(2 * time.Second)
        c2 <- "result 2"
    }()
    select {
    case res := <-c2:
        fmt.Println(res)
    case <-time.After(3 * time.Second):
        fmt.Println("timeout 2")
    }
}

Running this program shows the first operation timing out and the second succeeding.

$ go run timeouts.go 
timeout 1
result 2

Next example: .

================================================ FILE: public/timers ================================================ Go by Example: Timers

Go by Example: Timers

We often want to execute Go code at some point in the future, or repeatedly at some interval. Go’s built-in timer and ticker features make both of these tasks easy. We’ll look first at timers and then at tickers.

package main
import (
    "fmt"
    "time"
)
func main() {

Timers represent a single event in the future. You tell the timer how long you want to wait, and it provides a channel that will be notified at that time. This timer will wait 2 seconds.

    timer1 := time.NewTimer(2 * time.Second)

The <-timer1.C blocks on the timer’s channel C until it sends a value indicating that the timer fired.

    <-timer1.C
    fmt.Println("Timer 1 fired")

If you just wanted to wait, you could have used time.Sleep. One reason a timer may be useful is that you can cancel the timer before it fires. Here’s an example of that.

    timer2 := time.NewTimer(time.Second)
    go func() {
        <-timer2.C
        fmt.Println("Timer 2 fired")
    }()
    stop2 := timer2.Stop()
    if stop2 {
        fmt.Println("Timer 2 stopped")
    }

Give the timer2 enough time to fire, if it ever was going to, to show it is in fact stopped.

    time.Sleep(2 * time.Second)
}

The first timer will fire ~2s after we start the program, but the second should be stopped before it has a chance to fire.

$ go run timers.go
Timer 1 fired
Timer 2 stopped

Next example: .

================================================ FILE: public/url-parsing ================================================ Go by Example: URL Parsing

Go by Example: URL Parsing

URLs provide a uniform way to locate resources. Here’s how to parse URLs in Go.

package main
import (
    "fmt"
    "net"
    "net/url"
)
func main() {

We’ll parse this example URL, which includes a scheme, authentication info, host, port, path, query params, and query fragment.

    s := "postgres://user:pass@host.com:5432/path?k=v#f"

Parse the URL and ensure there are no errors.

    u, err := url.Parse(s)
    if err != nil {
        panic(err)
    }

Accessing the scheme is straightforward.

    fmt.Println(u.Scheme)

User contains all authentication info; call Username and Password on this for individual values.

    fmt.Println(u.User)
    fmt.Println(u.User.Username())
    p, _ := u.User.Password()
    fmt.Println(p)

The Host contains both the hostname and the port, if present. Use SplitHostPort to extract them.

    fmt.Println(u.Host)
    host, port, _ := net.SplitHostPort(u.Host)
    fmt.Println(host)
    fmt.Println(port)

Here we extract the path and the fragment after the #.

    fmt.Println(u.Path)
    fmt.Println(u.Fragment)

To get query params in a string of k=v format, use RawQuery. You can also parse query params into a map. The parsed query param maps are from strings to slices of strings, so index into [0] if you only want the first value.

    fmt.Println(u.RawQuery)
    m, _ := url.ParseQuery(u.RawQuery)
    fmt.Println(m)
    fmt.Println(m["k"][0])
}

Running our URL parsing program shows all the different pieces that we extracted.

$ go run url-parsing.go 
postgres
user:pass
user
pass
host.com:5432
host.com
5432
/path
f
k=v
map[k:[v]]
v

Next example: .

================================================ FILE: public/values ================================================ Go by Example: Values

Go by Example: Values

Go has various value types including strings, integers, floats, booleans, etc. Here are a few basic examples.

package main
import "fmt"
func main() {

Strings, which can be added together with +.

    fmt.Println("go" + "lang")

Integers and floats.

    fmt.Println("1+1 =", 1+1)
    fmt.Println("7.0/3.0 =", 7.0/3.0)

Booleans, with boolean operators as you’d expect.

    fmt.Println(true && false)
    fmt.Println(true || false)
    fmt.Println(!true)
}
$ go run values.go
golang
1+1 = 2
7.0/3.0 = 2.3333333333333335
false
true
false

Next example: .

================================================ FILE: public/variables ================================================ Go by Example: Variables

Go by Example: Variables

In Go, variables are explicitly declared and used by the compiler to e.g. check type-correctness of function calls.

package main
import "fmt"
func main() {

var declares 1 or more variables.

    var a = "initial"
    fmt.Println(a)

You can declare multiple variables at once.

    var b, c int = 1, 2
    fmt.Println(b, c)

Go will infer the type of initialized variables.

    var d = true
    fmt.Println(d)

Variables declared without a corresponding initialization are zero-valued. For example, the zero value for an int is 0.

    var e int
    fmt.Println(e)

The := syntax is shorthand for declaring and initializing a variable, e.g. for var f string = "apple" in this case. This syntax is only available inside functions.

    f := "apple"
    fmt.Println(f)
}
$ go run variables.go
initial
1 2
true
0
apple

Next example: .

================================================ FILE: public/variadic-functions ================================================ Go by Example: Variadic Functions

Go by Example: Variadic Functions

Variadic functions can be called with any number of trailing arguments. For example, fmt.Println is a common variadic function.

package main
import "fmt"

Here’s a function that will take an arbitrary number of ints as arguments.

func sum(nums ...int) {
    fmt.Print(nums, " ")
    total := 0

Within the function, the type of nums is equivalent to []int. We can call len(nums), iterate over it with range, etc.

    for _, num := range nums {
        total += num
    }
    fmt.Println(total)
}
func main() {

Variadic functions can be called in the usual way with individual arguments.

    sum(1, 2)
    sum(1, 2, 3)

If you already have multiple args in a slice, apply them to a variadic function using func(slice...) like this.

    nums := []int{1, 2, 3, 4}
    sum(nums...)
}
$ go run variadic-functions.go 
[1 2] 3
[1 2 3] 6
[1 2 3 4] 10

Another key aspect of functions in Go is their ability to form closures, which we’ll look at next.

Next example: .

================================================ FILE: public/waitgroups ================================================ Go by Example: WaitGroups

Go by Example: WaitGroups

To wait for multiple goroutines to finish, we can use a wait group.

package main
import (
    "fmt"
    "sync"
    "time"
)

This is the function we’ll run in every goroutine.

func worker(id int) {
    fmt.Printf("Worker %d starting\n", id)

Sleep to simulate an expensive task.

    time.Sleep(time.Second)
    fmt.Printf("Worker %d done\n", id)
}
func main() {

This WaitGroup is used to wait for all the goroutines launched here to finish. Note: if a WaitGroup is explicitly passed into functions, it should be done by pointer.

    var wg sync.WaitGroup

Launch several goroutines using WaitGroup.Go

    for i := 1; i <= 5; i++ {
        wg.Go(func() {
            worker(i)
        })
    }

Block until all the goroutines started by wg are done. A goroutine is done when the function it invokes returns.

    wg.Wait()

Note that this approach has no straightforward way to propagate errors from workers. For more advanced use cases, consider using the errgroup package.

}
$ go run waitgroups.go
Worker 5 starting
Worker 3 starting
Worker 4 starting
Worker 1 starting
Worker 2 starting
Worker 4 done
Worker 1 done
Worker 2 done
Worker 5 done
Worker 3 done

The order of workers starting up and finishing is likely to be different for each invocation.

Next example: .

================================================ FILE: public/worker-pools ================================================ Go by Example: Worker Pools

Go by Example: Worker Pools

In this example we’ll look at how to implement a worker pool using goroutines and channels.

package main
import (
    "fmt"
    "time"
)

Here’s the worker, of which we’ll run several concurrent instances. These workers will receive work on the jobs channel and send the corresponding results on results. We’ll sleep a second per job to simulate an expensive task.

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        fmt.Println("worker", id, "started  job", j)
        time.Sleep(time.Second)
        fmt.Println("worker", id, "finished job", j)
        results <- j * 2
    }
}
func main() {

In order to use our pool of workers we need to send them work and collect their results. We make 2 channels for this.

    const numJobs = 5
    jobs := make(chan int, numJobs)
    results := make(chan int, numJobs)

This starts up 3 workers, initially blocked because there are no jobs yet.

    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }

Here we send 5 jobs and then close that channel to indicate that’s all the work we have.

    for j := 1; j <= numJobs; j++ {
        jobs <- j
    }
    close(jobs)

Finally we collect all the results of the work. This also ensures that the worker goroutines have finished. An alternative way to wait for multiple goroutines is to use a WaitGroup.

    for a := 1; a <= numJobs; a++ {
        <-results
    }
}

Our running program shows the 5 jobs being executed by various workers. The program only takes about 2 seconds despite doing about 5 seconds of total work because there are 3 workers operating concurrently.

$ time go run worker-pools.go 
worker 1 started  job 1
worker 2 started  job 2
worker 3 started  job 3
worker 1 finished job 1
worker 1 started  job 4
worker 2 finished job 2
worker 2 started  job 5
worker 3 finished job 3
worker 1 finished job 4
worker 2 finished job 5
real    0m2.358s

Next example: .

================================================ FILE: public/writing-files ================================================ Go by Example: Writing Files

Go by Example: Writing Files

Writing files in Go follows similar patterns to the ones we saw earlier for reading.

package main
import (
    "bufio"
    "fmt"
    "os"
    "path/filepath"
)
func check(e error) {
    if e != nil {
        panic(e)
    }
}
func main() {

To start, here’s how to dump a string (or just bytes) into a file.

    d1 := []byte("hello\ngo\n")
    path1 := filepath.Join(os.TempDir(), "dat1")
    err := os.WriteFile(path1, d1, 0644)
    check(err)

For more granular writes, open a file for writing.

    path2 := filepath.Join(os.TempDir(), "dat2")
    f, err := os.Create(path2)
    check(err)

It’s idiomatic to defer a Close immediately after opening a file.

    defer f.Close()

You can Write byte slices as you’d expect.

    d2 := []byte{115, 111, 109, 101, 10}
    n2, err := f.Write(d2)
    check(err)
    fmt.Printf("wrote %d bytes\n", n2)

A WriteString is also available.

    n3, err := f.WriteString("writes\n")
    check(err)
    fmt.Printf("wrote %d bytes\n", n3)

Issue a Sync to flush writes to stable storage.

    f.Sync()

bufio provides buffered writers in addition to the buffered readers we saw earlier.

    w := bufio.NewWriter(f)
    n4, err := w.WriteString("buffered\n")
    check(err)
    fmt.Printf("wrote %d bytes\n", n4)

Use Flush to ensure all buffered operations have been applied to the underlying writer.

    w.Flush()
}

Try running the file-writing code.

$ go run writing-files.go 
wrote 5 bytes
wrote 7 bytes
wrote 9 bytes

Then check the contents of the written files.

$ cat /tmp/dat1
hello
go
$ cat /tmp/dat2
some
writes
buffered

Next we’ll look at applying some of the file I/O ideas we’ve just seen to the stdin and stdout streams.

Next example: .

================================================ FILE: public/xml ================================================ Go by Example: XML

Go by Example: XML

Go offers built-in support for XML and XML-like formats with the encoding/xml package.

package main
import (
    "encoding/xml"
    "fmt"
)

Plant will be mapped to XML. Similarly to the JSON examples, field tags contain directives for the encoder and decoder. Here we use some special features of the XML package: the XMLName field name dictates the name of the XML element representing this struct; id,attr means that the Id field is an XML attribute rather than a nested element.

type Plant struct {
    XMLName xml.Name `xml:"plant"`
    Id      int      `xml:"id,attr"`
    Name    string   `xml:"name"`
    Origin  []string `xml:"origin"`
}
func (p Plant) String() string {
    return fmt.Sprintf("Plant id=%v, name=%v, origin=%v",
        p.Id, p.Name, p.Origin)
}
func main() {
    coffee := &Plant{Id: 27, Name: "Coffee"}
    coffee.Origin = []string{"Ethiopia", "Brazil"}

Emit XML representing our plant; using MarshalIndent to produce a more human-readable output.

    out, _ := xml.MarshalIndent(coffee, " ", "  ")
    fmt.Println(string(out))

To add a generic XML header to the output, append it explicitly.

    fmt.Println(xml.Header + string(out))

Use Unmarshal to parse a stream of bytes with XML into a data structure. If the XML is malformed or cannot be mapped onto Plant, a descriptive error will be returned.

    var p Plant
    if err := xml.Unmarshal(out, &p); err != nil {
        panic(err)
    }
    fmt.Println(p)
    tomato := &Plant{Id: 81, Name: "Tomato"}
    tomato.Origin = []string{"Mexico", "California"}

The parent>child>plant field tag tells the encoder to nest all plants under <parent><child>...

    type Nesting struct {
        XMLName xml.Name `xml:"nesting"`
        Plants  []*Plant `xml:"parent>child>plant"`
    }
    nesting := &Nesting{}
    nesting.Plants = []*Plant{coffee, tomato}
    out, _ = xml.MarshalIndent(nesting, " ", "  ")
    fmt.Println(string(out))
}
$ go run xml.go
 <plant id="27">
   <name>Coffee</name>
   <origin>Ethiopia</origin>
   <origin>Brazil</origin>
 </plant>
<?xml version="1.0" encoding="UTF-8"?>
 <plant id="27">
   <name>Coffee</name>
   <origin>Ethiopia</origin>
   <origin>Brazil</origin>
 </plant>
Plant id=27, name=Coffee, origin=[Ethiopia Brazil]
 <nesting>
   <parent>
     <child>
       <plant id="27">
         <name>Coffee</name>
         <origin>Ethiopia</origin>
         <origin>Brazil</origin>
       </plant>
       <plant id="81">
         <name>Tomato</name>
         <origin>Mexico</origin>
         <origin>California</origin>
       </plant>
     </child>
   </parent>
 </nesting>

Next example: .

================================================ FILE: templates/404.tmpl ================================================ Go by Example: Not Found

Go by Example

Sorry, we couldn't find that! Check out the home page?

{{ template "footer" }}
================================================ FILE: templates/example.tmpl ================================================ Go by Example: {{.Name}}

Go by Example: {{.Name}}

{{range .Segs}} {{range .}} {{end}}
{{.DocsRendered}} {{if .CodeRun}}{{end}} {{.CodeRendered}}
{{end}} {{if .NextExample}}

Next example: .

{{end}} {{ template "footer" }}
================================================ FILE: templates/footer.tmpl ================================================ {{define "footer"}} {{end}} ================================================ FILE: templates/index.tmpl ================================================ Go by Example

Go by Example

Go is an open source programming language designed for building scalable, secure and reliable software. Please read the official documentation to learn more.

Go by Example is a hands-on introduction to Go using annotated example programs. Check out the first example or browse the full list below.

Unless stated otherwise, examples here assume the latest major release Go and may use new language features. Try to upgrade to the latest version if something isn't working.

{{ template "footer" }}
================================================ FILE: templates/site.css ================================================ /* CSS reset: https://meyerweb.com/eric/tools/css/reset/ */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; } article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } body { line-height: 1; } ol, ul { list-style: none; } blockquote, q { quotes: none; } blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } table { border-collapse: collapse; border-spacing: 0; } /* Layout and typography */ body { font-family: 'Georgia', serif; font-size: 16px; line-height: 20px; } em { font-style: italic; } h2 { font-size: 32px; line-height: 40px; margin-top: 40px; } h2 a { text-decoration: none; } div.example { width: 995px; max-width: calc(100% - 24px); margin-left: auto; margin-right: auto; margin-bottom: 120px; } div.example table { margin-top: 15px; margin-bottom: 20px; } p.next { margin-bottom: 20px; } p.footer { font-size: 75%; } div#intro { width: 420px; min-width: 420px; max-width: 420px; margin-left: auto; margin-right: auto; margin-bottom: 120px; } div#intro p { padding-top: 20px; } div#intro ul { padding-top: 20px; } table td { border: 0; outline: 0; } td.docs { width: 420px; max-width: 420px; min-width: 420px; min-height: 5px; vertical-align: top; text-align: left; } td.docs p { padding-right: 5px; padding-top: 5px; padding-bottom: 15px; } td.code { width: 575px; max-width: 575px; min-width: 575px; padding-top: 5px; padding-right: 5px; padding-left: 5px; padding-bottom: 5px; vertical-align: top; } td.code.leading { padding-bottom: 11px; } pre, code { font-size: 14px; line-height: 18px; font-family: 'Menlo', 'Monaco', 'Consolas', 'Lucida Console', monospace; } img.copy, img.run { height: 16px; width: 16px; float: right } img.copy, img.run { cursor: pointer; } img.copy { margin-right: 4px; } /* Colors: light mode */ body { background-color: #ffffff; color: #252519; } td.code.empty { background: #ffffff; } a, a:visited { color: #261a3b; } p.footer { color: #808080; } p.footer a, p.footer a:visited { color: #808080; } td.code { background: #f0f0f0; } /* Syntax highlighting: light mode */ body .nx { } /* Name.Other: package, variable, struct, param, generic type names, etc. */ body .nf { } /* Name.Function: function names (def and call) */ body .o { } /* Operator: :=, &, *, +, &&, <, etc. */ body .p { } /* Plain: = , . : [ ( { etc. */ body .k { color: #954121 } /* Keyword: if, for, range, return, defer, etc. */ body .kc { color: #954121 } /* Keyword.Constant: nil, true, false */ body .kd { color: #954121 } /* Keyword.Declaration: func, var, type, struct, map, chan, etc. */ body .kn { color: #954121 } /* Keyword.Namespace: package, import */ body .nb { color: #954121 } /* Name.Builtin: make, len, delete, append, etc. */ body .kt { color: #b00040 } /* Keyword.Type: string, int, byte, error, etc. */ body .m { color: #666666 } /* Literal.Number */ body .mf { color: #666666 } /* Literal.Number.Float */ body .mh { color: #666666 } /* Literal.Number.Hex */ body .mi { color: #666666 } /* Literal.Number.Integer */ body .mo { color: #666666 } /* Literal.Number.Oct */ body .s { color: #219161 } /* Literal.String */ body .sc { color: #219161 } /* Literal.String.Char */ body .gp { color: #000080 } /* Generic.Prompt: shell prompt */ body .go { color: #808080 } /* Generic.Output: shell output */ body .c1 { color: #808080 } /* Comment.Single */ @media (prefers-color-scheme: dark) { /* Colors: dark mode */ body { background-color: #1f1f1f; color: #dadada; } td.code.empty { background: #1f1f1f; } a, a:visited { color: #e4e4e4; } p.footer { color: #898e98; } p.footer a, p.footer a:visited { color: #898e98; } td.code { background: #282828; } /* Syntax highlighting: dark mode */ body .nx { } /* Name.Other */ body .nf { } /* Name.Function */ body .o { } /* Operator */ body .p { } /* Plain */ body .k { color: #af5a54 } /* Keyword */ body .kc { color: #af5a54 } /* Keyword.Constant */ body .kd { color: #af5a54 } /* Keyword.Declaration */ body .kn { color: #af5a54 } /* Keyword.Namespace */ body .nb { color: #af5a54 } /* Name.Builtin */ body .kt { color: #b64343 } /* Keyword.Type */ body .m { color: #688ec8 } /* Literal.Number */ body .mf { color: #688ec8 } /* Literal.Number.Float */ body .mh { color: #688ec8 } /* Literal.Number.Hex */ body .mi { color: #688ec8 } /* Literal.Number.Integer */ body .mo { color: #688ec8 } /* Literal.Number.Oct */ body .s { color: #718e72 } /* Literal.String */ body .sc { color: #718e72 } /* Literal.String.Char */ body .gp { color: #8a6ab1 } /* Generic.Prompt */ body .go { color: #868686 } /* Generic.Output */ body .c1 { color: #868686 } /* Comment.Single */ } ================================================ FILE: templates/site.js ================================================ /*! * clipboard.js v1.5.13 * https://zenorocha.github.io/clipboard.js * * Licensed MIT © Zeno Rocha */ !function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.Clipboard=t()}}(function(){var t,e,n;return function t(e,n,o){function r(c,a){if(!n[c]){if(!e[c]){var l="function"==typeof require&&require;if(!a&&l)return l(c,!0);if(i)return i(c,!0);var s=new Error("Cannot find module '"+c+"'");throw s.code="MODULE_NOT_FOUND",s}var u=n[c]={exports:{}};e[c][0].call(u.exports,function(t){var n=e[c][1][t];return r(n?n:t)},u,u.exports,t,e,n,o)}return n[c].exports}for(var i="function"==typeof require&&require,c=0;c 0 } func check(err error) { if err != nil { panic(err) } } func isDir(path string) bool { fileStat, _ := os.Stat(path) return fileStat.IsDir() } func ensureDir(dir string) { err := os.MkdirAll(dir, 0755) check(err) } func copyFile(src, dst string) { dat, err := os.ReadFile(src) check(err) err = os.WriteFile(dst, dat, 0644) check(err) } func sha1Sum(s string) string { h := sha1.New() h.Write([]byte(s)) b := h.Sum(nil) return fmt.Sprintf("%x", b) } func mustReadFile(path string) string { bytes, err := os.ReadFile(path) check(err) return string(bytes) } func markdown(src string) string { return string(blackfriday.Run([]byte(src))) } func readLines(path string) []string { src := mustReadFile(path) return strings.Split(src, "\n") } func mustGlob(glob string) []string { paths, err := filepath.Glob(glob) check(err) return paths } func whichLexer(path string) string { if strings.HasSuffix(path, ".go") { return "go" } else if strings.HasSuffix(path, ".sh") { return "console" } panic("No lexer for " + path) } func debug(msg string) { if os.Getenv("DEBUG") == "1" { fmt.Fprintln(os.Stderr, msg) } } var docsPat = regexp.MustCompile(`^(\s*(\/\/|#)\s|\s*\/\/$)`) var dashPat = regexp.MustCompile(`\-+`) // Seg is a segment of an example type Seg struct { Docs, DocsRendered string Code, CodeRendered, CodeForJs string CodeEmpty, CodeLeading, CodeRun bool } // Example is info extracted from an example file type Example struct { ID, Name string GoCode, GoCodeHash, URLHash string Segs [][]*Seg PrevExample *Example NextExample *Example } func parseHashFile(sourcePath string) (string, string) { lines := readLines(sourcePath) return lines[0], lines[1] } func resetURLHashFile(codehash, code, sourcePath string) string { if verbose() { fmt.Println(" Sending request to play.golang.org") } payload := strings.NewReader(code) resp, err := http.Post("https://play.golang.org/share", "text/plain", payload) check(err) defer resp.Body.Close() body, err := io.ReadAll(resp.Body) check(err) urlkey := string(body) data := fmt.Sprintf("%s\n%s\n", codehash, urlkey) os.WriteFile(sourcePath, []byte(data), 0644) return urlkey } func parseSegs(sourcePath string) ([]*Seg, string) { var ( lines []string source []string ) // Convert tabs to spaces for uniform rendering. for _, line := range readLines(sourcePath) { lines = append(lines, strings.Replace(line, "\t", " ", -1)) source = append(source, line) } segs := []*Seg{} lastSeen := "" for _, line := range lines { if line == "" { lastSeen = "" continue } matchDocs := docsPat.MatchString(line) matchCode := !matchDocs newDocs := (lastSeen == "") || ((lastSeen != "docs") && (segs[len(segs)-1].Docs != "")) newCode := (lastSeen == "") || ((lastSeen != "code") && (segs[len(segs)-1].Code != "")) if newDocs || newCode { debug("NEWSEG") } if matchDocs { trimmed := docsPat.ReplaceAllString(line, "") if newDocs { newSeg := Seg{Docs: trimmed, Code: ""} segs = append(segs, &newSeg) } else { segs[len(segs)-1].Docs = segs[len(segs)-1].Docs + "\n" + trimmed } debug("DOCS: " + line) lastSeen = "docs" } else if matchCode { if newCode { newSeg := Seg{Docs: "", Code: line} segs = append(segs, &newSeg) } else { lastSeg := segs[len(segs)-1] if len(lastSeg.Code) == 0 { lastSeg.Code = line } else { lastSeg.Code = lastSeg.Code + "\n" + line } } debug("CODE: " + line) lastSeen = "code" } } for i, seg := range segs { seg.CodeEmpty = (seg.Code == "") seg.CodeLeading = (i < (len(segs) - 1)) seg.CodeRun = strings.Contains(seg.Code, "package main") } return segs, strings.Join(source, "\n") } func chromaFormat(code, filePath string) string { lexer := lexers.Get(filePath) if lexer == nil { lexer = lexers.Fallback } if strings.HasSuffix(filePath, ".sh") { lexer = SimpleShellOutputLexer } lexer = chroma.Coalesce(lexer) style := styles.Get("swapoff") if style == nil { style = styles.Fallback } formatter := html.New(html.WithClasses(true)) iterator, err := lexer.Tokenise(nil, string(code)) check(err) buf := new(bytes.Buffer) err = formatter.Format(buf, style, iterator) check(err) return buf.String() } func parseAndRenderSegs(sourcePath string) ([]*Seg, string) { segs, filecontent := parseSegs(sourcePath) lexer := whichLexer(sourcePath) for _, seg := range segs { if seg.Docs != "" { seg.DocsRendered = markdown(seg.Docs) } if seg.Code != "" { seg.CodeRendered = chromaFormat(seg.Code, sourcePath) // adding the content to the js code for copying to the clipboard if strings.HasSuffix(sourcePath, ".go") { seg.CodeForJs = strings.Trim(seg.Code, "\n") + "\n" } } } // we are only interested in the 'go' code to pass to play.golang.org if lexer != "go" { filecontent = "" } return segs, filecontent } func parseExamples() []*Example { var exampleNames []string for _, line := range readLines("examples.txt") { if line != "" && !strings.HasPrefix(line, "#") { exampleNames = append(exampleNames, line) } } examples := make([]*Example, 0) for i, exampleName := range exampleNames { if verbose() { fmt.Printf("Processing %s [%d/%d]\n", exampleName, i+1, len(exampleNames)) } example := Example{Name: exampleName} exampleID := strings.ToLower(exampleName) exampleID = strings.Replace(exampleID, " ", "-", -1) exampleID = strings.Replace(exampleID, "/", "-", -1) exampleID = strings.Replace(exampleID, "'", "", -1) exampleID = dashPat.ReplaceAllString(exampleID, "-") example.ID = exampleID example.Segs = make([][]*Seg, 0) sourcePaths := mustGlob("examples/" + exampleID + "/*") for _, sourcePath := range sourcePaths { if !isDir(sourcePath) { if strings.HasSuffix(sourcePath, ".hash") { example.GoCodeHash, example.URLHash = parseHashFile(sourcePath) } else { sourceSegs, filecontents := parseAndRenderSegs(sourcePath) if filecontents != "" { example.GoCode = filecontents } example.Segs = append(example.Segs, sourceSegs) } } } newCodeHash := sha1Sum(example.GoCode) if example.GoCodeHash != newCodeHash { example.URLHash = resetURLHashFile(newCodeHash, example.GoCode, "examples/"+example.ID+"/"+example.ID+".hash") } examples = append(examples, &example) } for i, example := range examples { if i > 0 { example.PrevExample = examples[i-1] } if i < (len(examples) - 1) { example.NextExample = examples[i+1] } } return examples } func renderIndex(examples []*Example) { if verbose() { fmt.Println("Rendering index") } indexTmpl := template.New("index") template.Must(indexTmpl.Parse(mustReadFile("templates/footer.tmpl"))) template.Must(indexTmpl.Parse(mustReadFile("templates/index.tmpl"))) indexF, err := os.Create(siteDir + "/index.html") check(err) defer indexF.Close() check(indexTmpl.Execute(indexF, examples)) } func renderExamples(examples []*Example) { if verbose() { fmt.Println("Rendering examples") } exampleTmpl := template.New("example") template.Must(exampleTmpl.Parse(mustReadFile("templates/footer.tmpl"))) template.Must(exampleTmpl.Parse(mustReadFile("templates/example.tmpl"))) for _, example := range examples { exampleF, err := os.Create(siteDir + "/" + example.ID) check(err) defer exampleF.Close() check(exampleTmpl.Execute(exampleF, example)) } } func render404() { if verbose() { fmt.Println("Rendering 404") } tmpl := template.New("404") template.Must(tmpl.Parse(mustReadFile("templates/footer.tmpl"))) template.Must(tmpl.Parse(mustReadFile("templates/404.tmpl"))) file, err := os.Create(siteDir + "/404.html") check(err) defer file.Close() check(tmpl.Execute(file, "")) } func main() { if len(os.Args) > 1 { siteDir = os.Args[1] } ensureDir(siteDir) copyFile("templates/site.css", siteDir+"/site.css") copyFile("templates/site.js", siteDir+"/site.js") copyFile("templates/favicon.ico", siteDir+"/favicon.ico") copyFile("templates/play.png", siteDir+"/play.png") copyFile("templates/clipboard.png", siteDir+"/clipboard.png") examples := parseExamples() renderIndex(examples) renderExamples(examples) render404() } var SimpleShellOutputLexer = chroma.MustNewLexer( &chroma.Config{ Name: "Shell Output", Aliases: []string{"console"}, Filenames: []string{"*.sh"}, MimeTypes: []string{}, }, func() chroma.Rules { return chroma.Rules{ "root": { // $ or > triggers the start of prompt formatting {`^\$`, chroma.GenericPrompt, chroma.Push("prompt")}, {`^>`, chroma.GenericPrompt, chroma.Push("prompt")}, // empty lines are just text {`^$\n`, chroma.Text, nil}, // otherwise its all output {`[^\n]+$\n?`, chroma.GenericOutput, nil}, }, "prompt": { // when we find newline, do output formatting rules {`\n`, chroma.Text, chroma.Push("output")}, // otherwise its all text {`[^\n]+$`, chroma.Text, nil}, }, "output": { // sometimes there isn't output so we go right back to prompt {`^\$`, chroma.GenericPrompt, chroma.Pop(1)}, {`^>`, chroma.GenericPrompt, chroma.Pop(1)}, // otherwise its all output {`[^\n]+$\n?`, chroma.GenericOutput, nil}, }, } }, ) ================================================ FILE: tools/measure ================================================ #!/usr/bin/env bash exec go run tools/measure.go ================================================ FILE: tools/measure.go ================================================ package main import ( "fmt" "os" "path/filepath" "regexp" "strings" "unicode/utf8" ) func check(err error) { if err != nil { panic(err) } } func readLines(path string) []string { srcBytes, err := os.ReadFile(path) check(err) return strings.Split(string(srcBytes), "\n") } func isDir(path string) bool { fileStat, _ := os.Stat(path) return fileStat.IsDir() } var commentPat = regexp.MustCompile("\\s*\\/\\/") func main() { sourcePaths, err := filepath.Glob("./examples/*/*") check(err) foundLongFile := false for _, sourcePath := range sourcePaths { foundLongLine := false if !isDir(sourcePath) { lines := readLines(sourcePath) for i, line := range lines { // Convert tabs to spaces before measuring, so we get an accurate measure // of how long the output will end up being. line := strings.Replace(line, "\t", " ", -1) if !foundLongLine && !commentPat.MatchString(line) && (utf8.RuneCountInString(line) > 68) { fmt.Printf("measure: %s:%d (rune count = %d)\n", sourcePath, i+1, utf8.RuneCountInString(line)) foundLongLine = true foundLongFile = true } } } } if foundLongFile { os.Exit(1) } } ================================================ FILE: tools/serve ================================================ #!/usr/bin/env bash exec go run tools/serve.go ================================================ FILE: tools/serve.go ================================================ package main import ( "fmt" "net/http" ) func main() { port := "8000" publicDir := "public" fmt.Printf("Serving Go by Example at http://127.0.0.1:%s\n", port) http.ListenAndServe(":"+port, http.FileServer(http.Dir(publicDir))) } ================================================ FILE: tools/test ================================================ #!/usr/bin/env bash # Sanity testing of the examples. set -eo pipefail # go vet will attempt to build each example, making sure it compiles. It will # also report known issues with the code. Disabling the -unreachable check # because it will fire false positives for some examples demonstrating panics. go vet -unreachable=false ./examples/... ================================================ FILE: tools/upload ================================================ #!/usr/bin/env bash exec go run tools/upload.go -region us-east-1 -bucket gobyexample.com ================================================ FILE: tools/upload.go ================================================ // Uploads the generated site from the public/ directory to the S3 bucket from // which it's served. // To invoke this program, the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY // env vars have to be set appropriately, and the -region and -bucket flags // have to be passed in. package main import ( "context" "flag" "log" "os" "path/filepath" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/s3" ) // guessContentType guesses the HTTP content type appropriate for the given // filename. func guessContentType(filename string) string { switch filepath.Ext(filename) { case ".ico": return "image/x-icon" case ".png": return "image/png" case ".css": return "text/css" default: return "text/html" } } func main() { region := flag.String("region", "", "S3 region") bucket := flag.String("bucket", "", "S3 bucket name") flag.Parse() if len(*region) == 0 || len(*bucket) == 0 { log.Fatalf("region and bucket must be specified [region=%s, bucket=%s]", *region, *bucket) } cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion(*region)) if err != nil { log.Fatal(err) } client := s3.NewFromConfig(cfg) // The whole contents of the public/ directory are uploaded. This code assumes // the directory structure is flat - there are no subdirectories. publicDir := "./public/" c, err := os.ReadDir(publicDir) if err != nil { log.Fatal(err) } for _, entry := range c { if !entry.IsDir() { file, err := os.Open(filepath.Join(publicDir, entry.Name())) if err != nil { log.Fatal(err) } defer file.Close() contentType := guessContentType(entry.Name()) log.Printf("Uploading %s (%s)", entry.Name(), contentType) cfg := &s3.PutObjectInput{ Bucket: bucket, Key: aws.String(entry.Name()), Body: file, ContentType: aws.String(contentType), } _, err = client.PutObject(context.TODO(), cfg) if err != nil { log.Fatal(err) } } } }