From 1b608328baf5f72c658a15cb94abc29d7c044a10 Mon Sep 17 00:00:00 2001 From: Michael Reber Date: Mon, 18 Nov 2019 15:02:35 +0100 Subject: [PATCH] Adding golang examples --- go/README.md | 3 + go/arrays/arrays.go | 42 ++++++ go/arrays/arrays.hash | 2 + go/arrays/arrays.sh | 12 ++ go/atomic-counters/atomic-counters.go | 52 ++++++++ go/atomic-counters/atomic-counters.hash | 2 + go/atomic-counters/atomic-counters.sh | 11 ++ go/base64-encoding/base64-encoding.go | 39 ++++++ go/base64-encoding/base64-encoding.hash | 2 + go/base64-encoding/base64-encoding.sh | 9 ++ go/channel-buffering/channel-buffering.go | 27 ++++ go/channel-buffering/channel-buffering.hash | 2 + go/channel-buffering/channel-buffering.sh | 3 + go/channel-directions/channel-directions.go | 30 +++++ go/channel-directions/channel-directions.hash | 2 + go/channel-directions/channel-directions.sh | 2 + .../channel-synchronization.go | 36 +++++ .../channel-synchronization.hash | 2 + .../channel-synchronization.sh | 6 + go/channels/channels.go | 26 ++++ go/channels/channels.hash | 2 + go/channels/channels.sh | 10 ++ go/closing-channels/closing-channels.go | 50 +++++++ go/closing-channels/closing-channels.hash | 2 + go/closing-channels/closing-channels.sh | 12 ++ go/closures/closures.go | 40 ++++++ go/closures/closures.hash | 2 + go/closures/closures.sh | 8 ++ .../collection-functions.go | 113 ++++++++++++++++ .../collection-functions.hash | 2 + .../collection-functions.sh | 7 + .../command-line-arguments.go | 28 ++++ .../command-line-arguments.hash | 2 + .../command-line-arguments.sh | 10 ++ go/command-line-flags/command-line-flags.go | 51 +++++++ go/command-line-flags/command-line-flags.hash | 2 + go/command-line-flags/command-line-flags.sh | 56 ++++++++ .../command-line-subcommands.go | 57 ++++++++ .../command-line-subcommands.hash | 2 + .../command-line-subcommands.sh | 24 ++++ go/constants/constants.go | 35 +++++ go/constants/constants.hash | 2 + go/constants/constants.sh | 5 + go/defer/defer.go | 52 ++++++++ go/defer/defer.hash | 2 + go/defer/defer.sh | 6 + go/directories/directories.go | 95 ++++++++++++++ go/directories/directories.hash | 2 + go/directories/directories.sh | 15 +++ .../environment-variables.go | 33 +++++ .../environment-variables.hash | 2 + .../environment-variables.sh | 20 +++ go/epoch/epoch.go | 35 +++++ go/epoch/epoch.hash | 2 + go/epoch/epoch.sh | 10 ++ go/errors/errors.go | 87 ++++++++++++ go/errors/errors.hash | 2 + go/errors/errors.sh | 10 ++ go/execing-processes/execing-processes.go | 50 +++++++ go/execing-processes/execing-processes.hash | 2 + go/execing-processes/execing-processes.sh | 11 ++ go/exit/exit.go | 24 ++++ go/exit/exit.hash | 2 + go/exit/exit.sh | 13 ++ go/file-paths/file-paths.go | 64 +++++++++ go/file-paths/file-paths.hash | 2 + go/file-paths/file-paths.sh | 12 ++ go/for/for.go | 38 ++++++ go/for/for.hash | 2 + go/for/for.sh | 15 +++ go/functions/functions.go | 35 +++++ go/functions/functions.hash | 2 + go/functions/functions.sh | 6 + go/goroutines/goroutines.go | 39 ++++++ go/goroutines/goroutines.hash | 2 + go/goroutines/goroutines.sh | 16 +++ go/hello-world/hello-world.go | 9 ++ go/hello-world/hello-world.hash | 2 + go/hello-world/hello-world.sh | 17 +++ go/http-clients/http-clients.go | 38 ++++++ go/http-clients/http-clients.hash | 2 + go/http-clients/http-clients.sh | 7 + go/http-servers/http-servers.go | 50 +++++++ go/http-servers/http-servers.hash | 2 + go/http-servers/http-servers.sh | 6 + go/if-else/if-else.go | 35 +++++ go/if-else/if-else.hash | 2 + go/if-else/if-else.sh | 8 ++ go/interfaces/interfaces.go | 64 +++++++++ go/interfaces/interfaces.hash | 2 + go/interfaces/interfaces.sh | 10 ++ go/json/json.go | 124 ++++++++++++++++++ go/json/json.hash | 2 + go/json/json.sh | 21 +++ go/line-filters/line-filters.go | 41 ++++++ go/line-filters/line-filters.hash | 2 + go/line-filters/line-filters.sh | 9 ++ go/maps/maps.go | 50 +++++++ go/maps/maps.hash | 2 + go/maps/maps.sh | 9 ++ go/methods/methods.go | 37 ++++++ go/methods/methods.hash | 2 + go/methods/methods.sh | 8 ++ .../multiple-return-values.go | 27 ++++ .../multiple-return-values.hash | 2 + .../multiple-return-values.sh | 7 + go/mutexes/mutexes.go | 85 ++++++++++++ go/mutexes/mutexes.hash | 2 + go/mutexes/mutexes.sh | 10 ++ .../non-blocking-channel-operations.go | 49 +++++++ .../non-blocking-channel-operations.hash | 2 + .../non-blocking-channel-operations.sh | 4 + go/number-parsing/number-parsing.go | 42 ++++++ go/number-parsing/number-parsing.hash | 2 + go/number-parsing/number-parsing.sh | 9 ++ go/panic/panic.go | 25 ++++ go/panic/panic.hash | 2 + go/panic/panic.sh | 15 +++ go/pointers/pointers.go | 42 ++++++ go/pointers/pointers.hash | 2 + go/pointers/pointers.sh | 8 ++ go/random-numbers/random-numbers.go | 56 ++++++++ go/random-numbers/random-numbers.hash | 2 + go/random-numbers/random-numbers.sh | 12 ++ go/range-over-channels/range-over-channels.go | 25 ++++ .../range-over-channels.hash | 2 + go/range-over-channels/range-over-channels.sh | 7 + go/range/range.go | 48 +++++++ go/range/range.hash | 2 + go/range/range.sh | 9 ++ go/rate-limiting/rate-limiting.go | 71 ++++++++++ go/rate-limiting/rate-limiting.hash | 2 + go/rate-limiting/rate-limiting.sh | 17 +++ go/reading-files/reading-files.go | 84 ++++++++++++ go/reading-files/reading-files.hash | 2 + go/reading-files/reading-files.sh | 11 ++ go/recursion/recursion.go | 20 +++ go/recursion/recursion.hash | 2 + go/recursion/recursion.sh | 2 + go/regular-expressions/regular-expressions.go | 83 ++++++++++++ .../regular-expressions.hash | 2 + go/regular-expressions/regular-expressions.sh | 17 +++ go/select/select.go | 40 ++++++ go/select/select.hash | 2 + go/select/select.sh | 10 ++ go/sha1-hashes/sha1-hashes.go | 39 ++++++ go/sha1-hashes/sha1-hashes.hash | 2 + go/sha1-hashes/sha1-hashes.sh | 14 ++ go/signals/signals.go | 46 +++++++ go/signals/signals.hash | 2 + go/signals/signals.sh | 9 ++ go/slices/slices.go | 76 +++++++++++ go/slices/slices.hash | 2 + go/slices/slices.sh | 21 +++ .../sorting-by-functions.go | 45 +++++++ .../sorting-by-functions.hash | 2 + .../sorting-by-functions.sh | 10 ++ go/sorting/sorting.go | 31 +++++ go/sorting/sorting.hash | 2 + go/sorting/sorting.sh | 6 + go/spawning-processes/spawning-processes.go | 73 +++++++++++ go/spawning-processes/spawning-processes.hash | 2 + go/spawning-processes/spawning-processes.sh | 13 ++ go/stateful-goroutines/stateful-goroutines.go | 115 ++++++++++++++++ .../stateful-goroutines.hash | 2 + go/stateful-goroutines/stateful-goroutines.sh | 15 +++ go/string-formatting/string-formatting.go | 110 ++++++++++++++++ go/string-formatting/string-formatting.hash | 2 + go/string-formatting/string-formatting.sh | 24 ++++ go/string-functions/string-functions.go | 51 +++++++ go/string-functions/string-functions.hash | 2 + go/string-functions/string-functions.sh | 16 +++ go/structs/structs.go | 53 ++++++++ go/structs/structs.hash | 2 + go/structs/structs.sh | 9 ++ go/switch/switch.go | 63 +++++++++ go/switch/switch.hash | 2 + go/switch/switch.sh | 7 + .../temporary-files-and-directories.go | 65 +++++++++ .../temporary-files-and-directories.hash | 2 + .../temporary-files-and-directories.sh | 3 + go/testing/main_test.go | 69 ++++++++++ go/testing/main_test.sh | 18 +++ go/testing/testing.hash | 2 + go/tickers/tickers.go | 41 ++++++ go/tickers/tickers.hash | 2 + go/tickers/tickers.sh | 7 + .../time-formatting-parsing.go | 52 ++++++++ .../time-formatting-parsing.hash | 2 + .../time-formatting-parsing.sh | 9 ++ go/time/time.go | 63 +++++++++ go/time/time.hash | 2 + go/time/time.sh | 25 ++++ go/timeouts/timeouts.go | 53 ++++++++ go/timeouts/timeouts.hash | 2 + go/timeouts/timeouts.sh | 5 + go/timers/timers.go | 41 ++++++ go/timers/timers.hash | 2 + go/timers/timers.sh | 6 + go/url-parsing/url-parsing.go | 57 ++++++++ go/url-parsing/url-parsing.hash | 2 + go/url-parsing/url-parsing.sh | 15 +++ go/values/values.go | 22 ++++ go/values/values.hash | 2 + go/values/values.sh | 7 + go/variables/variables.go | 34 +++++ go/variables/variables.hash | 2 + go/variables/variables.sh | 6 + go/variadic-functions/variadic-functions.go | 33 +++++ go/variadic-functions/variadic-functions.hash | 2 + go/variadic-functions/variadic-functions.sh | 7 + go/waitgroups/waitgroups.go | 42 ++++++ go/waitgroups/waitgroups.hash | 2 + go/waitgroups/waitgroups.sh | 14 ++ go/worker-pools/worker-pools.go | 53 ++++++++ go/worker-pools/worker-pools.hash | 2 + go/worker-pools/worker-pools.sh | 17 +++ go/writing-files/writing-files.go | 58 ++++++++ go/writing-files/writing-files.hash | 2 + go/writing-files/writing-files.sh | 17 +++ go/xml/xml.go | 69 ++++++++++ go/xml/xml.hash | 2 + go/xml/xml.sh | 29 ++++ 223 files changed, 4799 insertions(+) create mode 100644 go/README.md create mode 100644 go/arrays/arrays.go create mode 100644 go/arrays/arrays.hash create mode 100644 go/arrays/arrays.sh create mode 100644 go/atomic-counters/atomic-counters.go create mode 100644 go/atomic-counters/atomic-counters.hash create mode 100644 go/atomic-counters/atomic-counters.sh create mode 100644 go/base64-encoding/base64-encoding.go create mode 100644 go/base64-encoding/base64-encoding.hash create mode 100644 go/base64-encoding/base64-encoding.sh create mode 100644 go/channel-buffering/channel-buffering.go create mode 100644 go/channel-buffering/channel-buffering.hash create mode 100644 go/channel-buffering/channel-buffering.sh create mode 100644 go/channel-directions/channel-directions.go create mode 100644 go/channel-directions/channel-directions.hash create mode 100644 go/channel-directions/channel-directions.sh create mode 100644 go/channel-synchronization/channel-synchronization.go create mode 100644 go/channel-synchronization/channel-synchronization.hash create mode 100644 go/channel-synchronization/channel-synchronization.sh create mode 100644 go/channels/channels.go create mode 100644 go/channels/channels.hash create mode 100644 go/channels/channels.sh create mode 100644 go/closing-channels/closing-channels.go create mode 100644 go/closing-channels/closing-channels.hash create mode 100644 go/closing-channels/closing-channels.sh create mode 100644 go/closures/closures.go create mode 100644 go/closures/closures.hash create mode 100644 go/closures/closures.sh create mode 100644 go/collection-functions/collection-functions.go create mode 100644 go/collection-functions/collection-functions.hash create mode 100644 go/collection-functions/collection-functions.sh create mode 100644 go/command-line-arguments/command-line-arguments.go create mode 100644 go/command-line-arguments/command-line-arguments.hash create mode 100644 go/command-line-arguments/command-line-arguments.sh create mode 100644 go/command-line-flags/command-line-flags.go create mode 100644 go/command-line-flags/command-line-flags.hash create mode 100644 go/command-line-flags/command-line-flags.sh create mode 100644 go/command-line-subcommands/command-line-subcommands.go create mode 100644 go/command-line-subcommands/command-line-subcommands.hash create mode 100644 go/command-line-subcommands/command-line-subcommands.sh create mode 100644 go/constants/constants.go create mode 100644 go/constants/constants.hash create mode 100644 go/constants/constants.sh create mode 100644 go/defer/defer.go create mode 100644 go/defer/defer.hash create mode 100644 go/defer/defer.sh create mode 100644 go/directories/directories.go create mode 100644 go/directories/directories.hash create mode 100644 go/directories/directories.sh create mode 100644 go/environment-variables/environment-variables.go create mode 100644 go/environment-variables/environment-variables.hash create mode 100644 go/environment-variables/environment-variables.sh create mode 100644 go/epoch/epoch.go create mode 100644 go/epoch/epoch.hash create mode 100644 go/epoch/epoch.sh create mode 100644 go/errors/errors.go create mode 100644 go/errors/errors.hash create mode 100644 go/errors/errors.sh create mode 100644 go/execing-processes/execing-processes.go create mode 100644 go/execing-processes/execing-processes.hash create mode 100644 go/execing-processes/execing-processes.sh create mode 100644 go/exit/exit.go create mode 100644 go/exit/exit.hash create mode 100644 go/exit/exit.sh create mode 100644 go/file-paths/file-paths.go create mode 100644 go/file-paths/file-paths.hash create mode 100644 go/file-paths/file-paths.sh create mode 100644 go/for/for.go create mode 100644 go/for/for.hash create mode 100644 go/for/for.sh create mode 100644 go/functions/functions.go create mode 100644 go/functions/functions.hash create mode 100644 go/functions/functions.sh create mode 100644 go/goroutines/goroutines.go create mode 100644 go/goroutines/goroutines.hash create mode 100644 go/goroutines/goroutines.sh create mode 100644 go/hello-world/hello-world.go create mode 100644 go/hello-world/hello-world.hash create mode 100644 go/hello-world/hello-world.sh create mode 100644 go/http-clients/http-clients.go create mode 100644 go/http-clients/http-clients.hash create mode 100644 go/http-clients/http-clients.sh create mode 100644 go/http-servers/http-servers.go create mode 100644 go/http-servers/http-servers.hash create mode 100644 go/http-servers/http-servers.sh create mode 100644 go/if-else/if-else.go create mode 100644 go/if-else/if-else.hash create mode 100644 go/if-else/if-else.sh create mode 100644 go/interfaces/interfaces.go create mode 100644 go/interfaces/interfaces.hash create mode 100644 go/interfaces/interfaces.sh create mode 100644 go/json/json.go create mode 100644 go/json/json.hash create mode 100644 go/json/json.sh create mode 100644 go/line-filters/line-filters.go create mode 100644 go/line-filters/line-filters.hash create mode 100644 go/line-filters/line-filters.sh create mode 100644 go/maps/maps.go create mode 100644 go/maps/maps.hash create mode 100644 go/maps/maps.sh create mode 100644 go/methods/methods.go create mode 100644 go/methods/methods.hash create mode 100644 go/methods/methods.sh create mode 100644 go/multiple-return-values/multiple-return-values.go create mode 100644 go/multiple-return-values/multiple-return-values.hash create mode 100644 go/multiple-return-values/multiple-return-values.sh create mode 100644 go/mutexes/mutexes.go create mode 100644 go/mutexes/mutexes.hash create mode 100644 go/mutexes/mutexes.sh create mode 100644 go/non-blocking-channel-operations/non-blocking-channel-operations.go create mode 100644 go/non-blocking-channel-operations/non-blocking-channel-operations.hash create mode 100644 go/non-blocking-channel-operations/non-blocking-channel-operations.sh create mode 100644 go/number-parsing/number-parsing.go create mode 100644 go/number-parsing/number-parsing.hash create mode 100644 go/number-parsing/number-parsing.sh create mode 100644 go/panic/panic.go create mode 100644 go/panic/panic.hash create mode 100644 go/panic/panic.sh create mode 100644 go/pointers/pointers.go create mode 100644 go/pointers/pointers.hash create mode 100644 go/pointers/pointers.sh create mode 100644 go/random-numbers/random-numbers.go create mode 100644 go/random-numbers/random-numbers.hash create mode 100644 go/random-numbers/random-numbers.sh create mode 100644 go/range-over-channels/range-over-channels.go create mode 100644 go/range-over-channels/range-over-channels.hash create mode 100644 go/range-over-channels/range-over-channels.sh create mode 100644 go/range/range.go create mode 100644 go/range/range.hash create mode 100644 go/range/range.sh create mode 100644 go/rate-limiting/rate-limiting.go create mode 100644 go/rate-limiting/rate-limiting.hash create mode 100644 go/rate-limiting/rate-limiting.sh create mode 100644 go/reading-files/reading-files.go create mode 100644 go/reading-files/reading-files.hash create mode 100644 go/reading-files/reading-files.sh create mode 100644 go/recursion/recursion.go create mode 100644 go/recursion/recursion.hash create mode 100644 go/recursion/recursion.sh create mode 100644 go/regular-expressions/regular-expressions.go create mode 100644 go/regular-expressions/regular-expressions.hash create mode 100644 go/regular-expressions/regular-expressions.sh create mode 100644 go/select/select.go create mode 100644 go/select/select.hash create mode 100644 go/select/select.sh create mode 100644 go/sha1-hashes/sha1-hashes.go create mode 100644 go/sha1-hashes/sha1-hashes.hash create mode 100644 go/sha1-hashes/sha1-hashes.sh create mode 100644 go/signals/signals.go create mode 100644 go/signals/signals.hash create mode 100644 go/signals/signals.sh create mode 100644 go/slices/slices.go create mode 100644 go/slices/slices.hash create mode 100644 go/slices/slices.sh create mode 100644 go/sorting-by-functions/sorting-by-functions.go create mode 100644 go/sorting-by-functions/sorting-by-functions.hash create mode 100644 go/sorting-by-functions/sorting-by-functions.sh create mode 100644 go/sorting/sorting.go create mode 100644 go/sorting/sorting.hash create mode 100644 go/sorting/sorting.sh create mode 100644 go/spawning-processes/spawning-processes.go create mode 100644 go/spawning-processes/spawning-processes.hash create mode 100644 go/spawning-processes/spawning-processes.sh create mode 100644 go/stateful-goroutines/stateful-goroutines.go create mode 100644 go/stateful-goroutines/stateful-goroutines.hash create mode 100644 go/stateful-goroutines/stateful-goroutines.sh create mode 100644 go/string-formatting/string-formatting.go create mode 100644 go/string-formatting/string-formatting.hash create mode 100644 go/string-formatting/string-formatting.sh create mode 100644 go/string-functions/string-functions.go create mode 100644 go/string-functions/string-functions.hash create mode 100644 go/string-functions/string-functions.sh create mode 100644 go/structs/structs.go create mode 100644 go/structs/structs.hash create mode 100644 go/structs/structs.sh create mode 100644 go/switch/switch.go create mode 100644 go/switch/switch.hash create mode 100644 go/switch/switch.sh create mode 100644 go/temporary-files-and-directories/temporary-files-and-directories.go create mode 100644 go/temporary-files-and-directories/temporary-files-and-directories.hash create mode 100644 go/temporary-files-and-directories/temporary-files-and-directories.sh create mode 100644 go/testing/main_test.go create mode 100644 go/testing/main_test.sh create mode 100644 go/testing/testing.hash create mode 100644 go/tickers/tickers.go create mode 100644 go/tickers/tickers.hash create mode 100644 go/tickers/tickers.sh create mode 100644 go/time-formatting-parsing/time-formatting-parsing.go create mode 100644 go/time-formatting-parsing/time-formatting-parsing.hash create mode 100644 go/time-formatting-parsing/time-formatting-parsing.sh create mode 100644 go/time/time.go create mode 100644 go/time/time.hash create mode 100644 go/time/time.sh create mode 100644 go/timeouts/timeouts.go create mode 100644 go/timeouts/timeouts.hash create mode 100644 go/timeouts/timeouts.sh create mode 100644 go/timers/timers.go create mode 100644 go/timers/timers.hash create mode 100644 go/timers/timers.sh create mode 100644 go/url-parsing/url-parsing.go create mode 100644 go/url-parsing/url-parsing.hash create mode 100644 go/url-parsing/url-parsing.sh create mode 100644 go/values/values.go create mode 100644 go/values/values.hash create mode 100644 go/values/values.sh create mode 100644 go/variables/variables.go create mode 100644 go/variables/variables.hash create mode 100644 go/variables/variables.sh create mode 100644 go/variadic-functions/variadic-functions.go create mode 100644 go/variadic-functions/variadic-functions.hash create mode 100644 go/variadic-functions/variadic-functions.sh create mode 100644 go/waitgroups/waitgroups.go create mode 100644 go/waitgroups/waitgroups.hash create mode 100644 go/waitgroups/waitgroups.sh create mode 100644 go/worker-pools/worker-pools.go create mode 100644 go/worker-pools/worker-pools.hash create mode 100644 go/worker-pools/worker-pools.sh create mode 100644 go/writing-files/writing-files.go create mode 100644 go/writing-files/writing-files.hash create mode 100644 go/writing-files/writing-files.sh create mode 100644 go/xml/xml.go create mode 100644 go/xml/xml.hash create mode 100644 go/xml/xml.sh diff --git a/go/README.md b/go/README.md new file mode 100644 index 0000000..5c9b033 --- /dev/null +++ b/go/README.md @@ -0,0 +1,3 @@ +# Golang + +Go, also known as Golang, is a statically typed, compiled programming language designed at Google by Robert Griesemer, Rob Pike, and Ken Thompson. Go is syntactically similar to C, but with memory safety, garbage collection, structural typing, and CSP-style concurrency. \ No newline at end of file diff --git a/go/arrays/arrays.go b/go/arrays/arrays.go new file mode 100644 index 0000000..54bff09 --- /dev/null +++ b/go/arrays/arrays.go @@ -0,0 +1,42 @@ +// In Go, an _array_ is a numbered sequence of elements of a +// specific length. + +package main + +import "fmt" + +func main() { + + // Here we create an array `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) + + // Array types are one-dimensional, but you can + // compose types to build multi-dimensional data + // structures. + var twoD [2][3]int + for i := 0; i < 2; i++ { + for j := 0; j < 3; j++ { + twoD[i][j] = i + j + } + } + fmt.Println("2d: ", twoD) +} diff --git a/go/arrays/arrays.hash b/go/arrays/arrays.hash new file mode 100644 index 0000000..cae0e0d --- /dev/null +++ b/go/arrays/arrays.hash @@ -0,0 +1,2 @@ +305975d13d24223181d13f042b290906d86c1a0e +W7NwfDq8Vdw diff --git a/go/arrays/arrays.sh b/go/arrays/arrays.sh new file mode 100644 index 0000000..61e3019 --- /dev/null +++ b/go/arrays/arrays.sh @@ -0,0 +1,12 @@ +# 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] +2d: [[0 1 2] [1 2 3]] + +# You'll see _slices_ much more often than arrays in +# typical Go. We'll look at slices next. diff --git a/go/atomic-counters/atomic-counters.go b/go/atomic-counters/atomic-counters.go new file mode 100644 index 0000000..046a347 --- /dev/null +++ b/go/atomic-counters/atomic-counters.go @@ -0,0 +1,52 @@ +// 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 unsigned integer to represent our + // (always-positive) counter. + var ops 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 i := 0; i < 50; i++ { + wg.Add(1) + + go func() { + for c := 0; c < 1000; c++ { + // To atomically increment the counter we + // use `AddUint64`, giving it the memory + // address of our `ops` counter with the + // `&` syntax. + atomic.AddUint64(&ops, 1) + } + wg.Done() + }() + } + + // Wait until all the goroutines are done. + wg.Wait() + + // It's safe to access `ops` now because we know + // no other goroutine is writing to it. Reading + // atomics safely while they are being updated is + // also possible, using functions like + // `atomic.LoadUint64`. + fmt.Println("ops:", ops) +} diff --git a/go/atomic-counters/atomic-counters.hash b/go/atomic-counters/atomic-counters.hash new file mode 100644 index 0000000..989ed19 --- /dev/null +++ b/go/atomic-counters/atomic-counters.hash @@ -0,0 +1,2 @@ +8ebec0be3b167021c96b8b497d0e8c0a2ea99385 +F2pJfduyQiA diff --git a/go/atomic-counters/atomic-counters.sh b/go/atomic-counters/atomic-counters.sh new file mode 100644 index 0000000..1680a10 --- /dev/null +++ b/go/atomic-counters/atomic-counters.sh @@ -0,0 +1,11 @@ +# We expect to get exactly 50,000 operations. Had we +# used the non-atomic `ops++` to increment the counter, +# 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. diff --git a/go/base64-encoding/base64-encoding.go b/go/base64-encoding/base64-encoding.go new file mode 100644 index 0000000..09e94fd --- /dev/null +++ b/go/base64-encoding/base64-encoding.go @@ -0,0 +1,39 @@ +// Go provides built-in support for [base64 +// encoding/decoding](http://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)) +} diff --git a/go/base64-encoding/base64-encoding.hash b/go/base64-encoding/base64-encoding.hash new file mode 100644 index 0000000..717ff3f --- /dev/null +++ b/go/base64-encoding/base64-encoding.hash @@ -0,0 +1,2 @@ +e0148b9b4acb01e849b8f678cba03f549d250c44 +V3oV1bvh94k diff --git a/go/base64-encoding/base64-encoding.sh b/go/base64-encoding/base64-encoding.sh new file mode 100644 index 0000000..8124a08 --- /dev/null +++ b/go/base64-encoding/base64-encoding.sh @@ -0,0 +1,9 @@ +# 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!?$*&()'-=@~ diff --git a/go/channel-buffering/channel-buffering.go b/go/channel-buffering/channel-buffering.go new file mode 100644 index 0000000..d2e8d8f --- /dev/null +++ b/go/channel-buffering/channel-buffering.go @@ -0,0 +1,27 @@ +// 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) +} diff --git a/go/channel-buffering/channel-buffering.hash b/go/channel-buffering/channel-buffering.hash new file mode 100644 index 0000000..6956e03 --- /dev/null +++ b/go/channel-buffering/channel-buffering.hash @@ -0,0 +1,2 @@ +122140f7ad1bc5cff4fcd7a9e7245b87aaca3ec5 +mPoF-Xi-rip diff --git a/go/channel-buffering/channel-buffering.sh b/go/channel-buffering/channel-buffering.sh new file mode 100644 index 0000000..13c63db --- /dev/null +++ b/go/channel-buffering/channel-buffering.sh @@ -0,0 +1,3 @@ +$ go run channel-buffering.go +buffered +channel diff --git a/go/channel-directions/channel-directions.go b/go/channel-directions/channel-directions.go new file mode 100644 index 0000000..5f2786c --- /dev/null +++ b/go/channel-directions/channel-directions.go @@ -0,0 +1,30 @@ +// 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) +} diff --git a/go/channel-directions/channel-directions.hash b/go/channel-directions/channel-directions.hash new file mode 100644 index 0000000..fb68662 --- /dev/null +++ b/go/channel-directions/channel-directions.hash @@ -0,0 +1,2 @@ +635cc13dfe33123ac188e01e3002d3aa935d765f +Jnn9_9hC48c diff --git a/go/channel-directions/channel-directions.sh b/go/channel-directions/channel-directions.sh new file mode 100644 index 0000000..8db5cb6 --- /dev/null +++ b/go/channel-directions/channel-directions.sh @@ -0,0 +1,2 @@ +$ go run channel-directions.go +passed message diff --git a/go/channel-synchronization/channel-synchronization.go b/go/channel-synchronization/channel-synchronization.go new file mode 100644 index 0000000..889b97b --- /dev/null +++ b/go/channel-synchronization/channel-synchronization.go @@ -0,0 +1,36 @@ +// 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 +} diff --git a/go/channel-synchronization/channel-synchronization.hash b/go/channel-synchronization/channel-synchronization.hash new file mode 100644 index 0000000..9dea9a0 --- /dev/null +++ b/go/channel-synchronization/channel-synchronization.hash @@ -0,0 +1,2 @@ +0d5a9de912e2a4a18943e082e2f8107cb75d0ce4 +fe9If6OhYMk diff --git a/go/channel-synchronization/channel-synchronization.sh b/go/channel-synchronization/channel-synchronization.sh new file mode 100644 index 0000000..b3496b3 --- /dev/null +++ b/go/channel-synchronization/channel-synchronization.sh @@ -0,0 +1,6 @@ +$ go run channel-synchronization.go +working...done + +# If you removed the `<- done` line from this program, the +# program would exit before the `worker` even +# started. diff --git a/go/channels/channels.go b/go/channels/channels.go new file mode 100644 index 0000000..7a5ac9a --- /dev/null +++ b/go/channels/channels.go @@ -0,0 +1,26 @@ +// _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) +} diff --git a/go/channels/channels.hash b/go/channels/channels.hash new file mode 100644 index 0000000..92301ce --- /dev/null +++ b/go/channels/channels.hash @@ -0,0 +1,2 @@ +926212c784ab820648906c96f6ab21afbc161526 +bRGMAqinovA diff --git a/go/channels/channels.sh b/go/channels/channels.sh new file mode 100644 index 0000000..0d37c18 --- /dev/null +++ b/go/channels/channels.sh @@ -0,0 +1,10 @@ +# 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. diff --git a/go/closing-channels/closing-channels.go b/go/closing-channels/closing-channels.go new file mode 100644 index 0000000..926d9b5 --- /dev/null +++ b/go/closing-channels/closing-channels.go @@ -0,0 +1,50 @@ +// _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 +} diff --git a/go/closing-channels/closing-channels.hash b/go/closing-channels/closing-channels.hash new file mode 100644 index 0000000..007751f --- /dev/null +++ b/go/closing-channels/closing-channels.hash @@ -0,0 +1,2 @@ +5205898a520533e46ea24c849848d19ebc2d08a9 +mkz69rVMHs6 diff --git a/go/closing-channels/closing-channels.sh b/go/closing-channels/closing-channels.sh new file mode 100644 index 0000000..013f8a8 --- /dev/null +++ b/go/closing-channels/closing-channels.sh @@ -0,0 +1,12 @@ +$ go run closing-channels.go +sent job 1 +received job 1 +sent job 2 +received job 2 +sent job 3 +received job 3 +sent all jobs +received all jobs + +# The idea of closed channels leads naturally to our next +# example: `range` over channels. diff --git a/go/closures/closures.go b/go/closures/closures.go new file mode 100644 index 0000000..90dc2eb --- /dev/null +++ b/go/closures/closures.go @@ -0,0 +1,40 @@ +// Go supports [_anonymous functions_](http://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()) +} diff --git a/go/closures/closures.hash b/go/closures/closures.hash new file mode 100644 index 0000000..fa66f0f --- /dev/null +++ b/go/closures/closures.hash @@ -0,0 +1,2 @@ +e304df67e760dda93ffe434aca58aea4a6c94f19 +zb93qzV6iN3 diff --git a/go/closures/closures.sh b/go/closures/closures.sh new file mode 100644 index 0000000..9fc7bd8 --- /dev/null +++ b/go/closures/closures.sh @@ -0,0 +1,8 @@ +$ go run closures.go +1 +2 +3 +1 + +# The last feature of functions we'll look at for now is +# recursion. diff --git a/go/collection-functions/collection-functions.go b/go/collection-functions/collection-functions.go new file mode 100644 index 0000000..730644c --- /dev/null +++ b/go/collection-functions/collection-functions.go @@ -0,0 +1,113 @@ +// We often need our programs to perform operations on +// collections of data, like selecting all items that +// satisfy a given predicate or mapping all items to a new +// collection with a custom function. + +// In some languages it's idiomatic to use [generic](http://en.wikipedia.org/wiki/Generic_programming) +// data structures and algorithms. Go does not support +// generics; in Go it's common to provide collection +// functions if and when they are specifically needed for +// your program and data types. + +// Here are some example collection functions for slices +// of `strings`. You can use these examples to build your +// own functions. Note that in some cases it may be +// clearest to just inline the collection-manipulating +// code directly, instead of creating and calling a +// helper function. + +package main + +import ( + "fmt" + "strings" +) + +// Index returns the first index of the target string `t`, or +// -1 if no match is found. +func Index(vs []string, t string) int { + for i, v := range vs { + if v == t { + return i + } + } + return -1 +} + +// Include returns `true` if the target string t is in the +// slice. +func Include(vs []string, t string) bool { + return Index(vs, t) >= 0 +} + +// Any returns `true` if one of the strings in the slice +// satisfies the predicate `f`. +func Any(vs []string, f func(string) bool) bool { + for _, v := range vs { + if f(v) { + return true + } + } + return false +} + +// All returns `true` if all of the strings in the slice +// satisfy the predicate `f`. +func All(vs []string, f func(string) bool) bool { + for _, v := range vs { + if !f(v) { + return false + } + } + return true +} + +// Filter returns a new slice containing all strings in the +// slice that satisfy the predicate `f`. +func Filter(vs []string, f func(string) bool) []string { + vsf := make([]string, 0) + for _, v := range vs { + if f(v) { + vsf = append(vsf, v) + } + } + return vsf +} + +// Map returns a new slice containing the results of applying +// the function `f` to each string in the original slice. +func Map(vs []string, f func(string) string) []string { + vsm := make([]string, len(vs)) + for i, v := range vs { + vsm[i] = f(v) + } + return vsm +} + +func main() { + + // Here we try out our various collection functions. + var strs = []string{"peach", "apple", "pear", "plum"} + + fmt.Println(Index(strs, "pear")) + + fmt.Println(Include(strs, "grape")) + + fmt.Println(Any(strs, func(v string) bool { + return strings.HasPrefix(v, "p") + })) + + fmt.Println(All(strs, func(v string) bool { + return strings.HasPrefix(v, "p") + })) + + fmt.Println(Filter(strs, func(v string) bool { + return strings.Contains(v, "e") + })) + + // The above examples all used anonymous functions, + // but you can also use named functions of the correct + // type. + fmt.Println(Map(strs, strings.ToUpper)) + +} diff --git a/go/collection-functions/collection-functions.hash b/go/collection-functions/collection-functions.hash new file mode 100644 index 0000000..abbd3d5 --- /dev/null +++ b/go/collection-functions/collection-functions.hash @@ -0,0 +1,2 @@ +28456737ea996664bdaeb8e1e821d95697d00646 +8hI6oPNEfyh diff --git a/go/collection-functions/collection-functions.sh b/go/collection-functions/collection-functions.sh new file mode 100644 index 0000000..95dc0ee --- /dev/null +++ b/go/collection-functions/collection-functions.sh @@ -0,0 +1,7 @@ +$ go run collection-functions.go +2 +false +true +false +[peach apple pear] +[PEACH APPLE PEAR PLUM] diff --git a/go/command-line-arguments/command-line-arguments.go b/go/command-line-arguments/command-line-arguments.go new file mode 100644 index 0000000..1104113 --- /dev/null +++ b/go/command-line-arguments/command-line-arguments.go @@ -0,0 +1,28 @@ +// [_Command-line arguments_](http://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) +} diff --git a/go/command-line-arguments/command-line-arguments.hash b/go/command-line-arguments/command-line-arguments.hash new file mode 100644 index 0000000..ad22b59 --- /dev/null +++ b/go/command-line-arguments/command-line-arguments.hash @@ -0,0 +1,2 @@ +9c80d495201148bbeb0fd0a5a2ce1735aeb34b60 +myJy_-H8Fo_Q diff --git a/go/command-line-arguments/command-line-arguments.sh b/go/command-line-arguments/command-line-arguments.sh new file mode 100644 index 0000000..b79d8b3 --- /dev/null +++ b/go/command-line-arguments/command-line-arguments.sh @@ -0,0 +1,10 @@ +# 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. diff --git a/go/command-line-flags/command-line-flags.go b/go/command-line-flags/command-line-flags.go new file mode 100644 index 0000000..96647f5 --- /dev/null +++ b/go/command-line-flags/command-line-flags.go @@ -0,0 +1,51 @@ +// [_Command-line flags_](http://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") + boolPtr := flag.Bool("fork", false, "a bool") + + // It's also possible to declare an option that uses an + // existing var declared elsewhere in the program. + // Note that we need to pass in a pointer to the flag + // declaration function. + var svar string + flag.StringVar(&svar, "svar", "bar", "a string var") + + // Once all flags are declared, call `flag.Parse()` + // to execute the command-line parsing. + flag.Parse() + + // Here we'll just dump out the parsed options and + // any trailing positional arguments. Note that we + // need to dereference the pointers with e.g. `*wordPtr` + // to get the actual option values. + fmt.Println("word:", *wordPtr) + fmt.Println("numb:", *numbPtr) + fmt.Println("fork:", *boolPtr) + fmt.Println("svar:", svar) + fmt.Println("tail:", flag.Args()) +} diff --git a/go/command-line-flags/command-line-flags.hash b/go/command-line-flags/command-line-flags.hash new file mode 100644 index 0000000..abee236 --- /dev/null +++ b/go/command-line-flags/command-line-flags.hash @@ -0,0 +1,2 @@ +ab08bf890dcd87b807956b5bee441d59efe458e3 +lPaZodnG9TF diff --git a/go/command-line-flags/command-line-flags.sh b/go/command-line-flags/command-line-flags.sh new file mode 100644 index 0000000..246f4c7 --- /dev/null +++ b/go/command-line-flags/command-line-flags.sh @@ -0,0 +1,56 @@ +# 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: +... diff --git a/go/command-line-subcommands/command-line-subcommands.go b/go/command-line-subcommands/command-line-subcommands.go new file mode 100644 index 0000000..e2d41ed --- /dev/null +++ b/go/command-line-subcommands/command-line-subcommands.go @@ -0,0 +1,57 @@ +// Some command-line tools, like the `go` tool or `git` +// have many *subcommands*, each with its own set of +// flags. For example, `go build` and `go get` are two +// different subcommands of the `go` tool. +// The `flag` package lets us easily define simple +// subcommands that have their own flags. + +package main + +import ( + "flag" + "fmt" + "os" +) + +func main() { + + // We declare a subcommand using the `NewFlagSet` + // function, and proceed to define new flags specific + // for this subcommand. + fooCmd := flag.NewFlagSet("foo", flag.ExitOnError) + fooEnable := fooCmd.Bool("enable", false, "enable") + fooName := fooCmd.String("name", "", "name") + + // For a different subcommand we can define different + // supported flags. + barCmd := flag.NewFlagSet("bar", flag.ExitOnError) + barLevel := barCmd.Int("level", 0, "level") + + // The subcommand is expected as the first argument + // to the program. + if len(os.Args) < 2 { + fmt.Println("expected 'foo' or 'bar' subcommands") + os.Exit(1) + } + + // Check which subcommand is invoked. + switch os.Args[1] { + + // For every subcommand, we parse its own flags and + // have access to trailing positional arguments. + case "foo": + fooCmd.Parse(os.Args[2:]) + fmt.Println("subcommand 'foo'") + fmt.Println(" enable:", *fooEnable) + fmt.Println(" name:", *fooName) + fmt.Println(" tail:", fooCmd.Args()) + case "bar": + barCmd.Parse(os.Args[2:]) + fmt.Println("subcommand 'bar'") + fmt.Println(" level:", *barLevel) + fmt.Println(" tail:", barCmd.Args()) + default: + fmt.Println("expected 'foo' or 'bar' subcommands") + os.Exit(1) + } +} diff --git a/go/command-line-subcommands/command-line-subcommands.hash b/go/command-line-subcommands/command-line-subcommands.hash new file mode 100644 index 0000000..f22bcc8 --- /dev/null +++ b/go/command-line-subcommands/command-line-subcommands.hash @@ -0,0 +1,2 @@ +5a0ec258e4992e9b93b11d48f2f249092ff3db66 +gtgSAg76N4I diff --git a/go/command-line-subcommands/command-line-subcommands.sh b/go/command-line-subcommands/command-line-subcommands.sh new file mode 100644 index 0000000..32531ed --- /dev/null +++ b/go/command-line-subcommands/command-line-subcommands.sh @@ -0,0 +1,24 @@ +$ go build command-line-subcommands.go + +# First invoke the foo subcommand. +$ ./command-line-subcommands foo -enable -name=joe a1 a2 +subcommand 'foo' + enable: true + name: joe + tail: [a1 a2] + +# Now try bar. +$ ./command-line-subcommands bar -level 8 a1 +subcommand 'bar' + level: 8 + tail: [a1] + +# But bar won't accept foo's flags. +$ ./command-line-subcommands bar -enable a1 +flag provided but not defined: -enable +Usage of bar: + -level int + level + +# Next we'll look at environment variables, another common +# way to parameterize programs. diff --git a/go/constants/constants.go b/go/constants/constants.go new file mode 100644 index 0000000..e7af860 --- /dev/null +++ b/go/constants/constants.go @@ -0,0 +1,35 @@ +// 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 appear anywhere a `var` + // statement can. + 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)) +} diff --git a/go/constants/constants.hash b/go/constants/constants.hash new file mode 100644 index 0000000..93d42ca --- /dev/null +++ b/go/constants/constants.hash @@ -0,0 +1,2 @@ +7cc09460e8dc6fffd0ba811679f92a85eb51e927 +gmjHSglwLic diff --git a/go/constants/constants.sh b/go/constants/constants.sh new file mode 100644 index 0000000..a29c22b --- /dev/null +++ b/go/constants/constants.sh @@ -0,0 +1,5 @@ +$ go run constant.go +constant +6e+11 +600000000000 +-0.28470407323754404 diff --git a/go/defer/defer.go b/go/defer/defer.go new file mode 100644 index 0000000..b4077c7 --- /dev/null +++ b/go/defer/defer.go @@ -0,0 +1,52 @@ +// _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" +) + +// 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. + f := createFile("/tmp/defer.txt") + defer closeFile(f) + writeFile(f) +} + +func createFile(p string) *os.File { + fmt.Println("creating") + f, err := os.Create(p) + if err != nil { + panic(err) + } + return f +} + +func writeFile(f *os.File) { + fmt.Println("writing") + fmt.Fprintln(f, "data") + +} + +func closeFile(f *os.File) { + fmt.Println("closing") + err := f.Close() + // It's important to check for errors when closing a + // file, even in a deferred function. + if err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } +} diff --git a/go/defer/defer.hash b/go/defer/defer.hash new file mode 100644 index 0000000..d45bfd7 --- /dev/null +++ b/go/defer/defer.hash @@ -0,0 +1,2 @@ +4fc23579a9bd5d8512884902394f4dce51eb7d60 +QJJ2R6kv6K5 diff --git a/go/defer/defer.sh b/go/defer/defer.sh new file mode 100644 index 0000000..e84518b --- /dev/null +++ b/go/defer/defer.sh @@ -0,0 +1,6 @@ +# Running the program confirms that the file is closed +# after being written. +$ go run defer.go +creating +writing +closing diff --git a/go/directories/directories.go b/go/directories/directories.go new file mode 100644 index 0000000..9b71274 --- /dev/null +++ b/go/directories/directories.go @@ -0,0 +1,95 @@ +// Go has several useful functions for working with +// *directories* in the file system. + +package main + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" +) + +func check(e error) { + if e != nil { + panic(e) + } +} + +func main() { + + // Create a new sub-directory in the current working + // directory. + err := os.Mkdir("subdir", 0755) + check(err) + + // When creating temporary directories, it's good + // practice to `defer` their removal. `os.RemoveAll` + // will delete a whole directory tree (similarly to + // `rm -rf`). + defer os.RemoveAll("subdir") + + // Helper function to create a new empty file. + createEmptyFile := func(name string) { + d := []byte("") + check(ioutil.WriteFile(name, d, 0644)) + } + + createEmptyFile("subdir/file1") + + // We can create a hierarchy of directories, including + // parents with `MkdirAll`. This is similar to the + // command-line `mkdir -p`. + err = os.MkdirAll("subdir/parent/child", 0755) + check(err) + + createEmptyFile("subdir/parent/file2") + createEmptyFile("subdir/parent/file3") + createEmptyFile("subdir/parent/child/file4") + + // `ReadDir` lists directory contents, returning a + // slice of `os.FileInfo` objects. + c, err := ioutil.ReadDir("subdir/parent") + check(err) + + fmt.Println("Listing subdir/parent") + for _, entry := range c { + fmt.Println(" ", entry.Name(), entry.IsDir()) + } + + // `Chdir` lets us change the current working directory, + // similarly to `cd`. + err = os.Chdir("subdir/parent/child") + check(err) + + // Now we'll see the contents of `subdir/parent/child` + // when listing the *current* directory. + c, err = ioutil.ReadDir(".") + check(err) + + fmt.Println("Listing subdir/parent/child") + for _, entry := range c { + fmt.Println(" ", entry.Name(), entry.IsDir()) + } + + // `cd` back to where we started. + err = os.Chdir("../../..") + check(err) + + // We can also visit a directory *recursively*, + // including all its sub-directories. `Walk` accepts + // a callback function to handle every file or + // directory visited. + fmt.Println("Visiting subdir") + err = filepath.Walk("subdir", visit) +} + +// `visit` is called for every file or directory found +// recursively by `filepath.Walk`. +func visit(p string, info os.FileInfo, err error) error { + if err != nil { + return err + } + fmt.Println(" ", p, info.IsDir()) + return nil +} diff --git a/go/directories/directories.hash b/go/directories/directories.hash new file mode 100644 index 0000000..cebae56 --- /dev/null +++ b/go/directories/directories.hash @@ -0,0 +1,2 @@ +83f67db91816b4544072d0a4d099111a21c60723 +-7kWq0PmATF diff --git a/go/directories/directories.sh b/go/directories/directories.sh new file mode 100644 index 0000000..31a8f0c --- /dev/null +++ b/go/directories/directories.sh @@ -0,0 +1,15 @@ +$ go run directories.go +Listing subdir/parent + child true + file2 false + file3 false +Listing subdir/parent/child + file4 false +Visiting subdir + subdir true + subdir/file1 false + subdir/parent true + subdir/parent/child true + subdir/parent/child/file4 false + subdir/parent/file2 false + subdir/parent/file3 false diff --git a/go/environment-variables/environment-variables.go b/go/environment-variables/environment-variables.go new file mode 100644 index 0000000..e3e42a5 --- /dev/null +++ b/go/environment-variables/environment-variables.go @@ -0,0 +1,33 @@ +// [Environment variables](http://en.wikipedia.org/wiki/Environment_variable) +// are a universal mechanism for [conveying configuration +// information to Unix programs](http://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]) + } +} diff --git a/go/environment-variables/environment-variables.hash b/go/environment-variables/environment-variables.hash new file mode 100644 index 0000000..e12d01d --- /dev/null +++ b/go/environment-variables/environment-variables.hash @@ -0,0 +1,2 @@ +69d6a768ac84c873ae03b2169ac5cc4cfbc601a6 +gSTxKWLOHRb diff --git a/go/environment-variables/environment-variables.sh b/go/environment-variables/environment-variables.sh new file mode 100644 index 0000000..0969313 --- /dev/null +++ b/go/environment-variables/environment-variables.sh @@ -0,0 +1,20 @@ +# 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 +... + +# 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 +... diff --git a/go/epoch/epoch.go b/go/epoch/epoch.go new file mode 100644 index 0000000..ed7f442 --- /dev/null +++ b/go/epoch/epoch.go @@ -0,0 +1,35 @@ +// A common requirement in programs is getting the number +// of seconds, milliseconds, or nanoseconds since the +// [Unix epoch](http://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` or `UnixNano` to get + // elapsed time since the Unix epoch in seconds or + // nanoseconds, respectively. + now := time.Now() + secs := now.Unix() + nanos := now.UnixNano() + fmt.Println(now) + + // Note that there is no `UnixMillis`, so to get the + // milliseconds since epoch you'll need to manually + // divide from nanoseconds. + millis := nanos / 1000000 + fmt.Println(secs) + fmt.Println(millis) + fmt.Println(nanos) + + // You can also convert integer seconds or nanoseconds + // since the epoch into the corresponding `time`. + fmt.Println(time.Unix(secs, 0)) + fmt.Println(time.Unix(0, nanos)) +} diff --git a/go/epoch/epoch.hash b/go/epoch/epoch.hash new file mode 100644 index 0000000..d37c230 --- /dev/null +++ b/go/epoch/epoch.hash @@ -0,0 +1,2 @@ +184c8f3e94dd28176be81956115ac417e33513a6 +3uYNHHRplmQ diff --git a/go/epoch/epoch.sh b/go/epoch/epoch.sh new file mode 100644 index 0000000..18c97df --- /dev/null +++ b/go/epoch/epoch.sh @@ -0,0 +1,10 @@ +$ 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. diff --git a/go/errors/errors.go b/go/errors/errors.go new file mode 100644 index 0000000..b850139 --- /dev/null +++ b/go/errors/errors.go @@ -0,0 +1,87 @@ +// In Go it's idiomatic to communicate errors via an +// explicit, separate return value. This contrasts with +// the exceptions used in languages like Java and Ruby and +// the overloaded single result / error value sometimes +// used in C. Go's approach makes it easy to see which +// functions return errors and to handle them using the +// same language constructs employed for any other, +// non-error tasks. + +package main + +import ( + "errors" + "fmt" +) + +// By convention, errors are the last return value and +// have type `error`, a built-in interface. +func f1(arg int) (int, error) { + if arg == 42 { + + // `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 +} + +// It's possible to use custom types as `error`s 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. +type argError struct { + arg int + prob string +} + +func (e *argError) Error() string { + return fmt.Sprintf("%d - %s", e.arg, e.prob) +} + +func f2(arg int) (int, error) { + if arg == 42 { + + // In this case we use `&argError` syntax to build + // a new struct, supplying values for the two + // fields `arg` and `prob`. + return -1, &argError{arg, "can't work with it"} + } + return arg + 3, nil +} + +func main() { + + // The two loops below test out each of our + // error-returning functions. Note that the use of an + // inline error check on the `if` line is a common + // idiom in Go code. + for _, i := range []int{7, 42} { + if r, e := f1(i); e != nil { + fmt.Println("f1 failed:", e) + } else { + fmt.Println("f1 worked:", r) + } + } + for _, i := range []int{7, 42} { + if r, e := f2(i); e != nil { + fmt.Println("f2 failed:", e) + } else { + fmt.Println("f2 worked:", r) + } + } + + // If you want to programmatically use the data in + // a custom error, you'll need to get the error as an + // instance of the custom error type via type + // assertion. + _, e := f2(42) + if ae, ok := e.(*argError); ok { + fmt.Println(ae.arg) + fmt.Println(ae.prob) + } +} diff --git a/go/errors/errors.hash b/go/errors/errors.hash new file mode 100644 index 0000000..b650d25 --- /dev/null +++ b/go/errors/errors.hash @@ -0,0 +1,2 @@ +ed58ad3162d93c723d3efe72529a61ce7041fe13 +vrwN32gxaYx diff --git a/go/errors/errors.sh b/go/errors/errors.sh new file mode 100644 index 0000000..068d606 --- /dev/null +++ b/go/errors/errors.sh @@ -0,0 +1,10 @@ +$ go run errors.go +f1 worked: 10 +f1 failed: can't work with 42 +f2 worked: 10 +f2 failed: 42 - can't work with it +42 +can't work with it + +# See this [great post](http://blog.golang.org/2011/07/error-handling-and-go.html) +# on the Go blog for more on error handling. diff --git a/go/execing-processes/execing-processes.go b/go/execing-processes/execing-processes.go new file mode 100644 index 0000000..18eb283 --- /dev/null +++ b/go/execing-processes/execing-processes.go @@ -0,0 +1,50 @@ +// 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 + // apposed to one big string). We'll give `ls` a few + // common arguments. Note that the first argument should + // be the program name. + args := []string{"ls", "-a", "-l", "-h"} + + // `Exec` also needs a set of [environment variables](environment-variables) + // to use. Here we just provide our current + // environment. + env := os.Environ() + + // 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) + } +} diff --git a/go/execing-processes/execing-processes.hash b/go/execing-processes/execing-processes.hash new file mode 100644 index 0000000..020f78e --- /dev/null +++ b/go/execing-processes/execing-processes.hash @@ -0,0 +1,2 @@ +e6b4830d4264f307506b54726ec79b25a0363874 +ahZjpJaZz44 diff --git a/go/execing-processes/execing-processes.sh b/go/execing-processes/execing-processes.sh new file mode 100644 index 0000000..7c3e4c5 --- /dev/null +++ b/go/execing-processes/execing-processes.sh @@ -0,0 +1,11 @@ +# 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`. diff --git a/go/exit/exit.go b/go/exit/exit.go new file mode 100644 index 0000000..578e553 --- /dev/null +++ b/go/exit/exit.go @@ -0,0 +1,24 @@ +// 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`. diff --git a/go/exit/exit.hash b/go/exit/exit.hash new file mode 100644 index 0000000..234717b --- /dev/null +++ b/go/exit/exit.hash @@ -0,0 +1,2 @@ +2316e6c8e364e2066c6bd350dc9b0b2af85b63fe +OX997ykuOGx diff --git a/go/exit/exit.sh b/go/exit/exit.sh new file mode 100644 index 0000000..6418d11 --- /dev/null +++ b/go/exit/exit.sh @@ -0,0 +1,13 @@ +# 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. diff --git a/go/file-paths/file-paths.go b/go/file-paths/file-paths.go new file mode 100644 index 0000000..c001dfd --- /dev/null +++ b/go/file-paths/file-paths.go @@ -0,0 +1,64 @@ +// The `filepath` package provides functions to parse +// and construct *file paths* in a way that is portable +// between operating systems; `dir/file` on Linux vs. +// `dir\file` on Windows, for example. +package main + +import ( + "fmt" + "path/filepath" + "strings" +) + +func main() { + + // `Join` should be used to construct paths in a + // portable way. It takes any number of arguments + // and constructs a hierarchical path from them. + p := filepath.Join("dir1", "dir2", "filename") + fmt.Println("p:", p) + + // You should always use `Join` instead of + // concatenating `/`s or `\`s manually. In addition + // to providing portability, `Join` will also + // normalize paths by removing superfluous separators + // and directory changes. + fmt.Println(filepath.Join("dir1//", "filename")) + fmt.Println(filepath.Join("dir1/../dir1", "filename")) + + // `Dir` and `Base` can be used to split a path to the + // directory and the file. Alternatively, `Split` will + // return both in the same call. + fmt.Println("Dir(p):", filepath.Dir(p)) + fmt.Println("Base(p):", filepath.Base(p)) + + // We can check whether a path is absolute. + fmt.Println(filepath.IsAbs("dir/file")) + fmt.Println(filepath.IsAbs("/dir/file")) + + filename := "config.json" + + // Some file names have extensions following a dot. We + // can split the extension out of such names with `Ext`. + ext := filepath.Ext(filename) + fmt.Println(ext) + + // To find the file's name with the extension removed, + // use `strings.TrimSuffix`. + fmt.Println(strings.TrimSuffix(filename, ext)) + + // `Rel` finds a relative path between a *base* and a + // *target*. It returns an error if the target cannot + // be made relative to base. + rel, err := filepath.Rel("a/b", "a/b/t/file") + if err != nil { + panic(err) + } + fmt.Println(rel) + + rel, err = filepath.Rel("a/b", "a/c/t/file") + if err != nil { + panic(err) + } + fmt.Println(rel) +} diff --git a/go/file-paths/file-paths.hash b/go/file-paths/file-paths.hash new file mode 100644 index 0000000..f1610a1 --- /dev/null +++ b/go/file-paths/file-paths.hash @@ -0,0 +1,2 @@ +1215302b9e59ee9dee21dcd3c47d5f6c672fb058 +QIitbMNiFRx diff --git a/go/file-paths/file-paths.sh b/go/file-paths/file-paths.sh new file mode 100644 index 0000000..4ef4251 --- /dev/null +++ b/go/file-paths/file-paths.sh @@ -0,0 +1,12 @@ +$ go run file-paths.go +p: dir1/dir2/filename +dir1/filename +dir1/filename +Dir(p): dir1/dir2 +Base(p): filename +false +true +.json +config +t/file +../c/t/file diff --git a/go/for/for.go b/go/for/for.go new file mode 100644 index 0000000..7c4aa04 --- /dev/null +++ b/go/for/for.go @@ -0,0 +1,38 @@ +// `for` is Go's only looping construct. Here are +// three 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 := 7; j <= 9; j++ { + fmt.Println(j) + } + + // `for` without a condition will loop repeatedly + // until you `break` out of the loop or `return` from + // the enclosing function. + for { + fmt.Println("loop") + break + } + + // You can also `continue` to the next iteration of + // the loop. + for n := 0; n <= 5; n++ { + if n%2 == 0 { + continue + } + fmt.Println(n) + } +} diff --git a/go/for/for.hash b/go/for/for.hash new file mode 100644 index 0000000..7852dec --- /dev/null +++ b/go/for/for.hash @@ -0,0 +1,2 @@ +33056d6b36f9894fb6359c9cf2ef8725bbdafa19 +lGYfUJwiGfi diff --git a/go/for/for.sh b/go/for/for.sh new file mode 100644 index 0000000..12785eb --- /dev/null +++ b/go/for/for.sh @@ -0,0 +1,15 @@ +$ go run for.go +1 +2 +3 +7 +8 +9 +loop +1 +3 +5 + +# We'll see some other `for` forms later when we look at +# `range` statements, channels, and other data +# structures. diff --git a/go/functions/functions.go b/go/functions/functions.go new file mode 100644 index 0000000..bf3e844 --- /dev/null +++ b/go/functions/functions.go @@ -0,0 +1,35 @@ +// _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) +} diff --git a/go/functions/functions.hash b/go/functions/functions.hash new file mode 100644 index 0000000..85d4e8f --- /dev/null +++ b/go/functions/functions.hash @@ -0,0 +1,2 @@ +ae669923c20e5ebf4a7b4b11b8fdf2972accf9e2 +hzGUvK6iJNm diff --git a/go/functions/functions.sh b/go/functions/functions.sh new file mode 100644 index 0000000..cc8ff15 --- /dev/null +++ b/go/functions/functions.sh @@ -0,0 +1,6 @@ +$ 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. diff --git a/go/goroutines/goroutines.go b/go/goroutines/goroutines.go new file mode 100644 index 0000000..4943ec9 --- /dev/null +++ b/go/goroutines/goroutines.go @@ -0,0 +1,39 @@ +// A _goroutine_ is a lightweight thread of execution. + +package main + +import ( + "fmt" + "time" +) + +func f(from string) { + for i := 0; i < 3; i++ { + fmt.Println(from, ":", i) + } +} + +func main() { + + // Suppose we have a function call `f(s)`. Here's how + // we'd call that in the usual way, running it + // synchronously. + f("direct") + + // 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") +} diff --git a/go/goroutines/goroutines.hash b/go/goroutines/goroutines.hash new file mode 100644 index 0000000..a83b7b8 --- /dev/null +++ b/go/goroutines/goroutines.hash @@ -0,0 +1,2 @@ +3737e7b5129b649d202e75225a1ac732eda116d0 +rovAFf9-n78 diff --git a/go/goroutines/goroutines.sh b/go/goroutines/goroutines.sh new file mode 100644 index 0000000..1d42ee2 --- /dev/null +++ b/go/goroutines/goroutines.sh @@ -0,0 +1,16 @@ +# When we run this program, we see the output of the +# blocking call first, then the interleaved output of the +# two goroutines. This interleaving reflects the +# goroutines being run concurrently by the Go runtime. +$ go run goroutines.go +direct : 0 +direct : 1 +direct : 2 +goroutine : 0 +going +goroutine : 1 +goroutine : 2 +done + +# Next we'll look at a complement to goroutines in +# concurrent Go programs: channels. diff --git a/go/hello-world/hello-world.go b/go/hello-world/hello-world.go new file mode 100644 index 0000000..4db5bfb --- /dev/null +++ b/go/hello-world/hello-world.go @@ -0,0 +1,9 @@ +// 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") +} diff --git a/go/hello-world/hello-world.hash b/go/hello-world/hello-world.hash new file mode 100644 index 0000000..df7b0e0 --- /dev/null +++ b/go/hello-world/hello-world.hash @@ -0,0 +1,2 @@ +c98395a44701add5bf84e2f3a63e300fc1bc4bfe +mp1ENMU6ZYu diff --git a/go/hello-world/hello-world.sh b/go/hello-world/hello-world.sh new file mode 100644 index 0000000..5e62132 --- /dev/null +++ b/go/hello-world/hello-world.sh @@ -0,0 +1,17 @@ +# 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. diff --git a/go/http-clients/http-clients.go b/go/http-clients/http-clients.go new file mode 100644 index 0000000..7cd71ef --- /dev/null +++ b/go/http-clients/http-clients.go @@ -0,0 +1,38 @@ +// The Go standard library comes with excellent support +// for HTTP clients and servers in the `net/http` +// package. In this example we'll use it to issue simple +// HTTP requests. +package main + +import ( + "bufio" + "fmt" + "net/http" +) + +func main() { + + // Issue an HTTP GET request to a server. `http.Get` is a + // convenient shortcut around creating an `http.Client` + // object and calling its `Get` method; it uses the + // `http.DefaultClient` object which has useful default + // settings. + resp, err := http.Get("http://gobyexample.com") + if err != nil { + panic(err) + } + defer resp.Body.Close() + + // Print the HTTP response status. + fmt.Println("Response status:", resp.Status) + + // Print the first 5 lines of the response body. + scanner := bufio.NewScanner(resp.Body) + for i := 0; scanner.Scan() && i < 5; i++ { + fmt.Println(scanner.Text()) + } + + if err := scanner.Err(); err != nil { + panic(err) + } +} diff --git a/go/http-clients/http-clients.hash b/go/http-clients/http-clients.hash new file mode 100644 index 0000000..e550dd5 --- /dev/null +++ b/go/http-clients/http-clients.hash @@ -0,0 +1,2 @@ +ec8fd69aa19e54a7ea05d2a911f09d3a98f0396c +VxYIifr_UuH diff --git a/go/http-clients/http-clients.sh b/go/http-clients/http-clients.sh new file mode 100644 index 0000000..afbbb77 --- /dev/null +++ b/go/http-clients/http-clients.sh @@ -0,0 +1,7 @@ +$ go run http-clients.go +Response status: 200 OK + + + + + Go by Example diff --git a/go/http-servers/http-servers.go b/go/http-servers/http-servers.go new file mode 100644 index 0000000..42a83b3 --- /dev/null +++ b/go/http-servers/http-servers.go @@ -0,0 +1,50 @@ +// Writing a basic HTTP server is easy using the +// `net/http` package. +package main + +import ( + "fmt" + "net/http" +) + +// A fundamental concept in `net/http` servers is +// *handlers*. A handler is an object implementing the +// `http.Handler` interface. A common way to write +// a handler is by using the `http.HandlerFunc` adapter +// on functions with the appropriate signature. +func hello(w http.ResponseWriter, req *http.Request) { + + // Functions serving as handlers take a + // `http.ResponseWriter` and a `http.Request` as + // arguments. The response writer is used to fill in the + // HTTP response. Here our simple response is just + // "hello\n". + fmt.Fprintf(w, "hello\n") +} + +func headers(w http.ResponseWriter, req *http.Request) { + + // This handler does something a little more + // sophisticated by reading all the HTTP request + // headers and echoing them into the response body. + for name, headers := range req.Header { + for _, h := range headers { + fmt.Fprintf(w, "%v: %v\n", name, h) + } + } +} + +func main() { + + // We register our handlers on server routes using the + // `http.HandleFunc` convenience function. It sets up + // the *default router* in the `net/http` package and + // takes a function as an argument. + http.HandleFunc("/hello", hello) + http.HandleFunc("/headers", headers) + + // Finally, we call the `ListenAndServe` with the port + // and a handler. `nil` tells it to use the default + // router we've just set up. + http.ListenAndServe(":8090", nil) +} diff --git a/go/http-servers/http-servers.hash b/go/http-servers/http-servers.hash new file mode 100644 index 0000000..a491ac1 --- /dev/null +++ b/go/http-servers/http-servers.hash @@ -0,0 +1,2 @@ +a4e8d30b7a6f3a6abd96b916d81ce5930bad94f9 +lNuS9ysZmxH diff --git a/go/http-servers/http-servers.sh b/go/http-servers/http-servers.sh new file mode 100644 index 0000000..2db5f45 --- /dev/null +++ b/go/http-servers/http-servers.sh @@ -0,0 +1,6 @@ +# Run the server in the background. +$ go run http-servers.go & + +# Access the `/hello` route. +$ curl localhost:8090/hello +hello diff --git a/go/if-else/if-else.go b/go/if-else/if-else.go new file mode 100644 index 0000000..1f2a403 --- /dev/null +++ b/go/if-else/if-else.go @@ -0,0 +1,35 @@ +// 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") + } + + // A statement can precede conditionals; any variables + // declared in this statement are available in all + // branches. + if num := 9; num < 0 { + fmt.Println(num, "is negative") + } else if num < 10 { + fmt.Println(num, "has 1 digit") + } else { + fmt.Println(num, "has multiple digits") + } +} + +// Note that you don't need parentheses around conditions +// in Go, but that the braces are required. diff --git a/go/if-else/if-else.hash b/go/if-else/if-else.hash new file mode 100644 index 0000000..844700f --- /dev/null +++ b/go/if-else/if-else.hash @@ -0,0 +1,2 @@ +89b78f3378e1a574ddfd0260a0404a962852eff8 +p6-WKTqEks4 diff --git a/go/if-else/if-else.sh b/go/if-else/if-else.sh new file mode 100644 index 0000000..13c363f --- /dev/null +++ b/go/if-else/if-else.sh @@ -0,0 +1,8 @@ +$ go run if-else.go +7 is odd +8 is divisible by 4 +9 has 1 digit + +# There is no [ternary if](http://en.wikipedia.org/wiki/%3F:) +# in Go, so you'll need to use a full `if` statement even +# for basic conditions. diff --git a/go/interfaces/interfaces.go b/go/interfaces/interfaces.go new file mode 100644 index 0000000..806ffa7 --- /dev/null +++ b/go/interfaces/interfaces.go @@ -0,0 +1,64 @@ +// _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()) +} + +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) +} diff --git a/go/interfaces/interfaces.hash b/go/interfaces/interfaces.hash new file mode 100644 index 0000000..b6fa983 --- /dev/null +++ b/go/interfaces/interfaces.hash @@ -0,0 +1,2 @@ +aac1328f5a04568272b82753ff0762b9eacff4fc +hXTlbUAGcvn diff --git a/go/interfaces/interfaces.sh b/go/interfaces/interfaces.sh new file mode 100644 index 0000000..f800e3a --- /dev/null +++ b/go/interfaces/interfaces.sh @@ -0,0 +1,10 @@ +$ go run interfaces.go +{3 4} +12 +14 +{5} +78.53981633974483 +31.41592653589793 + +# To learn more about Go's interfaces, check out this +# [great blog post](http://jordanorelli.tumblr.com/post/32665860244/how-to-use-interfaces-in-go). diff --git a/go/json/json.go b/go/json/json.go new file mode 100644 index 0000000..2594786 --- /dev/null +++ b/go/json/json.go @@ -0,0 +1,124 @@ +// 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" +) + +// 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) +} diff --git a/go/json/json.hash b/go/json/json.hash new file mode 100644 index 0000000..8c217d6 --- /dev/null +++ b/go/json/json.hash @@ -0,0 +1,2 @@ +c751bc7223b8bc615f82fe7643ab98ce2b80240f +ROikmz5tRhZ diff --git a/go/json/json.sh b/go/json/json.sh new file mode 100644 index 0000000..e9d0c9f --- /dev/null +++ b/go/json/json.sh @@ -0,0 +1,21 @@ +$ 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} + + +# We've covered the basic of JSON in Go here, but check +# out the [JSON and Go](http://blog.golang.org/2011/01/json-and-go.html) +# blog post and [JSON package docs](http://golang.org/pkg/encoding/json/) +# for more. diff --git a/go/line-filters/line-filters.go b/go/line-filters/line-filters.go new file mode 100644 index 0000000..abcdf96 --- /dev/null +++ b/go/line-filters/line-filters.go @@ -0,0 +1,41 @@ +// 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) + } +} diff --git a/go/line-filters/line-filters.hash b/go/line-filters/line-filters.hash new file mode 100644 index 0000000..d62fbff --- /dev/null +++ b/go/line-filters/line-filters.hash @@ -0,0 +1,2 @@ +87f4a67edf741979f8ff6da85947aa177547f9ef +hnaOIaQAjKF diff --git a/go/line-filters/line-filters.sh b/go/line-filters/line-filters.sh new file mode 100644 index 0000000..5cb5450 --- /dev/null +++ b/go/line-filters/line-filters.sh @@ -0,0 +1,9 @@ +# 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 diff --git a/go/maps/maps.go b/go/maps/maps.go new file mode 100644 index 0000000..3346270 --- /dev/null +++ b/go/maps/maps.go @@ -0,0 +1,50 @@ +// _Maps_ are Go's built-in [associative data type](http://en.wikipedia.org/wiki/Associative_array) +// (sometimes called _hashes_ or _dicts_ in other languages). + +package main + +import "fmt" + +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) + + // The builtin `len` returns the number of key/value + // pairs when called on a map. + fmt.Println("len:", len(m)) + + // The builtin `delete` removes key/value pairs from + // a map. + delete(m, "k2") + fmt.Println("map:", m) + + // The 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) +} diff --git a/go/maps/maps.hash b/go/maps/maps.hash new file mode 100644 index 0000000..57c1a72 --- /dev/null +++ b/go/maps/maps.hash @@ -0,0 +1,2 @@ +3e39d07e3f80ecbac558c6fb8baee2a5f914cf97 +U67R66Oab8r diff --git a/go/maps/maps.sh b/go/maps/maps.sh new file mode 100644 index 0000000..da7a841 --- /dev/null +++ b/go/maps/maps.sh @@ -0,0 +1,9 @@ +# 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 +len: 2 +map: map[k1:7] +prs: false +map: map[bar:2 foo:1] diff --git a/go/methods/methods.go b/go/methods/methods.go new file mode 100644 index 0000000..e56cb6f --- /dev/null +++ b/go/methods/methods.go @@ -0,0 +1,37 @@ +// 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()) +} diff --git a/go/methods/methods.hash b/go/methods/methods.hash new file mode 100644 index 0000000..00c7c28 --- /dev/null +++ b/go/methods/methods.hash @@ -0,0 +1,2 @@ +24cfb9ad45e43c2d49163149bc55925a4e1b3c7a +ffMb0txGnYB diff --git a/go/methods/methods.sh b/go/methods/methods.sh new file mode 100644 index 0000000..d646ae9 --- /dev/null +++ b/go/methods/methods.sh @@ -0,0 +1,8 @@ +$ 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. diff --git a/go/multiple-return-values/multiple-return-values.go b/go/multiple-return-values/multiple-return-values.go new file mode 100644 index 0000000..cb541c4 --- /dev/null +++ b/go/multiple-return-values/multiple-return-values.go @@ -0,0 +1,27 @@ +// 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) +} diff --git a/go/multiple-return-values/multiple-return-values.hash b/go/multiple-return-values/multiple-return-values.hash new file mode 100644 index 0000000..73f9fa8 --- /dev/null +++ b/go/multiple-return-values/multiple-return-values.hash @@ -0,0 +1,2 @@ +5063ce3d3c70c6bd70f4b709de24bb93d0f24e0c +FZoIB5LXQGZ diff --git a/go/multiple-return-values/multiple-return-values.sh b/go/multiple-return-values/multiple-return-values.sh new file mode 100644 index 0000000..0d4c599 --- /dev/null +++ b/go/multiple-return-values/multiple-return-values.sh @@ -0,0 +1,7 @@ +$ 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. diff --git a/go/mutexes/mutexes.go b/go/mutexes/mutexes.go new file mode 100644 index 0000000..79b0c31 --- /dev/null +++ b/go/mutexes/mutexes.go @@ -0,0 +1,85 @@ +// In the previous example we saw how to manage simple +// counter state using [atomic operations](atomic-counters). +// For more complex state we can use a [mutex](http://en.wikipedia.org/wiki/Mutual_exclusion) +// to safely access data across multiple goroutines. + +package main + +import ( + "fmt" + "math/rand" + "sync" + "sync/atomic" + "time" +) + +func main() { + + // For our example the `state` will be a map. + var state = make(map[int]int) + + // This `mutex` will synchronize access to `state`. + var mutex = &sync.Mutex{} + + // We'll keep track of how many read and write + // operations we do. + var readOps uint64 + var writeOps uint64 + + // Here we start 100 goroutines to execute repeated + // reads against the state, once per millisecond in + // each goroutine. + for r := 0; r < 100; r++ { + go func() { + total := 0 + for { + + // For each read we pick a key to access, + // `Lock()` the `mutex` to ensure + // exclusive access to the `state`, read + // the value at the chosen key, + // `Unlock()` the mutex, and increment + // the `readOps` count. + key := rand.Intn(5) + mutex.Lock() + total += state[key] + mutex.Unlock() + atomic.AddUint64(&readOps, 1) + + // Wait a bit between reads. + time.Sleep(time.Millisecond) + } + }() + } + + // We'll also start 10 goroutines to simulate writes, + // using the same pattern we did for reads. + for w := 0; w < 10; w++ { + go func() { + for { + key := rand.Intn(5) + val := rand.Intn(100) + mutex.Lock() + state[key] = val + mutex.Unlock() + atomic.AddUint64(&writeOps, 1) + time.Sleep(time.Millisecond) + } + }() + } + + // Let the 10 goroutines work on the `state` and + // `mutex` for a second. + time.Sleep(time.Second) + + // Take and report final operation counts. + readOpsFinal := atomic.LoadUint64(&readOps) + fmt.Println("readOps:", readOpsFinal) + writeOpsFinal := atomic.LoadUint64(&writeOps) + fmt.Println("writeOps:", writeOpsFinal) + + // With a final lock of `state`, show how it ended up. + mutex.Lock() + fmt.Println("state:", state) + mutex.Unlock() +} diff --git a/go/mutexes/mutexes.hash b/go/mutexes/mutexes.hash new file mode 100644 index 0000000..aea2025 --- /dev/null +++ b/go/mutexes/mutexes.hash @@ -0,0 +1,2 @@ +ca257d9594a6219d5803193132e999a32dc8c856 +IRewFKz2OPN diff --git a/go/mutexes/mutexes.sh b/go/mutexes/mutexes.sh new file mode 100644 index 0000000..185f4fa --- /dev/null +++ b/go/mutexes/mutexes.sh @@ -0,0 +1,10 @@ +# Running the program shows that we executed about +# 90,000 total operations against our `mutex`-synchronized +# `state`. +$ go run mutexes.go +readOps: 83285 +writeOps: 8320 +state: map[1:97 4:53 0:33 2:15 3:2] + +# Next we'll look at implementing this same state +# management task using only goroutines and channels. diff --git a/go/non-blocking-channel-operations/non-blocking-channel-operations.go b/go/non-blocking-channel-operations/non-blocking-channel-operations.go new file mode 100644 index 0000000..2429da1 --- /dev/null +++ b/go/non-blocking-channel-operations/non-blocking-channel-operations.go @@ -0,0 +1,49 @@ +// 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") + } +} diff --git a/go/non-blocking-channel-operations/non-blocking-channel-operations.hash b/go/non-blocking-channel-operations/non-blocking-channel-operations.hash new file mode 100644 index 0000000..a9f03de --- /dev/null +++ b/go/non-blocking-channel-operations/non-blocking-channel-operations.hash @@ -0,0 +1,2 @@ +a6e0a8bb87153c7ed0de4996172f7ad5d89c6814 +n5ttmOsMrrJ diff --git a/go/non-blocking-channel-operations/non-blocking-channel-operations.sh b/go/non-blocking-channel-operations/non-blocking-channel-operations.sh new file mode 100644 index 0000000..fe85dff --- /dev/null +++ b/go/non-blocking-channel-operations/non-blocking-channel-operations.sh @@ -0,0 +1,4 @@ +$ go run non-blocking-channel-operations.go +no message received +no message sent +no activity diff --git a/go/number-parsing/number-parsing.go b/go/number-parsing/number-parsing.go new file mode 100644 index 0000000..6371637 --- /dev/null +++ b/go/number-parsing/number-parsing.go @@ -0,0 +1,42 @@ +// 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) +} diff --git a/go/number-parsing/number-parsing.hash b/go/number-parsing/number-parsing.hash new file mode 100644 index 0000000..eeaf3cd --- /dev/null +++ b/go/number-parsing/number-parsing.hash @@ -0,0 +1,2 @@ +0191c7e43706640207c403ba92dd2272d66fc868 +t2q4KnWWTAw diff --git a/go/number-parsing/number-parsing.sh b/go/number-parsing/number-parsing.sh new file mode 100644 index 0000000..668c76a --- /dev/null +++ b/go/number-parsing/number-parsing.sh @@ -0,0 +1,9 @@ +$ 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. diff --git a/go/panic/panic.go b/go/panic/panic.go new file mode 100644 index 0000000..27e72f0 --- /dev/null +++ b/go/panic/panic.go @@ -0,0 +1,25 @@ +// 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" + +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. + _, err := os.Create("/tmp/file") + if err != nil { + panic(err) + } +} diff --git a/go/panic/panic.hash b/go/panic/panic.hash new file mode 100644 index 0000000..bd32ff3 --- /dev/null +++ b/go/panic/panic.hash @@ -0,0 +1,2 @@ +91639bbcfcc6ed088295a9ee6b1c36ab35ae402a +91HXbZZZopt diff --git a/go/panic/panic.sh b/go/panic/panic.sh new file mode 100644 index 0000000..a851be9 --- /dev/null +++ b/go/panic/panic.sh @@ -0,0 +1,15 @@ +# Running this program will cause it to panic, print +# an error message and goroutine traces, and exit with +# a non-zero status. +$ go run panic.go +panic: a problem + +goroutine 1 [running]: +main.main() + /.../panic.go:12 +0x47 +... +exit status 2 + +# Note that unlike some languages which use exceptions +# for handling of many errors, in Go it is idiomatic +# to use error-indicating return values wherever possible. diff --git a/go/pointers/pointers.go b/go/pointers/pointers.go new file mode 100644 index 0000000..5574997 --- /dev/null +++ b/go/pointers/pointers.go @@ -0,0 +1,42 @@ +// 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) +} diff --git a/go/pointers/pointers.hash b/go/pointers/pointers.hash new file mode 100644 index 0000000..a0c5985 --- /dev/null +++ b/go/pointers/pointers.hash @@ -0,0 +1,2 @@ +85cff3345d2f22b65a5d54eb8f7aa8f508f27887 +fnQkHp4hriG diff --git a/go/pointers/pointers.sh b/go/pointers/pointers.sh new file mode 100644 index 0000000..11ca0f0 --- /dev/null +++ b/go/pointers/pointers.sh @@ -0,0 +1,8 @@ +# `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 diff --git a/go/random-numbers/random-numbers.go b/go/random-numbers/random-numbers.go new file mode 100644 index 0000000..ebb6a8c --- /dev/null +++ b/go/random-numbers/random-numbers.go @@ -0,0 +1,56 @@ +// Go's `math/rand` package provides +// [pseudorandom number](http://en.wikipedia.org/wiki/Pseudorandom_number_generator) +// generation. + +package main + +import ( + "fmt" + "math/rand" + "time" +) + +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() + + // The default number generator is deterministic, so it'll + // produce the same sequence of numbers each time by default. + // To produce varying sequences, give it a seed that changes. + // Note that this is not safe to use for random numbers you + // intend to be secret, use `crypto/rand` for those. + s1 := rand.NewSource(time.Now().UnixNano()) + r1 := rand.New(s1) + + // Call the resulting `rand.Rand` just like the + // functions on the `rand` package. + fmt.Print(r1.Intn(100), ",") + fmt.Print(r1.Intn(100)) + fmt.Println() + + // If you seed a source with the same number, it + // produces the same sequence of random numbers. + s2 := rand.NewSource(42) + r2 := rand.New(s2) + fmt.Print(r2.Intn(100), ",") + fmt.Print(r2.Intn(100)) + fmt.Println() + s3 := rand.NewSource(42) + r3 := rand.New(s3) + fmt.Print(r3.Intn(100), ",") + fmt.Print(r3.Intn(100)) +} diff --git a/go/random-numbers/random-numbers.hash b/go/random-numbers/random-numbers.hash new file mode 100644 index 0000000..3ebbc4a --- /dev/null +++ b/go/random-numbers/random-numbers.hash @@ -0,0 +1,2 @@ +9374869a809d28ea784a9e1181b4aa1988018776 +DVHO7SjJZnp diff --git a/go/random-numbers/random-numbers.sh b/go/random-numbers/random-numbers.sh new file mode 100644 index 0000000..67c37a8 --- /dev/null +++ b/go/random-numbers/random-numbers.sh @@ -0,0 +1,12 @@ +$ go run random-numbers.go +81,87 +0.6645600532184904 +7.123187485356329,8.434115364335547 +0,28 +5,87 +5,87 + + +# See the [`math/rand`](http://golang.org/pkg/math/rand/) +# package docs for references on other random quantities +# that Go can provide. diff --git a/go/range-over-channels/range-over-channels.go b/go/range-over-channels/range-over-channels.go new file mode 100644 index 0000000..d8aa729 --- /dev/null +++ b/go/range-over-channels/range-over-channels.go @@ -0,0 +1,25 @@ +// In a [previous](range) 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) + } +} diff --git a/go/range-over-channels/range-over-channels.hash b/go/range-over-channels/range-over-channels.hash new file mode 100644 index 0000000..f0ffe62 --- /dev/null +++ b/go/range-over-channels/range-over-channels.hash @@ -0,0 +1,2 @@ +8b5d8a77e84c34771c5b14af014ecef3f88b2a6c +QnARPm-ddFB diff --git a/go/range-over-channels/range-over-channels.sh b/go/range-over-channels/range-over-channels.sh new file mode 100644 index 0000000..feea741 --- /dev/null +++ b/go/range-over-channels/range-over-channels.sh @@ -0,0 +1,7 @@ +$ 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. diff --git a/go/range/range.go b/go/range/range.go new file mode 100644 index 0000000..011af67 --- /dev/null +++ b/go/range/range.go @@ -0,0 +1,48 @@ +// _range_ iterates over elements in a variety of 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. + for i, c := range "go" { + fmt.Println(i, c) + } +} diff --git a/go/range/range.hash b/go/range/range.hash new file mode 100644 index 0000000..2f8d0da --- /dev/null +++ b/go/range/range.hash @@ -0,0 +1,2 @@ +ebe328a57f3d34708709ca99d3304af1733592d9 +JTY1VAUjfBw diff --git a/go/range/range.sh b/go/range/range.sh new file mode 100644 index 0000000..e3f40d8 --- /dev/null +++ b/go/range/range.sh @@ -0,0 +1,9 @@ +$ go run range.go +sum: 9 +index: 1 +a -> apple +b -> banana +key: a +key: b +0 103 +1 111 diff --git a/go/rate-limiting/rate-limiting.go b/go/rate-limiting/rate-limiting.go new file mode 100644 index 0000000..c32483d --- /dev/null +++ b/go/rate-limiting/rate-limiting.go @@ -0,0 +1,71 @@ +// [Rate limiting](http://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 i := 0; i < 3; i++ { + 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()) + } +} diff --git a/go/rate-limiting/rate-limiting.hash b/go/rate-limiting/rate-limiting.hash new file mode 100644 index 0000000..be48802 --- /dev/null +++ b/go/rate-limiting/rate-limiting.hash @@ -0,0 +1,2 @@ +357d83df3e48675eb1e135188cb9f07448c1f146 +AJ-MJephNib diff --git a/go/rate-limiting/rate-limiting.sh b/go/rate-limiting/rate-limiting.sh new file mode 100644 index 0000000..a01a130 --- /dev/null +++ b/go/rate-limiting/rate-limiting.sh @@ -0,0 +1,17 @@ +# 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 diff --git a/go/reading-files/reading-files.go b/go/reading-files/reading-files.go new file mode 100644 index 0000000..49387a6 --- /dev/null +++ b/go/reading-files/reading-files.go @@ -0,0 +1,84 @@ +// Reading and writing files are basic tasks needed for +// many Go programs. First we'll look at some examples of +// reading files. + +package main + +import ( + "bufio" + "fmt" + "io" + "io/ioutil" + "os" +) + +// Reading files requires checking most calls for errors. +// This helper will streamline our error checks below. +func check(e error) { + if e != nil { + panic(e) + } +} + +func main() { + + // Perhaps the most basic file reading task is + // slurping a file's entire contents into memory. + dat, err := ioutil.ReadFile("/tmp/dat") + check(err) + fmt.Print(string(dat)) + + // You'll often want more control over how and what + // parts of a file are read. For these tasks, start + // by `Open`ing a file to obtain an `os.File` value. + f, err := os.Open("/tmp/dat") + 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, 0) + 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])) + + // 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, 0) + check(err) + b3 := make([]byte, 2) + n3, err := io.ReadAtLeast(f, b3, 2) + check(err) + fmt.Printf("%d bytes @ %d: %s\n", n3, o3, string(b3)) + + // There is no built-in rewind, but `Seek(0, 0)` + // accomplishes this. + _, err = f.Seek(0, 0) + 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() +} diff --git a/go/reading-files/reading-files.hash b/go/reading-files/reading-files.hash new file mode 100644 index 0000000..8cf5bf6 --- /dev/null +++ b/go/reading-files/reading-files.hash @@ -0,0 +1,2 @@ +463a6f2999a023887af6b23c8f79f24978eb8115 +cocJ6kBH_iZ diff --git a/go/reading-files/reading-files.sh b/go/reading-files/reading-files.sh new file mode 100644 index 0000000..a1deb72 --- /dev/null +++ b/go/reading-files/reading-files.sh @@ -0,0 +1,11 @@ +$ 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. diff --git a/go/recursion/recursion.go b/go/recursion/recursion.go new file mode 100644 index 0000000..a88b1e5 --- /dev/null +++ b/go/recursion/recursion.go @@ -0,0 +1,20 @@ +// Go supports +// recursive functions. +// Here's a classic factorial 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)) +} diff --git a/go/recursion/recursion.hash b/go/recursion/recursion.hash new file mode 100644 index 0000000..2ebf561 --- /dev/null +++ b/go/recursion/recursion.hash @@ -0,0 +1,2 @@ +5d1ba6b03a50ccae2a0f46865eb72c587e11857c +4yUp5wLVyiG diff --git a/go/recursion/recursion.sh b/go/recursion/recursion.sh new file mode 100644 index 0000000..53b8e15 --- /dev/null +++ b/go/recursion/recursion.sh @@ -0,0 +1,2 @@ +$ go run recursion.go +5040 diff --git a/go/regular-expressions/regular-expressions.go b/go/regular-expressions/regular-expressions.go new file mode 100644 index 0000000..52ec06d --- /dev/null +++ b/go/regular-expressions/regular-expressions.go @@ -0,0 +1,83 @@ +// Go offers built-in support for [regular expressions](http://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(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(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 constants with regular expressions + // you can use the `MustCompile` variation of + // `Compile`. A plain `Compile` won't work for + // constants because it has 2 return values. + r = regexp.MustCompile("p([a-z]+)ch") + fmt.Println(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)) +} diff --git a/go/regular-expressions/regular-expressions.hash b/go/regular-expressions/regular-expressions.hash new file mode 100644 index 0000000..236706c --- /dev/null +++ b/go/regular-expressions/regular-expressions.hash @@ -0,0 +1,2 @@ +de24265897edf1d3913e3b87f70757284a66ecea +urHlUNDVenk diff --git a/go/regular-expressions/regular-expressions.sh b/go/regular-expressions/regular-expressions.sh new file mode 100644 index 0000000..1f3882c --- /dev/null +++ b/go/regular-expressions/regular-expressions.sh @@ -0,0 +1,17 @@ +$ go run regular-expressions.go +true +true +peach +[0 5] +[peach ea] +[0 5 1 3] +[peach punch pinch] +[[0 5 1 3] [6 11 7 9] [12 17 13 15]] +[peach punch] +true +p([a-z]+)ch +a +a PEACH + +# For a complete reference on Go regular expressions check +# the [`regexp`](http://golang.org/pkg/regexp/) package docs. diff --git a/go/select/select.go b/go/select/select.go new file mode 100644 index 0000000..d2ce16e --- /dev/null +++ b/go/select/select.go @@ -0,0 +1,40 @@ +// 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 i := 0; i < 2; i++ { + select { + case msg1 := <-c1: + fmt.Println("received", msg1) + case msg2 := <-c2: + fmt.Println("received", msg2) + } + } +} diff --git a/go/select/select.hash b/go/select/select.hash new file mode 100644 index 0000000..ea97b26 --- /dev/null +++ b/go/select/select.hash @@ -0,0 +1,2 @@ +6e1125087bc036ebd905452300575f160d683918 +yF-xgN7Xf9P diff --git a/go/select/select.sh b/go/select/select.sh new file mode 100644 index 0000000..2895b86 --- /dev/null +++ b/go/select/select.sh @@ -0,0 +1,10 @@ +# 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 diff --git a/go/sha1-hashes/sha1-hashes.go b/go/sha1-hashes/sha1-hashes.go new file mode 100644 index 0000000..24e5921 --- /dev/null +++ b/go/sha1-hashes/sha1-hashes.go @@ -0,0 +1,39 @@ +// [_SHA1 hashes_](http://en.wikipedia.org/wiki/SHA-1) are +// frequently used to compute short identities for binary +// or text blobs. For example, the [git revision control +// system](http://git-scm.com/) uses SHA1s extensively to +// identify versioned files and directories. Here's how to +// compute SHA1 hashes in Go. + +package main + +// Go implements several hash functions in various +// `crypto/*` packages. +import ( + "crypto/sha1" + "fmt" +) + +func main() { + s := "sha1 this string" + + // The pattern for generating a hash is `sha1.New()`, + // `sha1.Write(bytes)`, then `sha1.Sum([]byte{})`. + // Here we start with a new hash. + h := sha1.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) + + // SHA1 values are often printed in hex, for example + // in git commits. Use the `%x` format verb to convert + // a hash results to a hex string. + fmt.Println(s) + fmt.Printf("%x\n", bs) +} diff --git a/go/sha1-hashes/sha1-hashes.hash b/go/sha1-hashes/sha1-hashes.hash new file mode 100644 index 0000000..5af2939 --- /dev/null +++ b/go/sha1-hashes/sha1-hashes.hash @@ -0,0 +1,2 @@ +4cda643ba233014fe6b30966c37d4d0fcd4edbe8 +oqcrTfY4Ykd diff --git a/go/sha1-hashes/sha1-hashes.sh b/go/sha1-hashes/sha1-hashes.sh new file mode 100644 index 0000000..3ed62c9 --- /dev/null +++ b/go/sha1-hashes/sha1-hashes.sh @@ -0,0 +1,14 @@ +# Running the program computes the hash and prints it in +# a human-readable hex format. +$ go run sha1-hashes.go +sha1 this string +cf23df2207d99a74fbe169e3eba035e633b65d94 + + +# You can compute other hashes using a similar pattern to +# the one shown above. For example, to compute MD5 hashes +# import `crypto/md5` and use `md5.New()`. + +# Note that if you need cryptographically secure hashes, +# you should carefully research +# [hash strength](http://en.wikipedia.org/wiki/Cryptographic_hash_function)! diff --git a/go/signals/signals.go b/go/signals/signals.go new file mode 100644 index 0000000..4cd373b --- /dev/null +++ b/go/signals/signals.go @@ -0,0 +1,46 @@ +// Sometimes we'd like our Go programs to intelligently +// handle [Unix signals](http://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 how to handle signals in Go with channels. + +package main + +import ( + "fmt" + "os" + "os/signal" + "syscall" +) + +func main() { + + // Go signal notification works by sending `os.Signal` + // values on a channel. We'll create a channel to + // receive these notifications (we'll also make one to + // notify us when the program can exit). + sigs := make(chan os.Signal, 1) + done := make(chan bool, 1) + + // `signal.Notify` registers the given channel to + // receive notifications of the specified signals. + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + + // This goroutine executes a blocking receive for + // signals. When it gets one it'll print it out + // and then notify the program that it can finish. + go func() { + sig := <-sigs + fmt.Println() + fmt.Println(sig) + done <- true + }() + + // The program will wait here until it gets the + // expected signal (as indicated by the goroutine + // above sending a value on `done`) and then exit. + fmt.Println("awaiting signal") + <-done + fmt.Println("exiting") +} diff --git a/go/signals/signals.hash b/go/signals/signals.hash new file mode 100644 index 0000000..82345ca --- /dev/null +++ b/go/signals/signals.hash @@ -0,0 +1,2 @@ +1e43c6f63f1d57e1a52c89f52d35b68757e9676b +_6oj-T3Gko2 diff --git a/go/signals/signals.sh b/go/signals/signals.sh new file mode 100644 index 0000000..78b5570 --- /dev/null +++ b/go/signals/signals.sh @@ -0,0 +1,9 @@ +# 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 `interrupt` and then exit. +$ go run signals.go +awaiting signal +^C +interrupt +exiting diff --git a/go/slices/slices.go b/go/slices/slices.go new file mode 100644 index 0000000..914a327 --- /dev/null +++ b/go/slices/slices.go @@ -0,0 +1,76 @@ +// _Slices_ are a key data type in Go, giving a more +// powerful interface to sequences than arrays. + +package main + +import "fmt" + +func main() { + + // Unlike arrays, slices are typed only by the + // elements they contain (not the number of elements). + // To create an empty slice with non-zero length, use + // the builtin `make`. Here we make a slice of + // `string`s of length `3` (initially zero-valued). + s := make([]string, 3) + fmt.Println("emp:", s) + + // We can set and get just like with arrays. + s[0] = "a" + s[1] = "b" + s[2] = "c" + fmt.Println("set:", s) + fmt.Println("get:", s[2]) + + // `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) + + // Slices can be composed into multi-dimensional data + // structures. The length of the inner slices can + // vary, unlike with multi-dimensional arrays. + twoD := make([][]int, 3) + for i := 0; i < 3; i++ { + innerLen := i + 1 + twoD[i] = make([]int, innerLen) + for j := 0; j < innerLen; j++ { + twoD[i][j] = i + j + } + } + fmt.Println("2d: ", twoD) +} diff --git a/go/slices/slices.hash b/go/slices/slices.hash new file mode 100644 index 0000000..8b501cb --- /dev/null +++ b/go/slices/slices.hash @@ -0,0 +1,2 @@ +c6fa1627841f199dbf901f88580cb97eb92c5530 +Z3_U32sn8RF diff --git a/go/slices/slices.sh b/go/slices/slices.sh new file mode 100644 index 0000000..9d40831 --- /dev/null +++ b/go/slices/slices.sh @@ -0,0 +1,21 @@ +# Note that while slices are different types than arrays, +# they are rendered similarly by `fmt.Println`. +$ go run slices.go +emp: [ ] +set: [a b c] +get: c +len: 3 +apd: [a b c d e f] +cpy: [a b c d e f] +sl1: [c d e] +sl2: [a b c d e] +sl3: [c d e f] +dcl: [g h i] +2d: [[0] [1 2] [2 3 4]] + +# Check out this [great blog post](http://blog.golang.org/2011/01/go-slices-usage-and-internals.html) +# 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. diff --git a/go/sorting-by-functions/sorting-by-functions.go b/go/sorting-by-functions/sorting-by-functions.go new file mode 100644 index 0000000..8524f66 --- /dev/null +++ b/go/sorting-by-functions/sorting-by-functions.go @@ -0,0 +1,45 @@ +// 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 ( + "fmt" + "sort" +) + +// In order to sort by a custom function in Go, we need a +// corresponding type. Here we've created a `byLength` +// type that is just an alias for the builtin `[]string` +// type. +type byLength []string + +// We implement `sort.Interface` - `Len`, `Less`, and +// `Swap` - on our type so we can use the `sort` package's +// generic `Sort` function. `Len` and `Swap` +// will usually be similar across types and `Less` will +// hold the actual custom sorting logic. In our case we +// want to sort in order of increasing string length, so +// we use `len(s[i])` and `len(s[j])` here. +func (s byLength) Len() int { + return len(s) +} +func (s byLength) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} +func (s byLength) Less(i, j int) bool { + return len(s[i]) < len(s[j]) +} + +// With all of this in place, we can now implement our +// custom sort by converting the original `fruits` slice +// to `byLength`, and then use `sort.Sort` on that typed +// slice. +func main() { + fruits := []string{"peach", "banana", "kiwi"} + sort.Sort(byLength(fruits)) + fmt.Println(fruits) +} diff --git a/go/sorting-by-functions/sorting-by-functions.hash b/go/sorting-by-functions/sorting-by-functions.hash new file mode 100644 index 0000000..d62f035 --- /dev/null +++ b/go/sorting-by-functions/sorting-by-functions.hash @@ -0,0 +1,2 @@ +f7d0b7840dd12601fb86946f9dc4c38fb1c0501f +Jtxf94x94Hx diff --git a/go/sorting-by-functions/sorting-by-functions.sh b/go/sorting-by-functions/sorting-by-functions.sh new file mode 100644 index 0000000..d2a1c4c --- /dev/null +++ b/go/sorting-by-functions/sorting-by-functions.sh @@ -0,0 +1,10 @@ +# Running our program shows a list sorted by string +# length, as desired. +$ go run sorting-by-functions.go +[kiwi peach banana] + +# By following this same pattern of creating a custom +# type, implementing the three `Interface` methods on that +# type, and then calling sort.Sort on a collection of that +# custom type, we can sort Go slices by arbitrary +# functions. diff --git a/go/sorting/sorting.go b/go/sorting/sorting.go new file mode 100644 index 0000000..acc67d8 --- /dev/null +++ b/go/sorting/sorting.go @@ -0,0 +1,31 @@ +// Go's `sort` package implements sorting for builtins +// and user-defined types. We'll look at sorting for +// builtins first. + +package main + +import ( + "fmt" + "sort" +) + +func main() { + + // Sort methods are specific to the builtin type; + // here's an example for strings. Note that sorting is + // in-place, so it changes the given slice and doesn't + // return a new one. + strs := []string{"c", "a", "b"} + sort.Strings(strs) + fmt.Println("Strings:", strs) + + // An example of sorting `int`s. + ints := []int{7, 2, 4} + sort.Ints(ints) + fmt.Println("Ints: ", ints) + + // We can also use `sort` to check if a slice is + // already in sorted order. + s := sort.IntsAreSorted(ints) + fmt.Println("Sorted: ", s) +} diff --git a/go/sorting/sorting.hash b/go/sorting/sorting.hash new file mode 100644 index 0000000..d72396f --- /dev/null +++ b/go/sorting/sorting.hash @@ -0,0 +1,2 @@ +e11e944d34b21e75ce4f7c91026d4200ce592dc5 +tAWAkRlBJNX diff --git a/go/sorting/sorting.sh b/go/sorting/sorting.sh new file mode 100644 index 0000000..c0e7462 --- /dev/null +++ b/go/sorting/sorting.sh @@ -0,0 +1,6 @@ +# Running our program prints the sorted string and int +# slices and `true` as the result of our `AreSorted` test. +$ go run sorting.go +Strings: [a b c] +Ints: [2 4 7] +Sorted: true diff --git a/go/spawning-processes/spawning-processes.go b/go/spawning-processes/spawning-processes.go new file mode 100644 index 0000000..58fdae8 --- /dev/null +++ b/go/spawning-processes/spawning-processes.go @@ -0,0 +1,73 @@ +// Sometimes our Go programs need to spawn other, non-Go +// processes. For example, the syntax highlighting on this +// site is [implemented](https://github.com/mmcgrana/gobyexample/blob/master/tools/generate.go) +// by spawning a [`pygmentize`](http://pygments.org/) +// process from a Go program. Let's look at a few examples +// of spawning processes from Go. + +package main + +import ( + "fmt" + "io/ioutil" + "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") + + // `.Output` is another helper that handles the common + // case of running a command, waiting for it to finish, + // and collecting its 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)) + + // 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, _ := ioutil.ReadAll(grepOut) + grepCmd.Wait() + + // We ommited 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)) +} diff --git a/go/spawning-processes/spawning-processes.hash b/go/spawning-processes/spawning-processes.hash new file mode 100644 index 0000000..bcde707 --- /dev/null +++ b/go/spawning-processes/spawning-processes.hash @@ -0,0 +1,2 @@ +cc68e4290f10209ad2fa8db74fdfaea7fdb44d5c +QS_Nkoe8VLG diff --git a/go/spawning-processes/spawning-processes.sh b/go/spawning-processes/spawning-processes.sh new file mode 100644 index 0000000..a297f5b --- /dev/null +++ b/go/spawning-processes/spawning-processes.sh @@ -0,0 +1,13 @@ +# The spawned programs return output that is the same +# as if we had run them directly from the command-line. +$ go run spawning-processes.go +> date +Wed Oct 10 09:53:11 PDT 2012 + +> grep hello +hello grep + +> ls -a -l -h +drwxr-xr-x 4 mark 136B Oct 3 16:29 . +drwxr-xr-x 91 mark 3.0K Oct 3 12:50 .. +-rw-r--r-- 1 mark 1.3K Oct 3 16:28 spawning-processes.go diff --git a/go/stateful-goroutines/stateful-goroutines.go b/go/stateful-goroutines/stateful-goroutines.go new file mode 100644 index 0000000..cb8b347 --- /dev/null +++ b/go/stateful-goroutines/stateful-goroutines.go @@ -0,0 +1,115 @@ +// 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 1 goroutine. + +package main + +import ( + "fmt" + "math/rand" + "sync/atomic" + "time" +) + +// In this example our state will be owned by a single +// goroutine. This will guarantee that the data is never +// corrupted with concurrent access. In order to read or +// write that state, other goroutines will send messages +// to the owning goroutine and receive corresponding +// replies. These `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 the receiving the + // result over the provided `resp` channel. + for r := 0; r < 100; r++ { + 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 w := 0; w < 10; w++ { + go func() { + for { + write := writeOp{ + key: rand.Intn(5), + val: rand.Intn(100), + resp: make(chan bool)} + writes <- write + <-write.resp + atomic.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) +} diff --git a/go/stateful-goroutines/stateful-goroutines.hash b/go/stateful-goroutines/stateful-goroutines.hash new file mode 100644 index 0000000..1f3eb48 --- /dev/null +++ b/go/stateful-goroutines/stateful-goroutines.hash @@ -0,0 +1,2 @@ +956afe7524b492b2e85f8320c70f180c448a764a +saQTLpdIgp2 diff --git a/go/stateful-goroutines/stateful-goroutines.sh b/go/stateful-goroutines/stateful-goroutines.sh new file mode 100644 index 0000000..dc26609 --- /dev/null +++ b/go/stateful-goroutines/stateful-goroutines.sh @@ -0,0 +1,15 @@ +# 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. diff --git a/go/string-formatting/string-formatting.go b/go/string-formatting/string-formatting.go new file mode 100644 index 0000000..958cb64 --- /dev/null +++ b/go/string-formatting/string-formatting.go @@ -0,0 +1,110 @@ +// 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("%v\n", p) + + // If the value is a struct, the `%+v` variant will + // include the struct's field names. + fmt.Printf("%+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("%#v\n", p) + + // To print the type of a value, use `%T`. + fmt.Printf("%T\n", p) + + // Formatting booleans is straight-forward. + fmt.Printf("%t\n", true) + + // There are many options for formatting integers. + // Use `%d` for standard, base-10 formatting. + fmt.Printf("%d\n", 123) + + // This prints a binary representation. + fmt.Printf("%b\n", 14) + + // This prints the character corresponding to the + // given integer. + fmt.Printf("%c\n", 33) + + // `%x` provides hex encoding. + fmt.Printf("%x\n", 456) + + // There are also several formatting options for + // floats. For basic decimal formatting use `%f`. + fmt.Printf("%f\n", 78.9) + + // `%e` and `%E` format the float in (slightly + // different versions of) scientific notation. + fmt.Printf("%e\n", 123400000.0) + fmt.Printf("%E\n", 123400000.0) + + // For basic string printing use `%s`. + fmt.Printf("%s\n", "\"string\"") + + // To double-quote strings as in Go source, use `%q`. + fmt.Printf("%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("%x\n", "hex this") + + // To print a representation of a pointer, use `%p`. + fmt.Printf("%p\n", &p) + + // When formatting numbers you will often want to + // control the width and precision of the resulting + // figure. To specify the width of an integer, use a + // number after the `%` in the verb. By default the + // result will be right-justified and padded with + // spaces. + fmt.Printf("|%6d|%6d|\n", 12, 345) + + // You can also specify the width of printed floats, + // though usually you'll also want to restrict the + // decimal precision at the same time with the + // width.precision syntax. + fmt.Printf("|%6.2f|%6.2f|\n", 1.2, 3.45) + + // To left-justify, use the `-` flag. + fmt.Printf("|%-6.2f|%-6.2f|\n", 1.2, 3.45) + + // You may also want to control width when formatting + // strings, especially to ensure that they align in + // table-like output. For basic right-justified width. + fmt.Printf("|%6s|%6s|\n", "foo", "b") + + // To left-justify use the `-` flag as with numbers. + fmt.Printf("|%-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("a %s", "string") + fmt.Println(s) + + // You can format+print to `io.Writers` other than + // `os.Stdout` using `Fprintf`. + fmt.Fprintf(os.Stderr, "an %s\n", "error") +} diff --git a/go/string-formatting/string-formatting.hash b/go/string-formatting/string-formatting.hash new file mode 100644 index 0000000..c339d9b --- /dev/null +++ b/go/string-formatting/string-formatting.hash @@ -0,0 +1,2 @@ +12b245c576b43537c092a5b84995ebca8ce78a57 +vmYSdxfUcRh diff --git a/go/string-formatting/string-formatting.sh b/go/string-formatting/string-formatting.sh new file mode 100644 index 0000000..a1a7f6b --- /dev/null +++ b/go/string-formatting/string-formatting.sh @@ -0,0 +1,24 @@ +$ go run string-formatting.go +{1 2} +{x:1 y:2} +main.point{x:1, y:2} +main.point +true +123 +1110 +! +1c8 +78.900000 +1.234000e+08 +1.234000E+08 +"string" +"\"string\"" +6865782074686973 +0x42135100 +| 12| 345| +| 1.20| 3.45| +|1.20 |3.45 | +| foo| b| +|foo |b | +a string +an error diff --git a/go/string-functions/string-functions.go b/go/string-functions/string-functions.go new file mode 100644 index 0000000..676e904 --- /dev/null +++ b/go/string-functions/string-functions.go @@ -0,0 +1,51 @@ +// 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 pass the string in question as the first + // argument to the function. You can find more + // functions in the [`strings`](http://golang.org/pkg/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")) + p() + + // Not part of `strings`, but worth mentioning here, are + // the mechanisms for getting the length of a string in + // bytes and getting a byte by index. + p("Len: ", len("hello")) + p("Char:", "hello"[1]) +} + +// Note that `len` and indexing above work at the byte level. +// Go uses UTF-8 encoded strings, so this is often useful +// as-is. If you're working with potentially multi-byte +// characters you'll want to use encoding-aware operations. +// See [strings, bytes, runes and characters in Go](https://blog.golang.org/strings) +// for more information. diff --git a/go/string-functions/string-functions.hash b/go/string-functions/string-functions.hash new file mode 100644 index 0000000..568c7c6 --- /dev/null +++ b/go/string-functions/string-functions.hash @@ -0,0 +1,2 @@ +bf39c7540bd78eba38eb5a9047a9d0ffc7235f85 +xoRUhG86wsF diff --git a/go/string-functions/string-functions.sh b/go/string-functions/string-functions.sh new file mode 100644 index 0000000..b83688d --- /dev/null +++ b/go/string-functions/string-functions.sh @@ -0,0 +1,16 @@ +$ go run string-functions.go +Contains: true +Count: 2 +HasPrefix: true +HasSuffix: true +Index: 1 +Join: a-b +Repeat: aaaaa +Replace: f00 +Replace: f0o +Split: [a b c d e] +ToLower: test +ToUpper: TEST + +Len: 5 +Char: 101 diff --git a/go/structs/structs.go b/go/structs/structs.go new file mode 100644 index 0000000..384b57c --- /dev/null +++ b/go/structs/structs.go @@ -0,0 +1,53 @@ +// 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 { + // You can safely return a pointer to local variable + // as a local variable will survive the scope of the function. + p := person{name: name} + p.age = 42 + return &p +} + +func main() { + + // This syntax creates a new struct. + 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) +} diff --git a/go/structs/structs.hash b/go/structs/structs.hash new file mode 100644 index 0000000..15fcdc0 --- /dev/null +++ b/go/structs/structs.hash @@ -0,0 +1,2 @@ +c5caaf1eefaf084d688afb70d2ee5884a4983182 +00Yiw6xuICq diff --git a/go/structs/structs.sh b/go/structs/structs.sh new file mode 100644 index 0000000..039fd00 --- /dev/null +++ b/go/structs/structs.sh @@ -0,0 +1,9 @@ +$ go run structs.go +{Bob 20} +{Alice 30} +{Fred 0} +&{Ann 40} +&{Jon 42} +Sean +50 +51 \ No newline at end of file diff --git a/go/switch/switch.go b/go/switch/switch.go new file mode 100644 index 0000000..2d2ec2e --- /dev/null +++ b/go/switch/switch.go @@ -0,0 +1,63 @@ +// _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") +} diff --git a/go/switch/switch.hash b/go/switch/switch.hash new file mode 100644 index 0000000..54d2e9f --- /dev/null +++ b/go/switch/switch.hash @@ -0,0 +1,2 @@ +2486fc553301cdeac9a26f3d0b3aed4143d9f4f0 +ZcDzdx3nYQn diff --git a/go/switch/switch.sh b/go/switch/switch.sh new file mode 100644 index 0000000..42e5206 --- /dev/null +++ b/go/switch/switch.sh @@ -0,0 +1,7 @@ +$ 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 diff --git a/go/temporary-files-and-directories/temporary-files-and-directories.go b/go/temporary-files-and-directories/temporary-files-and-directories.go new file mode 100644 index 0000000..abed14e --- /dev/null +++ b/go/temporary-files-and-directories/temporary-files-and-directories.go @@ -0,0 +1,65 @@ +// 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" + "io/ioutil" + "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 `ioutil.TempFile`. It creates a file *and* + // opens it for reading and writing. We provide `""` + // as the first argument, so `ioutil.TempFile` will + // create the file in the default location for our OS. + f, err := ioutil.TempFile("", "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 `ioutil.TempFile` 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*. + // `ioutil.TempDir`'s arguments are the same as + // `TempFile`'s, but it returns a directory *name* + // rather than an open file. + dname, err := ioutil.TempDir("", "sampledir") + 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 = ioutil.WriteFile(fname, []byte{1, 2}, 0666) + check(err) +} diff --git a/go/temporary-files-and-directories/temporary-files-and-directories.hash b/go/temporary-files-and-directories/temporary-files-and-directories.hash new file mode 100644 index 0000000..f523002 --- /dev/null +++ b/go/temporary-files-and-directories/temporary-files-and-directories.hash @@ -0,0 +1,2 @@ +5f7d0c43988d7dce235adb06ec02f4d2026b7f83 +pxE20wGTFjv diff --git a/go/temporary-files-and-directories/temporary-files-and-directories.sh b/go/temporary-files-and-directories/temporary-files-and-directories.sh new file mode 100644 index 0000000..c855624 --- /dev/null +++ b/go/temporary-files-and-directories/temporary-files-and-directories.sh @@ -0,0 +1,3 @@ +$ go run temporary-files-and-directories.go +Temp file name: /tmp/sample610887201 +Temp dir name: /tmp/sampledir898854668 diff --git a/go/testing/main_test.go b/go/testing/main_test.go new file mode 100644 index 0000000..3bcdd6e --- /dev/null +++ b/go/testing/main_test.go @@ -0,0 +1,69 @@ +// 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 + } else { + 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.Fail*` 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) + } + }) + } +} diff --git a/go/testing/main_test.sh b/go/testing/main_test.sh new file mode 100644 index 0000000..58e0615 --- /dev/null +++ b/go/testing/main_test.sh @@ -0,0 +1,18 @@ +# 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 0.023s diff --git a/go/testing/testing.hash b/go/testing/testing.hash new file mode 100644 index 0000000..317487f --- /dev/null +++ b/go/testing/testing.hash @@ -0,0 +1,2 @@ +8f00c5178a33be2e92a853f14bfc3fbf0919cd97 +fyy7h1adGWr diff --git a/go/tickers/tickers.go b/go/tickers/tickers.go new file mode 100644 index 0000000..d9615d3 --- /dev/null +++ b/go/tickers/tickers.go @@ -0,0 +1,41 @@ +// [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") +} diff --git a/go/tickers/tickers.hash b/go/tickers/tickers.hash new file mode 100644 index 0000000..cd2eaf3 --- /dev/null +++ b/go/tickers/tickers.hash @@ -0,0 +1,2 @@ +05a1e62b5d363b67df0f9bac5c8f75dc19ca2c54 +E1ro_QHJD9L diff --git a/go/tickers/tickers.sh b/go/tickers/tickers.sh new file mode 100644 index 0000000..0bce8ff --- /dev/null +++ b/go/tickers/tickers.sh @@ -0,0 +1,7 @@ +# 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 diff --git a/go/time-formatting-parsing/time-formatting-parsing.go b/go/time-formatting-parsing/time-formatting-parsing.go new file mode 100644 index 0000000..2968c3c --- /dev/null +++ b/go/time-formatting-parsing/time-formatting-parsing.go @@ -0,0 +1,52 @@ +// 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, e := 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, e := 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. + ansic := "Mon Jan _2 15:04:05 2006" + _, e = time.Parse(ansic, "8:41PM") + p(e) +} diff --git a/go/time-formatting-parsing/time-formatting-parsing.hash b/go/time-formatting-parsing/time-formatting-parsing.hash new file mode 100644 index 0000000..eee6d65 --- /dev/null +++ b/go/time-formatting-parsing/time-formatting-parsing.hash @@ -0,0 +1,2 @@ +9e3f17061fef280191e3e8518365e231e17a5d5a +1410R7Fcyx0 diff --git a/go/time-formatting-parsing/time-formatting-parsing.sh b/go/time-formatting-parsing/time-formatting-parsing.sh new file mode 100644 index 0000000..da58cea --- /dev/null +++ b/go/time-formatting-parsing/time-formatting-parsing.sh @@ -0,0 +1,9 @@ +$ 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": ... diff --git a/go/time/time.go b/go/time/time.go new file mode 100644 index 0000000..db96527 --- /dev/null +++ b/go/time/time.go @@ -0,0 +1,63 @@ +// 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)) +} diff --git a/go/time/time.hash b/go/time/time.hash new file mode 100644 index 0000000..2c9d415 --- /dev/null +++ b/go/time/time.hash @@ -0,0 +1,2 @@ +c47d853fa7527a652ce78b0285e452c6cd740050 +u-7i_p8BHVt diff --git a/go/time/time.sh b/go/time/time.sh new file mode 100644 index 0000000..53c9030 --- /dev/null +++ b/go/time/time.sh @@ -0,0 +1,25 @@ +$ 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. diff --git a/go/timeouts/timeouts.go b/go/timeouts/timeouts.go new file mode 100644 index 0000000..3f25aec --- /dev/null +++ b/go/timeouts/timeouts.go @@ -0,0 +1,53 @@ +// _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") + } +} diff --git a/go/timeouts/timeouts.hash b/go/timeouts/timeouts.hash new file mode 100644 index 0000000..f622aeb --- /dev/null +++ b/go/timeouts/timeouts.hash @@ -0,0 +1,2 @@ +b8d3e745539b24d3530ca21efcdc924f08769edb +TYJgoFjlTd6 diff --git a/go/timeouts/timeouts.sh b/go/timeouts/timeouts.sh new file mode 100644 index 0000000..3c4344f --- /dev/null +++ b/go/timeouts/timeouts.sh @@ -0,0 +1,5 @@ +# Running this program shows the first operation timing +# out and the second succeeding. +$ go run timeouts.go +timeout 1 +result 2 diff --git a/go/timers/timers.go b/go/timers/timers.go new file mode 100644 index 0000000..d0256e6 --- /dev/null +++ b/go/timers/timers.go @@ -0,0 +1,41 @@ +// 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 + // expired. + <-timer1.C + fmt.Println("Timer 1 expired") + + // 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 expires. + // Here's an example of that. + timer2 := time.NewTimer(time.Second) + go func() { + <-timer2.C + fmt.Println("Timer 2 expired") + }() + stop2 := timer2.Stop() + if stop2 { + fmt.Println("Timer 2 stopped") + } +} diff --git a/go/timers/timers.hash b/go/timers/timers.hash new file mode 100644 index 0000000..8c0f2c4 --- /dev/null +++ b/go/timers/timers.hash @@ -0,0 +1,2 @@ +e8e501d6083bea786629ca5e485e8b18caab4815 +pLnKEIesooU diff --git a/go/timers/timers.sh b/go/timers/timers.sh new file mode 100644 index 0000000..b4963a9 --- /dev/null +++ b/go/timers/timers.sh @@ -0,0 +1,6 @@ +// The first timer will expire ~2s after we start the +// program, but the second should be stopped before it has +// a chance to expire. +$ go run timers.go +Timer 1 expired +Timer 2 stopped diff --git a/go/url-parsing/url-parsing.go b/go/url-parsing/url-parsing.go new file mode 100644 index 0000000..f7590f1 --- /dev/null +++ b/go/url-parsing/url-parsing.go @@ -0,0 +1,57 @@ +// 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]) +} diff --git a/go/url-parsing/url-parsing.hash b/go/url-parsing/url-parsing.hash new file mode 100644 index 0000000..77021f1 --- /dev/null +++ b/go/url-parsing/url-parsing.hash @@ -0,0 +1,2 @@ +babc12f5066652f4cb0151231c06f1037298ff28 +M218D9Tldlr diff --git a/go/url-parsing/url-parsing.sh b/go/url-parsing/url-parsing.sh new file mode 100644 index 0000000..9323124 --- /dev/null +++ b/go/url-parsing/url-parsing.sh @@ -0,0 +1,15 @@ +# 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 diff --git a/go/values/values.go b/go/values/values.go new file mode 100644 index 0000000..2d34a4b --- /dev/null +++ b/go/values/values.go @@ -0,0 +1,22 @@ +// 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) +} diff --git a/go/values/values.hash b/go/values/values.hash new file mode 100644 index 0000000..b10176b --- /dev/null +++ b/go/values/values.hash @@ -0,0 +1,2 @@ +c5a53c75cc57dc15ac4458285c9b139bf85c67bf +aGiVohrYqYC diff --git a/go/values/values.sh b/go/values/values.sh new file mode 100644 index 0000000..26181ed --- /dev/null +++ b/go/values/values.sh @@ -0,0 +1,7 @@ +$ go run values.go +golang +1+1 = 2 +7.0/3.0 = 2.3333333333333335 +false +true +false diff --git a/go/variables/variables.go b/go/variables/variables.go new file mode 100644 index 0000000..8e0912f --- /dev/null +++ b/go/variables/variables.go @@ -0,0 +1,34 @@ +// 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. + f := "apple" + fmt.Println(f) +} diff --git a/go/variables/variables.hash b/go/variables/variables.hash new file mode 100644 index 0000000..316281a --- /dev/null +++ b/go/variables/variables.hash @@ -0,0 +1,2 @@ +636a63e1bf810cb9d0620cc5160330f6d3d8679d +kwm2xuWnlKq diff --git a/go/variables/variables.sh b/go/variables/variables.sh new file mode 100644 index 0000000..6513b1d --- /dev/null +++ b/go/variables/variables.sh @@ -0,0 +1,6 @@ +$ go run variables.go +initial +1 2 +true +0 +apple diff --git a/go/variadic-functions/variadic-functions.go b/go/variadic-functions/variadic-functions.go new file mode 100644 index 0000000..02c67a5 --- /dev/null +++ b/go/variadic-functions/variadic-functions.go @@ -0,0 +1,33 @@ +// [_Variadic functions_](http://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 + 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...) +} diff --git a/go/variadic-functions/variadic-functions.hash b/go/variadic-functions/variadic-functions.hash new file mode 100644 index 0000000..3c44a2b --- /dev/null +++ b/go/variadic-functions/variadic-functions.hash @@ -0,0 +1,2 @@ +34ba16069a5d972a837cc5c0172ab30873535220 +7f0JlVhToDD diff --git a/go/variadic-functions/variadic-functions.sh b/go/variadic-functions/variadic-functions.sh new file mode 100644 index 0000000..feef61e --- /dev/null +++ b/go/variadic-functions/variadic-functions.sh @@ -0,0 +1,7 @@ +$ 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. diff --git a/go/waitgroups/waitgroups.go b/go/waitgroups/waitgroups.go new file mode 100644 index 0000000..2cf16a3 --- /dev/null +++ b/go/waitgroups/waitgroups.go @@ -0,0 +1,42 @@ +// 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. +// Note that a WaitGroup must be passed to functions by +// pointer. +func worker(id int, wg *sync.WaitGroup) { + fmt.Printf("Worker %d starting\n", id) + + // Sleep to simulate an expensive task. + time.Sleep(time.Second) + fmt.Printf("Worker %d done\n", id) + + // Notify the WaitGroup that this worker is done. + wg.Done() +} + +func main() { + + // This WaitGroup is used to wait for all the + // goroutines launched here to finish. + var wg sync.WaitGroup + + // Launch several goroutines and increment the WaitGroup + // counter for each. + for i := 1; i <= 5; i++ { + wg.Add(1) + go worker(i, &wg) + } + + // Block until the WaitGroup counter goes back to 0; + // all the workers notified they're done. + wg.Wait() +} diff --git a/go/waitgroups/waitgroups.hash b/go/waitgroups/waitgroups.hash new file mode 100644 index 0000000..71ecfc8 --- /dev/null +++ b/go/waitgroups/waitgroups.hash @@ -0,0 +1,2 @@ +499c7ee59b2ae06d2d3171768d9cf11762121a87 +gLLmgcR7YkP diff --git a/go/waitgroups/waitgroups.sh b/go/waitgroups/waitgroups.sh new file mode 100644 index 0000000..c2a6c11 --- /dev/null +++ b/go/waitgroups/waitgroups.sh @@ -0,0 +1,14 @@ +$ 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. diff --git a/go/worker-pools/worker-pools.go b/go/worker-pools/worker-pools.go new file mode 100644 index 0000000..1584d59 --- /dev/null +++ b/go/worker-pools/worker-pools.go @@ -0,0 +1,53 @@ +// 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. + jobs := make(chan int, 100) + results := make(chan int, 100) + + // This starts up 3 workers, initially blocked + // because there are no jobs yet. + for w := 1; w <= 3; w++ { + go worker(w, jobs, results) + } + + // Here we send 5 `jobs` and then `close` that + // channel to indicate that's all the work we have. + for j := 1; j <= 5; 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 <= 5; a++ { + <-results + } +} diff --git a/go/worker-pools/worker-pools.hash b/go/worker-pools/worker-pools.hash new file mode 100644 index 0000000..dbfeb88 --- /dev/null +++ b/go/worker-pools/worker-pools.hash @@ -0,0 +1,2 @@ +9b30cdfc3f46d634c3b8671a7ae1551c133fb6e2 +IiKZ-nj-nKY diff --git a/go/worker-pools/worker-pools.sh b/go/worker-pools/worker-pools.sh new file mode 100644 index 0000000..10139fb --- /dev/null +++ b/go/worker-pools/worker-pools.sh @@ -0,0 +1,17 @@ +# 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 diff --git a/go/writing-files/writing-files.go b/go/writing-files/writing-files.go new file mode 100644 index 0000000..82275ce --- /dev/null +++ b/go/writing-files/writing-files.go @@ -0,0 +1,58 @@ +// Writing files in Go follows similar patterns to the +// ones we saw earlier for reading. + +package main + +import ( + "bufio" + "fmt" + "io/ioutil" + "os" +) + +func check(e error) { + if e != nil { + panic(e) + } +} + +func main() { + + // To start, here's how to dump a string (or just + // bytes) into a file. + d1 := []byte("hello\ngo\n") + err := ioutil.WriteFile("/tmp/dat1", d1, 0644) + check(err) + + // For more granular writes, open a file for writing. + f, err := os.Create("/tmp/dat2") + check(err) + + // It's idiomatic to defer a `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") + 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") + fmt.Printf("wrote %d bytes\n", n4) + + // Use `Flush` to ensure all buffered operations have + // been applied to the underlying writer. + w.Flush() + +} diff --git a/go/writing-files/writing-files.hash b/go/writing-files/writing-files.hash new file mode 100644 index 0000000..360897a --- /dev/null +++ b/go/writing-files/writing-files.hash @@ -0,0 +1,2 @@ +0853ca57176872e9b34b501855ceb8bf5fbdbf46 +gyJn9PcndtP diff --git a/go/writing-files/writing-files.sh b/go/writing-files/writing-files.sh new file mode 100644 index 0000000..945a823 --- /dev/null +++ b/go/writing-files/writing-files.sh @@ -0,0 +1,17 @@ +# 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. diff --git a/go/xml/xml.go b/go/xml/xml.go new file mode 100644 index 0000000..e545106 --- /dev/null +++ b/go/xml/xml.go @@ -0,0 +1,69 @@ +// Go offers built-in support for XML and XML-like +// formats with the `encoding.xml` package. + +package main + +import ( + "encoding/xml" + "fmt" +) + +// This type 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 `Unmarhshal` 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)) +} diff --git a/go/xml/xml.hash b/go/xml/xml.hash new file mode 100644 index 0000000..104125d --- /dev/null +++ b/go/xml/xml.hash @@ -0,0 +1,2 @@ +18ada773098bca38778a58b438d6af70529f18b0 +qd9Ii_3AW0s diff --git a/go/xml/xml.sh b/go/xml/xml.sh new file mode 100644 index 0000000..0e8d2cd --- /dev/null +++ b/go/xml/xml.sh @@ -0,0 +1,29 @@ +$ go run xml.go + + Coffee + Ethiopia + Brazil + + + + Coffee + Ethiopia + Brazil + +Plant id=27, name=Coffee, origin=[Ethiopia Brazil] + + + + + Coffee + Ethiopia + Brazil + + + Tomato + Mexico + California + + + +