From dba288dcfdccd41ec02cb02544d452769dd0a857 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Wed, 4 Mar 2026 20:55:17 +0200 Subject: fix: populate stub reference files, remove redundant TL;DR, fix HTML entities - Populate 19 stub files with actual content from 100go.co - Remove redundant bare 'TL;DR' line after '#### TL;DR' header in 76 files - Convert 3 mid-file '#### TL;DR / Note' sections to '#### Note' - Fix HTML entities (< > &) to proper characters in 9 files Amp-Thread-ID: https://ampcode.com/threads/T-019cba2c-0536-70d9-956b-8ff115f07646 Co-authored-by: Amp --- .../mistake-01-unintended-variable-shadowing.md | 1 - .../mistake-02-unnecessary-nested-code.md | 1 - .../mistake-03-misusing-init-functions.md | 1 - .../mistake-04-overusing-getters-and-setters.md | 1 - .../references/mistake-05-interface-pollution.md | 1 - .../mistake-06-interface-on-the-producer-side.md | 1 - .../references/mistake-07-returning-interfaces.md | 1 - .../references/mistake-08-any-says-nothing.md | 6 ++- ...09-being-confused-about-when-to-use-generics.md | 1 - ...of-the-possible-problems-with-type-embedding.md | 1 - ...pacts-of-running-go-in-docker-and-kubernetes.md | 12 ++++- ...-11-not-using-the-functional-options-pattern.md | 7 ++- .../mistake-13-creating-utility-packages.md | 1 - .../mistake-14-ignoring-package-name-collisions.md | 1 - .../mistake-15-missing-code-documentation.md | 1 - .../references/mistake-16-not-using-linters.md | 1 - ...ke-17-creating-confusion-with-octal-literals.md | 1 - .../mistake-18-neglecting-integer-overflows.md | 1 - ...mistake-19-not-understanding-floating-points.md | 1 - ...-not-understanding-slice-length-and-capacity.md | 1 - .../mistake-21-inefficient-slice-initialization.md | 1 - ...e-22-being-confused-about-nil-vs-empty-slice.md | 1 - ...23-not-properly-checking-if-a-slice-is-empty.md | 1 - ...mistake-24-not-making-slice-copies-correctly.md | 1 - ...5-unexpected-side-effects-using-slice-append.md | 4 +- .../mistake-26-slices-and-memory-leaks.md | 1 - .../mistake-27-inefficient-map-initialization.md | 1 - .../references/mistake-28-maps-and-memory-leaks.md | 1 - .../mistake-29-comparing-values-incorrectly.md | 6 +-- ...ring-that-elements-are-copied-in-range-loops.md | 12 ++++- ...evaluated-in-range-loops-channels-and-arrays.md | 18 ++++++- ...cts-of-using-pointer-elements-in-range-loops.md | 15 +++++- ...ons-ordering-and-map-insert-during-iteration.md | 1 - ...ke-34-ignoring-how-the-break-statement-works.md | 35 ++++++++++++- .../mistake-35-using-defer-inside-a-loop.md | 45 ++++++++++++++++- ...ake-36-not-understanding-the-concept-of-rune.md | 1 - .../mistake-37-inaccurate-string-iteration.md | 1 - .../mistake-38-misusing-trim-functions.md | 1 - ...ake-39-under-optimized-strings-concatenation.md | 6 +-- .../mistake-40-useless-string-conversions.md | 1 - .../mistake-41-substring-and-memory-leaks.md | 1 - ...42-not-knowing-which-type-of-receiver-to-use.md | 1 - ...stake-43-never-using-named-result-parameters.md | 1 - ...ed-side-effects-with-named-result-parameters.md | 1 - .../mistake-45-returning-a-nil-receiver.md | 1 - ...take-46-using-a-filename-as-a-function-input.md | 1 - ...-defer-arguments-and-receivers-are-evaluated.md | 59 +++++++++++++++++++++- .../references/mistake-48-panicking.md | 1 - .../mistake-49-ignoring-when-to-wrap-an-error.md | 1 - ...take-50-comparing-an-error-type-inaccurately.md | 1 - ...ake-51-comparing-an-error-value-inaccurately.md | 1 - .../mistake-52-handling-an-error-twice.md | 1 - .../references/mistake-53-not-handling-an-error.md | 1 - .../mistake-54-not-handling-defer-errors.md | 29 ++++++++++- ...ake-55-mixing-up-concurrency-and-parallelism.md | 1 - ...ake-56-thinking-concurrency-is-always-faster.md | 1 - ...uzzled-about-when-to-use-channels-or-mutexes.md | 1 - ...-race-problems-data-races-vs-race-conditions.md | 1 - ...g-the-concurrency-impacts-of-a-workload-type.md | 1 - .../mistake-60-misunderstanding-go-contexts.md | 3 +- ...take-61-propagating-an-inappropriate-context.md | 1 - ...-a-goroutine-without-knowing-when-to-stop-it.md | 1 - ...g-careful-with-goroutines-and-loop-variables.md | 31 +++++++++++- ...rministic-behavior-using-select-and-channels.md | 11 ++-- .../mistake-65-not-using-notification-channels.md | 1 - .../mistake-66-not-using-nil-channels.md | 15 +++--- .../mistake-67-being-puzzled-about-channel-size.md | 5 +- ...possible-side-effects-with-string-formatting.md | 7 ++- .../mistake-69-creating-data-races-with-append.md | 1 - ...ng-mutexes-inaccurately-with-slices-and-maps.md | 1 - .../mistake-71-misusing-syncwaitgroup.md | 36 ++++++++++++- .../mistake-72-forgetting-about-synccond.md | 13 ++++- .../references/mistake-73-not-using-errgroup.md | 12 ++++- .../references/mistake-74-copying-a-sync-type.md | 14 ++++- .../mistake-75-providing-a-wrong-time-duration.md | 3 +- .../mistake-76-timeafter-and-memory-leaks.md | 22 +++++++- ...sient-resources-http-body-sqlrows-and-osfile.md | 20 +++++++- ...-statement-after-replying-to-an-http-request.md | 1 - ...-81-using-the-default-http-client-and-server.md | 1 - ...ld-tags-environment-variables-and-short-mode.md | 1 - .../mistake-83-not-enabling-the-race-flag.md | 1 - ...ng-test-execution-modes-parallel-and-shuffle.md | 1 - .../mistake-85-not-using-table-driven-tests.md | 1 - .../mistake-86-sleeping-in-unit-tests.md | 1 - ...87-not-dealing-with-the-time-api-efficiently.md | 1 - ...testing-utility-packages-httptest-and-iotest.md | 20 +++++++- .../mistake-89-writing-inaccurate-benchmarks.md | 1 - ...-concurrent-code-that-leads-to-false-sharing.md | 1 - ...g-into-account-instruction-level-parallelism.md | 1 - ...mistake-94-not-being-aware-of-data-alignment.md | 1 - .../mistake-95-not-understanding-stack-vs-heap.md | 1 - ...ake-96-not-knowing-how-to-reduce-allocations.md | 22 +++++++- .../mistake-97-not-relying-on-inlining.md | 1 - .../mistake-98-not-using-go-diagnostics-tooling.md | 1 - ...istake-99-not-understanding-how-the-gc-works.md | 20 +++++++- 95 files changed, 449 insertions(+), 125 deletions(-) diff --git a/prompts/skills/100-go-mistakes/references/mistake-01-unintended-variable-shadowing.md b/prompts/skills/100-go-mistakes/references/mistake-01-unintended-variable-shadowing.md index 076039e..54d5f69 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-01-unintended-variable-shadowing.md +++ b/prompts/skills/100-go-mistakes/references/mistake-01-unintended-variable-shadowing.md @@ -1,7 +1,6 @@ # Mistake #1: Unintended variable shadowing #### TL;DR -TL;DR Avoiding shadowed variables can help prevent mistakes like referencing the wrong variable or confusing readers. diff --git a/prompts/skills/100-go-mistakes/references/mistake-02-unnecessary-nested-code.md b/prompts/skills/100-go-mistakes/references/mistake-02-unnecessary-nested-code.md index dfd04f3..e8cf6c4 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-02-unnecessary-nested-code.md +++ b/prompts/skills/100-go-mistakes/references/mistake-02-unnecessary-nested-code.md @@ -1,7 +1,6 @@ # Mistake #2: Unnecessary nested code #### TL;DR -TL;DR Avoiding nested levels and keeping the happy path aligned on the left makes building a mental code model easier. diff --git a/prompts/skills/100-go-mistakes/references/mistake-03-misusing-init-functions.md b/prompts/skills/100-go-mistakes/references/mistake-03-misusing-init-functions.md index 805677e..8695301 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-03-misusing-init-functions.md +++ b/prompts/skills/100-go-mistakes/references/mistake-03-misusing-init-functions.md @@ -1,7 +1,6 @@ # Mistake #3: Misusing init functions #### TL;DR -TL;DR When initializing variables, remember that init functions have limited error handling and make state handling and testing more complex. In most cases, initializations should be handled as specific functions. diff --git a/prompts/skills/100-go-mistakes/references/mistake-04-overusing-getters-and-setters.md b/prompts/skills/100-go-mistakes/references/mistake-04-overusing-getters-and-setters.md index a93dffc..d7730ef 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-04-overusing-getters-and-setters.md +++ b/prompts/skills/100-go-mistakes/references/mistake-04-overusing-getters-and-setters.md @@ -1,7 +1,6 @@ # Mistake #4: Overusing getters and setters #### TL;DR -TL;DR Forcing the use of getters and setters isn’t idiomatic in Go. Being pragmatic and finding the right balance between efficiency and blindly following certain idioms should be the way to go. diff --git a/prompts/skills/100-go-mistakes/references/mistake-05-interface-pollution.md b/prompts/skills/100-go-mistakes/references/mistake-05-interface-pollution.md index 00c9738..203be64 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-05-interface-pollution.md +++ b/prompts/skills/100-go-mistakes/references/mistake-05-interface-pollution.md @@ -1,7 +1,6 @@ # Mistake #5: Interface pollution #### TL;DR -TL;DR Abstractions should be discovered, not created. To prevent unnecessary complexity, create an interface when you need it and not when you foresee needing it, or if you can at least prove the abstraction to be a valid one. diff --git a/prompts/skills/100-go-mistakes/references/mistake-06-interface-on-the-producer-side.md b/prompts/skills/100-go-mistakes/references/mistake-06-interface-on-the-producer-side.md index d77acb7..ca7e752 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-06-interface-on-the-producer-side.md +++ b/prompts/skills/100-go-mistakes/references/mistake-06-interface-on-the-producer-side.md @@ -1,7 +1,6 @@ # Mistake #6: Interface on the producer side #### TL;DR -TL;DR Keeping interfaces on the client side avoids unnecessary abstractions. diff --git a/prompts/skills/100-go-mistakes/references/mistake-07-returning-interfaces.md b/prompts/skills/100-go-mistakes/references/mistake-07-returning-interfaces.md index f7c4fca..db52c2e 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-07-returning-interfaces.md +++ b/prompts/skills/100-go-mistakes/references/mistake-07-returning-interfaces.md @@ -1,7 +1,6 @@ # Mistake #7: Returning interfaces #### TL;DR -TL;DR To prevent being restricted in terms of flexibility, a function shouldn’t return interfaces but concrete implementations in most cases. Conversely, a function should accept interfaces whenever possible. diff --git a/prompts/skills/100-go-mistakes/references/mistake-08-any-says-nothing.md b/prompts/skills/100-go-mistakes/references/mistake-08-any-says-nothing.md index af8a478..f7c8931 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-08-any-says-nothing.md +++ b/prompts/skills/100-go-mistakes/references/mistake-08-any-says-nothing.md @@ -1,3 +1,7 @@ # Mistake #8: any says nothing -[Documentation for mistake #8 from 100go.co] +#### TL;DR + +The `any` type can be helpful if there is a genuine need for accepting or returning any possible type (for instance, when it comes to marshaling or formatting). In general, we should avoid overgeneralizing the code we write at all costs. Perhaps a little bit of duplicated code might occasionally be better if it improves other aspects such as code expressiveness. + +[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/8-any/main.go) diff --git a/prompts/skills/100-go-mistakes/references/mistake-09-being-confused-about-when-to-use-generics.md b/prompts/skills/100-go-mistakes/references/mistake-09-being-confused-about-when-to-use-generics.md index 64e10ce..120881f 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-09-being-confused-about-when-to-use-generics.md +++ b/prompts/skills/100-go-mistakes/references/mistake-09-being-confused-about-when-to-use-generics.md @@ -1,7 +1,6 @@ # Mistake #9: Being confused about when to use generics #### TL;DR -TL;DR Relying on generics and type parameters can prevent writing boilerplate code to factor out elements or behaviors. However, do not use type parameters prematurely, but only when you see a concrete need for them. Otherwise, they introduce unnecessary abstractions and complexity. diff --git a/prompts/skills/100-go-mistakes/references/mistake-10-not-being-aware-of-the-possible-problems-with-type-embedding.md b/prompts/skills/100-go-mistakes/references/mistake-10-not-being-aware-of-the-possible-problems-with-type-embedding.md index 28f1864..0d6912c 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-10-not-being-aware-of-the-possible-problems-with-type-embedding.md +++ b/prompts/skills/100-go-mistakes/references/mistake-10-not-being-aware-of-the-possible-problems-with-type-embedding.md @@ -1,7 +1,6 @@ # Mistake #10: Not being aware of the possible problems with type embedding #### TL;DR -TL;DR Using type embedding can also help avoid boilerplate code; however, ensure that doing so doesn’t lead to visibility issues where some fields should have remained hidden. diff --git a/prompts/skills/100-go-mistakes/references/mistake-100-not-understanding-the-impacts-of-running-go-in-docker-and-kubernetes.md b/prompts/skills/100-go-mistakes/references/mistake-100-not-understanding-the-impacts-of-running-go-in-docker-and-kubernetes.md index a31b72a..67c197c 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-100-not-understanding-the-impacts-of-running-go-in-docker-and-kubernetes.md +++ b/prompts/skills/100-go-mistakes/references/mistake-100-not-understanding-the-impacts-of-running-go-in-docker-and-kubernetes.md @@ -1,3 +1,13 @@ # Mistake #100: Not understanding the impacts of running Go in Docker and Kubernetes -[Documentation for mistake #100 from 100go.co] +#### TL;DR + +Be aware that `GOMAXPROCS` defaults to the number of OS-visible CPUs, not the container's CPU limit. Use libraries like `automaxprocs` to set it correctly based on cgroup limits. + +When running Go applications in Docker or Kubernetes, several runtime behaviors can lead to performance issues: + +* **GOMAXPROCS**: By default, `runtime.GOMAXPROCS` is set to the number of CPUs visible to the OS, not the container's CPU limit. For example, on a 64-core host with a container limited to 2 CPUs, Go will create 64 OS threads for scheduling. This leads to excessive context switching and reduced performance. Use `go.uber.org/automaxprocs` to automatically set `GOMAXPROCS` based on the cgroup CPU quota. + +* **Memory limits**: Similarly, Go's GC doesn't natively know about container memory limits (prior to `GOMEMLIMIT` in Go 1.19). Without `GOMEMLIMIT`, the GC may allow heap growth beyond the container's memory limit, causing OOM kills. + +* **Minimal Docker images**: Use multi-stage builds with `scratch` or `distroless` base images to reduce image size and attack surface. Remember to include CA certificates if making HTTPS calls. diff --git a/prompts/skills/100-go-mistakes/references/mistake-11-not-using-the-functional-options-pattern.md b/prompts/skills/100-go-mistakes/references/mistake-11-not-using-the-functional-options-pattern.md index 56f7cae..8179281 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-11-not-using-the-functional-options-pattern.md +++ b/prompts/skills/100-go-mistakes/references/mistake-11-not-using-the-functional-options-pattern.md @@ -1,7 +1,6 @@ # Mistake #11: Not using the functional options pattern #### TL;DR -TL;DR To handle options conveniently and in an API-friendly manner, use the functional options pattern. @@ -18,10 +17,10 @@ type Option func(options *options) error func WithPort(port int) Option { return func(options *options) error { - if port < 0 { + if port < 0 { return errors.New("port should be positive") } - options.port = &port + options.port = &port return nil } } @@ -29,7 +28,7 @@ func WithPort(port int) Option { func NewServer(addr string, opts ...Option) ( *http.Server, error) { var options options for _, opt := range opts { - err := opt(&options) + err := opt(&options) if err != nil { return nil, err } diff --git a/prompts/skills/100-go-mistakes/references/mistake-13-creating-utility-packages.md b/prompts/skills/100-go-mistakes/references/mistake-13-creating-utility-packages.md index 5b45254..0e6c4f6 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-13-creating-utility-packages.md +++ b/prompts/skills/100-go-mistakes/references/mistake-13-creating-utility-packages.md @@ -1,7 +1,6 @@ # Mistake #13: Creating utility packages #### TL;DR -TL;DR Naming is a critical piece of application design. Creating packages such as `common`, `util`, and `shared` doesn’t bring much value for the reader. Refactor such packages into meaningful and specific package names. diff --git a/prompts/skills/100-go-mistakes/references/mistake-14-ignoring-package-name-collisions.md b/prompts/skills/100-go-mistakes/references/mistake-14-ignoring-package-name-collisions.md index 98a050c..eee965c 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-14-ignoring-package-name-collisions.md +++ b/prompts/skills/100-go-mistakes/references/mistake-14-ignoring-package-name-collisions.md @@ -1,7 +1,6 @@ # Mistake #14: Ignoring package name collisions #### TL;DR -TL;DR To avoid naming collisions between variables and packages, leading to confusion or perhaps even bugs, use unique names for each one. If this isn’t feasible, use an import alias to change the qualifier to differentiate the package name from the variable name, or think of a better name. diff --git a/prompts/skills/100-go-mistakes/references/mistake-15-missing-code-documentation.md b/prompts/skills/100-go-mistakes/references/mistake-15-missing-code-documentation.md index 2151605..f9fdc8e 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-15-missing-code-documentation.md +++ b/prompts/skills/100-go-mistakes/references/mistake-15-missing-code-documentation.md @@ -1,7 +1,6 @@ # Mistake #15: Missing code documentation #### TL;DR -TL;DR To help clients and maintainers understand your code’s purpose, document exported elements. diff --git a/prompts/skills/100-go-mistakes/references/mistake-16-not-using-linters.md b/prompts/skills/100-go-mistakes/references/mistake-16-not-using-linters.md index 440e0c5..1106f75 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-16-not-using-linters.md +++ b/prompts/skills/100-go-mistakes/references/mistake-16-not-using-linters.md @@ -1,7 +1,6 @@ # Mistake #16: Not using linters #### TL;DR -TL;DR To improve code quality and consistency, use linters and formatters. diff --git a/prompts/skills/100-go-mistakes/references/mistake-17-creating-confusion-with-octal-literals.md b/prompts/skills/100-go-mistakes/references/mistake-17-creating-confusion-with-octal-literals.md index 7c89902..010f574 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-17-creating-confusion-with-octal-literals.md +++ b/prompts/skills/100-go-mistakes/references/mistake-17-creating-confusion-with-octal-literals.md @@ -1,7 +1,6 @@ # Mistake #17: Creating confusion with octal literals #### TL;DR -TL;DR When reading existing code, bear in mind that integer literals starting with `0` are octal numbers. Also, to improve readability, make octal integers explicit by prefixing them with `0o`. diff --git a/prompts/skills/100-go-mistakes/references/mistake-18-neglecting-integer-overflows.md b/prompts/skills/100-go-mistakes/references/mistake-18-neglecting-integer-overflows.md index 8ab3127..f0e35ee 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-18-neglecting-integer-overflows.md +++ b/prompts/skills/100-go-mistakes/references/mistake-18-neglecting-integer-overflows.md @@ -1,7 +1,6 @@ # Mistake #18: Neglecting integer overflows #### TL;DR -TL;DR Because integer overflows and underflows are handled silently in Go, you can implement your own functions to catch them. diff --git a/prompts/skills/100-go-mistakes/references/mistake-19-not-understanding-floating-points.md b/prompts/skills/100-go-mistakes/references/mistake-19-not-understanding-floating-points.md index e95be48..85867e6 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-19-not-understanding-floating-points.md +++ b/prompts/skills/100-go-mistakes/references/mistake-19-not-understanding-floating-points.md @@ -1,7 +1,6 @@ # Mistake #19: Not understanding floating-points #### TL;DR -TL;DR Making floating-point comparisons within a given delta can ensure that your code is portable. When performing addition or subtraction, group the operations with a similar order of magnitude to favor accuracy. Also, perform multiplication and division before addition and subtraction. diff --git a/prompts/skills/100-go-mistakes/references/mistake-20-not-understanding-slice-length-and-capacity.md b/prompts/skills/100-go-mistakes/references/mistake-20-not-understanding-slice-length-and-capacity.md index 473d0e2..7fb89cb 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-20-not-understanding-slice-length-and-capacity.md +++ b/prompts/skills/100-go-mistakes/references/mistake-20-not-understanding-slice-length-and-capacity.md @@ -1,7 +1,6 @@ # Mistake #20: Not understanding slice length and capacity #### TL;DR -TL;DR Understanding the difference between slice length and capacity should be part of a Go developer’s core knowledge. The slice length is the number of available elements in the slice, whereas the slice capacity is the number of elements in the backing array. diff --git a/prompts/skills/100-go-mistakes/references/mistake-21-inefficient-slice-initialization.md b/prompts/skills/100-go-mistakes/references/mistake-21-inefficient-slice-initialization.md index 49f3a4e..024a2c1 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-21-inefficient-slice-initialization.md +++ b/prompts/skills/100-go-mistakes/references/mistake-21-inefficient-slice-initialization.md @@ -1,7 +1,6 @@ # Mistake #21: Inefficient slice initialization #### TL;DR -TL;DR When creating a slice, initialize it with a given length or capacity if its length is already known. This reduces the number of allocations and improves performance. diff --git a/prompts/skills/100-go-mistakes/references/mistake-22-being-confused-about-nil-vs-empty-slice.md b/prompts/skills/100-go-mistakes/references/mistake-22-being-confused-about-nil-vs-empty-slice.md index f850dbc..528508e 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-22-being-confused-about-nil-vs-empty-slice.md +++ b/prompts/skills/100-go-mistakes/references/mistake-22-being-confused-about-nil-vs-empty-slice.md @@ -1,7 +1,6 @@ # Mistake #22: Being confused about nil vs. empty slice #### TL;DR -TL;DR To prevent common confusions such as when using the `encoding/json` or the `reflect` package, you need to understand the difference between nil and empty slices. Both are zero-length, zero-capacity slices, but only a nil slice doesn’t require allocation. diff --git a/prompts/skills/100-go-mistakes/references/mistake-23-not-properly-checking-if-a-slice-is-empty.md b/prompts/skills/100-go-mistakes/references/mistake-23-not-properly-checking-if-a-slice-is-empty.md index 25c6f29..9b734e8 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-23-not-properly-checking-if-a-slice-is-empty.md +++ b/prompts/skills/100-go-mistakes/references/mistake-23-not-properly-checking-if-a-slice-is-empty.md @@ -1,7 +1,6 @@ # Mistake #23: Not properly checking if a slice is empty #### TL;DR -TL;DR To check if a slice doesn’t contain any element, check its length. This check works regardless of whether the slice is `nil` or empty. The same goes for maps. To design unambiguous APIs, you shouldn’t distinguish between nil and empty slices. diff --git a/prompts/skills/100-go-mistakes/references/mistake-24-not-making-slice-copies-correctly.md b/prompts/skills/100-go-mistakes/references/mistake-24-not-making-slice-copies-correctly.md index a16b53f..0fa368a 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-24-not-making-slice-copies-correctly.md +++ b/prompts/skills/100-go-mistakes/references/mistake-24-not-making-slice-copies-correctly.md @@ -1,7 +1,6 @@ # Mistake #24: Not making slice copies correctly #### TL;DR -TL;DR To copy one slice to another using the `copy` built-in function, remember that the number of copied elements corresponds to the minimum between the two slice’s lengths. diff --git a/prompts/skills/100-go-mistakes/references/mistake-25-unexpected-side-effects-using-slice-append.md b/prompts/skills/100-go-mistakes/references/mistake-25-unexpected-side-effects-using-slice-append.md index e1b513d..b38083f 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-25-unexpected-side-effects-using-slice-append.md +++ b/prompts/skills/100-go-mistakes/references/mistake-25-unexpected-side-effects-using-slice-append.md @@ -1,14 +1,12 @@ # Mistake #25: Unexpected side effects using slice append #### TL;DR -TL;DR Using copy or the full slice expression is a way to prevent `append` from creating conflicts if two different functions use slices backed by the same array. However, only a slice copy prevents memory leaks if you want to shrink a large slice. When using slicing, we must remember that we can face a situation leading to unintended side effects. If the resulting slice has a length smaller than its capacity, append can mutate the original slice. If we want to restrict the range of possible side effects, we can use either a slice copy or the full slice expression, which prevents us from doing a copy. -#### TL;DR -Note +#### Note `s[low:high:max]` (full slice expression): This statement creates a slice similar to the one created with `s[low:high]`, except that the resulting slice’s capacity is equal to `max - low`. diff --git a/prompts/skills/100-go-mistakes/references/mistake-26-slices-and-memory-leaks.md b/prompts/skills/100-go-mistakes/references/mistake-26-slices-and-memory-leaks.md index a5cd33e..78474a3 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-26-slices-and-memory-leaks.md +++ b/prompts/skills/100-go-mistakes/references/mistake-26-slices-and-memory-leaks.md @@ -1,7 +1,6 @@ # Mistake #26: Slices and memory leaks #### TL;DR -TL;DR Working with a slice of pointers or structs with pointer fields, you can avoid memory leaks by marking as nil the elements excluded by a slicing operation. diff --git a/prompts/skills/100-go-mistakes/references/mistake-27-inefficient-map-initialization.md b/prompts/skills/100-go-mistakes/references/mistake-27-inefficient-map-initialization.md index bff64ac..3328921 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-27-inefficient-map-initialization.md +++ b/prompts/skills/100-go-mistakes/references/mistake-27-inefficient-map-initialization.md @@ -1,7 +1,6 @@ # Mistake #27: Inefficient map initialization #### TL;DR -TL;DR When creating a map, initialize it with a given length if its length is already known. This reduces the number of allocations and improves performance. diff --git a/prompts/skills/100-go-mistakes/references/mistake-28-maps-and-memory-leaks.md b/prompts/skills/100-go-mistakes/references/mistake-28-maps-and-memory-leaks.md index b281d21..7fb8f7e 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-28-maps-and-memory-leaks.md +++ b/prompts/skills/100-go-mistakes/references/mistake-28-maps-and-memory-leaks.md @@ -1,7 +1,6 @@ # Mistake #28: Maps and memory leaks #### TL;DR -TL;DR A map can always grow in memory, but it never shrinks. Hence, if it leads to some memory issues, you can try different options, such as forcing Go to recreate the map or using pointers. diff --git a/prompts/skills/100-go-mistakes/references/mistake-29-comparing-values-incorrectly.md b/prompts/skills/100-go-mistakes/references/mistake-29-comparing-values-incorrectly.md index 2d99d7e..daef02a 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-29-comparing-values-incorrectly.md +++ b/prompts/skills/100-go-mistakes/references/mistake-29-comparing-values-incorrectly.md @@ -1,7 +1,6 @@ # Mistake #29: Comparing values incorrectly #### TL;DR -TL;DR To compare types in Go, you can use the == and != operators if two types are comparable: Booleans, numerals, strings, pointers, channels, and structs are composed entirely of comparable types. Otherwise, you can either use `reflect.DeepEqual` and pay the price of reflection or use custom implementations and libraries. @@ -15,10 +14,9 @@ It’s essential to understand how to use `==` and `!=` to make comparisons effe * Pointers—Compare whether two pointers point to the same value in memory or if both are nil. * Structs and arrays—Compare whether they are composed of similar types. -#### TL;DR -Note +#### Note -We can also use the `?`, `>=`, `<`, and `>` operators with numeric types to compare values and with strings to compare their lexical order. +We can also use the `<=`, `>=`, `<`, and `>` operators with numeric types to compare values and with strings to compare their lexical order. If operands are not comparable (e.g., slices and maps), we have to use other options such as reflection. Reflection is a form of metaprogramming, and it refers to the ability of an application to introspect and modify its structure and behavior. For example, in Go, we can use `reflect.DeepEqual`. This function reports whether two elements are deeply equal by recursively traversing two values. The elements it accepts are basic types plus arrays, structs, slices, maps, pointers, interfaces, and functions. Yet, the main catch is the performance penalty. diff --git a/prompts/skills/100-go-mistakes/references/mistake-30-ignoring-that-elements-are-copied-in-range-loops.md b/prompts/skills/100-go-mistakes/references/mistake-30-ignoring-that-elements-are-copied-in-range-loops.md index 32d79c5..83a9c1a 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-30-ignoring-that-elements-are-copied-in-range-loops.md +++ b/prompts/skills/100-go-mistakes/references/mistake-30-ignoring-that-elements-are-copied-in-range-loops.md @@ -1,3 +1,13 @@ # Mistake #30: Ignoring that elements are copied in range loops -[Documentation for mistake #30 from 100go.co] +#### TL;DR + +The value element in a range loop is a copy. Therefore, to mutate a struct, use the index to access it directly or use a classic for loop with pointers. + +A range loop allows iterating over different data structures: String, Array, Pointer to an array, Slice, Map, Receiving channel. + +Compared to a classic for loop, a range loop is a convenient way to iterate over all the elements of one of these data structures, thanks to its concise syntax. + +Yet, we should remember that the value element in a range loop is a copy. Therefore, if the value is a struct we need to mutate, we will only update the copy, not the element itself, unless the value or field we modify is a pointer. The favored options are to access the element via the index using a range loop or a classic for loop. + +[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/30-range-loop-element-copied/) diff --git a/prompts/skills/100-go-mistakes/references/mistake-31-ignoring-how-arguments-are-evaluated-in-range-loops-channels-and-arrays.md b/prompts/skills/100-go-mistakes/references/mistake-31-ignoring-how-arguments-are-evaluated-in-range-loops-channels-and-arrays.md index 9775d58..7a16c53 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-31-ignoring-how-arguments-are-evaluated-in-range-loops-channels-and-arrays.md +++ b/prompts/skills/100-go-mistakes/references/mistake-31-ignoring-how-arguments-are-evaluated-in-range-loops-channels-and-arrays.md @@ -1,3 +1,19 @@ # Mistake #31: Ignoring how arguments are evaluated in range loops (channels and arrays) -[Documentation for mistake #31 from 100go.co] +#### TL;DR + +The range loop expression is evaluated only once, before the beginning of the loop, by doing a copy. Be aware of this to avoid common mistakes. + +The range loop evaluates the provided expression only once, before the beginning of the loop, by doing a copy (regardless of the type). We should remember this behavior to avoid common mistakes that might, for example, lead us to access the wrong element. For example: + + a := [3]int{0, 1, 2} + for i, v := range a { + a[2] = 10 + if i == 2 { + fmt.Println(v) + } + } + +This code updates the last index to 10. However, if we run this code, it does not print 10; it prints 2, because the range expression `a` was copied before the loop started. + +[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/31-range-loop-arg-evaluation/) diff --git a/prompts/skills/100-go-mistakes/references/mistake-32-ignoring-the-impacts-of-using-pointer-elements-in-range-loops.md b/prompts/skills/100-go-mistakes/references/mistake-32-ignoring-the-impacts-of-using-pointer-elements-in-range-loops.md index bad6234..c6e02c3 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-32-ignoring-the-impacts-of-using-pointer-elements-in-range-loops.md +++ b/prompts/skills/100-go-mistakes/references/mistake-32-ignoring-the-impacts-of-using-pointer-elements-in-range-loops.md @@ -1,3 +1,16 @@ # Mistake #32: Ignoring the impacts of using pointer elements in range loops -[Documentation for mistake #32 from 100go.co] +#### TL;DR + +When iterating over a data structure using a range loop and storing the pointer of each element, be aware that all pointers will point to the same element: the last one. Use a local variable or index access instead. + +When using a range loop with pointer elements, a common mistake is to store pointers to the loop variable. Since the loop variable is reused across iterations, all stored pointers end up referencing the same (last) element. To fix this, create a local copy within the loop or access elements by index. + + s := []int{1, 2, 3} + var ptrs []*int + for _, v := range s { + v := v // Create a local copy + ptrs = append(ptrs, &v) + } + +Note: As of Go 1.22, the loop variable is redefined per iteration, which eliminates this issue in newer Go versions. diff --git a/prompts/skills/100-go-mistakes/references/mistake-33-making-wrong-assumptions-during-map-iterations-ordering-and-map-insert-during-iteration.md b/prompts/skills/100-go-mistakes/references/mistake-33-making-wrong-assumptions-during-map-iterations-ordering-and-map-insert-during-iteration.md index b5eafb8..10c3f19 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-33-making-wrong-assumptions-during-map-iterations-ordering-and-map-insert-during-iteration.md +++ b/prompts/skills/100-go-mistakes/references/mistake-33-making-wrong-assumptions-during-map-iterations-ordering-and-map-insert-during-iteration.md @@ -1,7 +1,6 @@ # Mistake #33: Making wrong assumptions during map iterations (ordering and map insert during iteration) #### TL;DR -TL;DR To ensure predictable outputs when using maps, remember that a map data structure: diff --git a/prompts/skills/100-go-mistakes/references/mistake-34-ignoring-how-the-break-statement-works.md b/prompts/skills/100-go-mistakes/references/mistake-34-ignoring-how-the-break-statement-works.md index 9bdb601..594de48 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-34-ignoring-how-the-break-statement-works.md +++ b/prompts/skills/100-go-mistakes/references/mistake-34-ignoring-how-the-break-statement-works.md @@ -1,3 +1,36 @@ # Mistake #34: Ignoring how the break statement works -[Documentation for mistake #34 from 100go.co] +#### TL;DR + +A `break` statement terminates the execution of the innermost `for`, `switch`, or `select` statement. Use labels to break out of an outer loop from within a `switch` or `select`. + +A break statement is commonly used to terminate the execution of a loop. When loops are used in conjunction with switch or select, developers frequently make the mistake of breaking the wrong statement. For example: + + for i := 0; i < 5; i++ { + fmt.Printf("%d ", i) + switch i { + default: + case 2: + break + } + } + +The break statement doesn't terminate the `for` loop: it terminates the `switch` statement, instead. Hence, instead of iterating from 0 to 2, this code iterates from 0 to 4: `0 1 2 3 4`. + +One essential rule to keep in mind is that a `break` statement terminates the execution of the innermost `for`, `switch`, or `select` statement. + +To break the loop instead of the `switch` statement, the most idiomatic way is to use a label: + + loop: + for i := 0; i < 5; i++ { + fmt.Printf("%d ", i) + switch i { + default: + case 2: + break loop + } + } + +Here, we associate the `loop` label with the `for` loop. Then, because we provide the `loop` label to the `break` statement, it breaks the loop, not the switch. Therefore, this new version will print `0 1 2`, as we expected. + +[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/34-break/main.go) diff --git a/prompts/skills/100-go-mistakes/references/mistake-35-using-defer-inside-a-loop.md b/prompts/skills/100-go-mistakes/references/mistake-35-using-defer-inside-a-loop.md index 54b3a20..f0dff21 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-35-using-defer-inside-a-loop.md +++ b/prompts/skills/100-go-mistakes/references/mistake-35-using-defer-inside-a-loop.md @@ -1,3 +1,46 @@ # Mistake #35: Using defer inside a loop -[Documentation for mistake #35 from 100go.co] +#### TL;DR + +A `defer` call is executed not during each loop iteration but when the surrounding function returns. Be cautious about using `defer` inside loops; extract the loop body into a function to ensure `defer` executes per iteration. + +The `defer` statement delays a call's execution until the surrounding function returns. One common mistake with `defer` is to forget that it schedules a function call when the surrounding function returns, not when the current block or iteration completes. For example: + + func readFiles(ch <-chan string) error { + for path := range ch { + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + // Do something with file + } + return nil + } + +The `defer` calls are executed not during each loop iteration but when the `readFiles` function returns. If `readFiles` doesn't return, the file descriptors will be kept open forever, causing leaks. + +One common option to fix this problem is to create a surrounding function after `defer`, called during each iteration: + + func readFiles(ch <-chan string) error { + for path := range ch { + if err := readFile(path); err != nil { + return err + } + } + return nil + } + + func readFile(path string) error { + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + // Do something with file + return nil + } + +Another solution is to make the `readFile` function a closure but intrinsically, this remains the same solution: adding another surrounding function to execute the `defer` calls during each iteration. + +[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/35-defer-loop/main.go) diff --git a/prompts/skills/100-go-mistakes/references/mistake-36-not-understanding-the-concept-of-rune.md b/prompts/skills/100-go-mistakes/references/mistake-36-not-understanding-the-concept-of-rune.md index d3f174f..c4c97e7 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-36-not-understanding-the-concept-of-rune.md +++ b/prompts/skills/100-go-mistakes/references/mistake-36-not-understanding-the-concept-of-rune.md @@ -1,7 +1,6 @@ # Mistake #36: Not understanding the concept of rune #### TL;DR -TL;DR Understanding that a rune corresponds to the concept of a Unicode code point and that it can be composed of multiple bytes should be part of the Go developer’s core knowledge to work accurately with strings. diff --git a/prompts/skills/100-go-mistakes/references/mistake-37-inaccurate-string-iteration.md b/prompts/skills/100-go-mistakes/references/mistake-37-inaccurate-string-iteration.md index 9693598..b3e83bd 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-37-inaccurate-string-iteration.md +++ b/prompts/skills/100-go-mistakes/references/mistake-37-inaccurate-string-iteration.md @@ -1,7 +1,6 @@ # Mistake #37: Inaccurate string iteration #### TL;DR -TL;DR Iterating on a string with the `range` operator iterates on the runes with the index corresponding to the starting index of the rune’s byte sequence. To access a specific rune index (such as the third rune), convert the string into a `[]rune`. diff --git a/prompts/skills/100-go-mistakes/references/mistake-38-misusing-trim-functions.md b/prompts/skills/100-go-mistakes/references/mistake-38-misusing-trim-functions.md index 5faf065..177c373 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-38-misusing-trim-functions.md +++ b/prompts/skills/100-go-mistakes/references/mistake-38-misusing-trim-functions.md @@ -1,7 +1,6 @@ # Mistake #38: Misusing trim functions #### TL;DR -TL;DR `strings.TrimRight`/`strings.TrimLeft` removes all the trailing/leading runes contained in a given set, whereas `strings.TrimSuffix`/`strings.TrimPrefix` returns a string without a provided suffix/prefix. diff --git a/prompts/skills/100-go-mistakes/references/mistake-39-under-optimized-strings-concatenation.md b/prompts/skills/100-go-mistakes/references/mistake-39-under-optimized-strings-concatenation.md index 5949474..862225c 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-39-under-optimized-strings-concatenation.md +++ b/prompts/skills/100-go-mistakes/references/mistake-39-under-optimized-strings-concatenation.md @@ -1,7 +1,6 @@ # Mistake #39: Under-optimized strings concatenation #### TL;DR -TL;DR Concatenating a list of strings should be done with `strings.Builder` to prevent allocating a new string during each iteration. @@ -29,8 +28,7 @@ func concat(values []string) string { During each iteration, we constructed the resulting string by calling the `WriteString` method that appends the content of value to its internal buffer, hence minimizing memory copying. -#### TL;DR -Note +#### Note `WriteString` returns an error as the second output, but we purposely ignore it. Indeed, this method will never return a non-nil error. So what’s the purpose of this method returning an error as part of its signature? `strings.Builder` implements the `io.StringWriter` interface, which contains a single method: `WriteString(s string) (n int, err error)`. Hence, to comply with this interface, `WriteString` must return an error. @@ -38,7 +36,7 @@ Internally, `strings.Builder` holds a byte slice. Each call to `WriteString` res func concat(values []string) string { total := 0 - for i := 0; i < len(values); i++ { + for i := 0; i < len(values); i++ { total += len(values[i]) } diff --git a/prompts/skills/100-go-mistakes/references/mistake-40-useless-string-conversions.md b/prompts/skills/100-go-mistakes/references/mistake-40-useless-string-conversions.md index 3a46cc7..5301d23 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-40-useless-string-conversions.md +++ b/prompts/skills/100-go-mistakes/references/mistake-40-useless-string-conversions.md @@ -1,7 +1,6 @@ # Mistake #40: Useless string conversions #### TL;DR -TL;DR Remembering that the `bytes` package offers the same operations as the `strings` package can help avoid extra byte/string conversions. diff --git a/prompts/skills/100-go-mistakes/references/mistake-41-substring-and-memory-leaks.md b/prompts/skills/100-go-mistakes/references/mistake-41-substring-and-memory-leaks.md index 9042bb6..5b5d0e6 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-41-substring-and-memory-leaks.md +++ b/prompts/skills/100-go-mistakes/references/mistake-41-substring-and-memory-leaks.md @@ -1,7 +1,6 @@ # Mistake #41: Substring and memory leaks #### TL;DR -TL;DR Using copies instead of substrings can prevent memory leaks, as the string returned by a substring operation will be backed by the same byte array. diff --git a/prompts/skills/100-go-mistakes/references/mistake-42-not-knowing-which-type-of-receiver-to-use.md b/prompts/skills/100-go-mistakes/references/mistake-42-not-knowing-which-type-of-receiver-to-use.md index aa51515..1d7bd35 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-42-not-knowing-which-type-of-receiver-to-use.md +++ b/prompts/skills/100-go-mistakes/references/mistake-42-not-knowing-which-type-of-receiver-to-use.md @@ -1,7 +1,6 @@ # Mistake #42: Not knowing which type of receiver to use #### TL;DR -TL;DR The decision whether to use a value or a pointer receiver should be made based on factors such as the type, whether it has to be mutated, whether it contains a field that can’t be copied, and how large the object is. When in doubt, use a pointer receiver. diff --git a/prompts/skills/100-go-mistakes/references/mistake-43-never-using-named-result-parameters.md b/prompts/skills/100-go-mistakes/references/mistake-43-never-using-named-result-parameters.md index d8a9857..3bc2ac4 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-43-never-using-named-result-parameters.md +++ b/prompts/skills/100-go-mistakes/references/mistake-43-never-using-named-result-parameters.md @@ -1,7 +1,6 @@ # Mistake #43: Never using named result parameters #### TL;DR -TL;DR Using named result parameters can be an efficient way to improve the readability of a function/method, especially if multiple result parameters have the same type. In some cases, this approach can also be convenient because named result parameters are initialized to their zero value. But be cautious about potential side effects. diff --git a/prompts/skills/100-go-mistakes/references/mistake-44-unintended-side-effects-with-named-result-parameters.md b/prompts/skills/100-go-mistakes/references/mistake-44-unintended-side-effects-with-named-result-parameters.md index 9cea481..2230494 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-44-unintended-side-effects-with-named-result-parameters.md +++ b/prompts/skills/100-go-mistakes/references/mistake-44-unintended-side-effects-with-named-result-parameters.md @@ -1,7 +1,6 @@ # Mistake #44: Unintended side effects with named result parameters #### TL;DR -TL;DR See #43. diff --git a/prompts/skills/100-go-mistakes/references/mistake-45-returning-a-nil-receiver.md b/prompts/skills/100-go-mistakes/references/mistake-45-returning-a-nil-receiver.md index d0463ff..a2f869b 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-45-returning-a-nil-receiver.md +++ b/prompts/skills/100-go-mistakes/references/mistake-45-returning-a-nil-receiver.md @@ -1,7 +1,6 @@ # Mistake #45: Returning a nil receiver #### TL;DR -TL;DR When returning an interface, be cautious about not returning a nil pointer but an explicit nil value. Otherwise, unintended consequences may occur and the caller will receive a non-nil value. diff --git a/prompts/skills/100-go-mistakes/references/mistake-46-using-a-filename-as-a-function-input.md b/prompts/skills/100-go-mistakes/references/mistake-46-using-a-filename-as-a-function-input.md index d26e4b8..860f9ef 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-46-using-a-filename-as-a-function-input.md +++ b/prompts/skills/100-go-mistakes/references/mistake-46-using-a-filename-as-a-function-input.md @@ -1,7 +1,6 @@ # Mistake #46: Using a filename as a function input #### TL;DR -TL;DR Designing functions to receive `io.Reader` types instead of filenames improves the reusability of a function and makes testing easier. diff --git a/prompts/skills/100-go-mistakes/references/mistake-47-ignoring-how-defer-arguments-and-receivers-are-evaluated.md b/prompts/skills/100-go-mistakes/references/mistake-47-ignoring-how-defer-arguments-and-receivers-are-evaluated.md index 167877f..3e6818f 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-47-ignoring-how-defer-arguments-and-receivers-are-evaluated.md +++ b/prompts/skills/100-go-mistakes/references/mistake-47-ignoring-how-defer-arguments-and-receivers-are-evaluated.md @@ -1,3 +1,60 @@ # Mistake #47: Ignoring how defer arguments and receivers are evaluated -[Documentation for mistake #47 from 100go.co] +#### TL;DR + +In a `defer` function, arguments are evaluated right away, not once the surrounding function returns. To defer a call with an updated value, use pointers or closures. + +In a `defer` function the arguments are evaluated right away, not once the surrounding function returns. For example, in this code, we always call `notify` and `incrementCounter` with the same status: an empty string. + + const ( + StatusSuccess = "success" + StatusErrorFoo = "error_foo" + StatusErrorBar = "error_bar" + ) + + func f() error { + var status string + defer notify(status) + defer incrementCounter(status) + + if err := foo(); err != nil { + status = StatusErrorFoo + return err + } + + if err := bar(); err != nil { + status = StatusErrorBar + return err + } + + status = StatusSuccess + return nil + } + +Two leading options if we want to keep using `defer`: + +The first solution is to pass a string pointer: + + func f() error { + var status string + defer notify(&status) + defer incrementCounter(&status) + // The rest of the function unchanged + } + +There's another solution: calling a closure as a `defer` statement: + + func f() error { + var status string + defer func() { + notify(status) + incrementCounter(status) + }() + // The rest of the function unchanged + } + +Here, we wrap the calls within a closure. This closure references the status variable from outside its body. Therefore, `status` is evaluated once the closure is executed, not when we call `defer`. + +Let's also note this behavior applies with method receivers: the receiver is evaluated immediately. + +[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/47-defer-evaluation/) diff --git a/prompts/skills/100-go-mistakes/references/mistake-48-panicking.md b/prompts/skills/100-go-mistakes/references/mistake-48-panicking.md index e2e650d..e2ac88e 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-48-panicking.md +++ b/prompts/skills/100-go-mistakes/references/mistake-48-panicking.md @@ -1,7 +1,6 @@ # Mistake #48: Panicking #### TL;DR -TL;DR Using `panic` is an option to deal with errors in Go. However, it should only be used sparingly in unrecoverable conditions: for example, to signal a programmer error or when you fail to load a mandatory dependency. diff --git a/prompts/skills/100-go-mistakes/references/mistake-49-ignoring-when-to-wrap-an-error.md b/prompts/skills/100-go-mistakes/references/mistake-49-ignoring-when-to-wrap-an-error.md index 5284e0b..dffec32 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-49-ignoring-when-to-wrap-an-error.md +++ b/prompts/skills/100-go-mistakes/references/mistake-49-ignoring-when-to-wrap-an-error.md @@ -1,7 +1,6 @@ # Mistake #49: Ignoring when to wrap an error #### TL;DR -TL;DR Wrapping an error allows you to mark an error and/or provide additional context. However, error wrapping creates potential coupling as it makes the source error available for the caller. If you want to prevent that, don’t use error wrapping. diff --git a/prompts/skills/100-go-mistakes/references/mistake-50-comparing-an-error-type-inaccurately.md b/prompts/skills/100-go-mistakes/references/mistake-50-comparing-an-error-type-inaccurately.md index 9d9a78d..4a0f122 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-50-comparing-an-error-type-inaccurately.md +++ b/prompts/skills/100-go-mistakes/references/mistake-50-comparing-an-error-type-inaccurately.md @@ -1,7 +1,6 @@ # Mistake #50: Comparing an error type inaccurately #### TL;DR -TL;DR If you use Go 1.13 error wrapping with the `%w` directive and `fmt.Errorf`, comparing an error against a type has to be done using `errors.As`. Otherwise, if the returned error you want to check is wrapped, it will fail the checks. diff --git a/prompts/skills/100-go-mistakes/references/mistake-51-comparing-an-error-value-inaccurately.md b/prompts/skills/100-go-mistakes/references/mistake-51-comparing-an-error-value-inaccurately.md index 23dce0e..6ad5963 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-51-comparing-an-error-value-inaccurately.md +++ b/prompts/skills/100-go-mistakes/references/mistake-51-comparing-an-error-value-inaccurately.md @@ -1,7 +1,6 @@ # Mistake #51: Comparing an error value inaccurately #### TL;DR -TL;DR If you use Go 1.13 error wrapping with the `%w` directive and `fmt.Errorf`, comparing an error against or a value has to be done using `errors.As`. Otherwise, if the returned error you want to check is wrapped, it will fail the checks. diff --git a/prompts/skills/100-go-mistakes/references/mistake-52-handling-an-error-twice.md b/prompts/skills/100-go-mistakes/references/mistake-52-handling-an-error-twice.md index 812d3f0..2e2869a 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-52-handling-an-error-twice.md +++ b/prompts/skills/100-go-mistakes/references/mistake-52-handling-an-error-twice.md @@ -1,7 +1,6 @@ # Mistake #52: Handling an error twice #### TL;DR -TL;DR In most situations, an error should be handled only once. Logging an error is handling an error. Therefore, you have to choose between logging or returning an error. In many cases, error wrapping is the solution as it allows you to provide additional context to an error and return the source error. diff --git a/prompts/skills/100-go-mistakes/references/mistake-53-not-handling-an-error.md b/prompts/skills/100-go-mistakes/references/mistake-53-not-handling-an-error.md index fe98f60..f7bc7c7 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-53-not-handling-an-error.md +++ b/prompts/skills/100-go-mistakes/references/mistake-53-not-handling-an-error.md @@ -1,7 +1,6 @@ # Mistake #53: Not handling an error #### TL;DR -TL;DR Ignoring an error, whether during a function call or in a `defer` function, should be done explicitly using the blank identifier. Otherwise, future readers may be confused about whether it was intentional or a miss. diff --git a/prompts/skills/100-go-mistakes/references/mistake-54-not-handling-defer-errors.md b/prompts/skills/100-go-mistakes/references/mistake-54-not-handling-defer-errors.md index 80dc62a..111ead8 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-54-not-handling-defer-errors.md +++ b/prompts/skills/100-go-mistakes/references/mistake-54-not-handling-defer-errors.md @@ -1,3 +1,30 @@ # Mistake #54: Not handling defer errors -[Documentation for mistake #54 from 100go.co] +#### TL;DR + +When we want to ignore an error in a `defer` call, use the blank identifier (`_`) to make it explicit and add a comment explaining why. + +Consider the following code: + + func f() { + // ... + notify() // Error handling is omitted + } + + func notify() error { + // ... + } + +From a maintainability perspective, the code can lead to some issues. A reader looking at it cannot tell whether the error was intentionally ignored or accidentally forgotten. + +For these reasons, when we want to ignore an error, there's only one way to do it, using the blank identifier (`_`): + + _ = notify() + +In terms of compilation and run time, this approach doesn't change anything compared to the first piece of code. But this new version makes explicit that we aren't interested in the error. Also, we can add a comment that indicates the rationale for why an error is ignored: + + // At-most once delivery. + // Hence, it's accepted to miss some of them in case of errors. + _ = notify() + +[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/54-defer-errors/main.go) diff --git a/prompts/skills/100-go-mistakes/references/mistake-55-mixing-up-concurrency-and-parallelism.md b/prompts/skills/100-go-mistakes/references/mistake-55-mixing-up-concurrency-and-parallelism.md index a63149b..f619e0b 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-55-mixing-up-concurrency-and-parallelism.md +++ b/prompts/skills/100-go-mistakes/references/mistake-55-mixing-up-concurrency-and-parallelism.md @@ -1,7 +1,6 @@ # Mistake #55: Mixing up concurrency and parallelism #### TL;DR -TL;DR Understanding the fundamental differences between concurrency and parallelism is a cornerstone of the Go developer’s knowledge. Concurrency is about structure, whereas parallelism is about execution. diff --git a/prompts/skills/100-go-mistakes/references/mistake-56-thinking-concurrency-is-always-faster.md b/prompts/skills/100-go-mistakes/references/mistake-56-thinking-concurrency-is-always-faster.md index 203520f..53e09f6 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-56-thinking-concurrency-is-always-faster.md +++ b/prompts/skills/100-go-mistakes/references/mistake-56-thinking-concurrency-is-always-faster.md @@ -1,7 +1,6 @@ # Mistake #56: Thinking concurrency is always faster #### TL;DR -TL;DR To be a proficient developer, you must acknowledge that concurrency isn’t always faster. Solutions involving parallelization of minimal workloads may not necessarily be faster than a sequential implementation. Benchmarking sequential versus concurrent solutions should be the way to validate assumptions. diff --git a/prompts/skills/100-go-mistakes/references/mistake-57-being-puzzled-about-when-to-use-channels-or-mutexes.md b/prompts/skills/100-go-mistakes/references/mistake-57-being-puzzled-about-when-to-use-channels-or-mutexes.md index 7bc9e23..29310ad 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-57-being-puzzled-about-when-to-use-channels-or-mutexes.md +++ b/prompts/skills/100-go-mistakes/references/mistake-57-being-puzzled-about-when-to-use-channels-or-mutexes.md @@ -1,7 +1,6 @@ # Mistake #57: Being puzzled about when to use channels or mutexes #### TL;DR -TL;DR Being aware of goroutine interactions can also be helpful when deciding between channels and mutexes. In general, parallel goroutines require synchronization and hence mutexes. Conversely, concurrent goroutines generally require coordination and orchestration and hence channels. diff --git a/prompts/skills/100-go-mistakes/references/mistake-58-not-understanding-race-problems-data-races-vs-race-conditions.md b/prompts/skills/100-go-mistakes/references/mistake-58-not-understanding-race-problems-data-races-vs-race-conditions.md index 00d536d..418b909 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-58-not-understanding-race-problems-data-races-vs-race-conditions.md +++ b/prompts/skills/100-go-mistakes/references/mistake-58-not-understanding-race-problems-data-races-vs-race-conditions.md @@ -1,7 +1,6 @@ # Mistake #58: Not understanding race problems (data races vs. race conditions and the Go memory model) #### TL;DR -TL;DR Being proficient in concurrency also means understanding that data races and race conditions are different concepts. Data races occur when multiple goroutines simultaneously access the same memory location and at least one of them is writing. Meanwhile, being data-race-free doesn’t necessarily mean deterministic execution. When a behavior depends on the sequence or the timing of events that can’t be controlled, this is a race condition. diff --git a/prompts/skills/100-go-mistakes/references/mistake-59-not-understanding-the-concurrency-impacts-of-a-workload-type.md b/prompts/skills/100-go-mistakes/references/mistake-59-not-understanding-the-concurrency-impacts-of-a-workload-type.md index a3476be..0a60da5 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-59-not-understanding-the-concurrency-impacts-of-a-workload-type.md +++ b/prompts/skills/100-go-mistakes/references/mistake-59-not-understanding-the-concurrency-impacts-of-a-workload-type.md @@ -1,7 +1,6 @@ # Mistake #59: Not understanding the concurrency impacts of a workload type #### TL;DR -TL;DR When creating a certain number of goroutines, consider the workload type. Creating CPU-bound goroutines means bounding this number close to the GOMAXPROCS variable (based by default on the number of CPU cores on the host). Creating I/O-bound goroutines depends on other factors, such as the external system. diff --git a/prompts/skills/100-go-mistakes/references/mistake-60-misunderstanding-go-contexts.md b/prompts/skills/100-go-mistakes/references/mistake-60-misunderstanding-go-contexts.md index 71be7c5..fe44998 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-60-misunderstanding-go-contexts.md +++ b/prompts/skills/100-go-mistakes/references/mistake-60-misunderstanding-go-contexts.md @@ -1,7 +1,6 @@ # Mistake #60: Misunderstanding Go contexts #### TL;DR -TL;DR Go contexts are also one of the cornerstones of concurrency in Go. A context allows you to carry a deadline, a cancellation signal, and/or a list of keys-values. @@ -30,7 +29,7 @@ For example, if we use tracing, we may want different subfunctions to share the Catching a context cancellation -The `context.Context` type exports a `Done` method that returns a receive-only notification channel: `<-chan struct{}`. This channel is closed when the work associated with the context should be canceled. For example, +The `context.Context` type exports a `Done` method that returns a receive-only notification channel: `<-chan struct{}`. This channel is closed when the work associated with the context should be canceled. For example, * The Done channel related to a context created with `context.WithCancel` is closed when the cancel function is called. * The Done channel related to a context created with `context.WithDeadline` is closed when the deadline has expired. diff --git a/prompts/skills/100-go-mistakes/references/mistake-61-propagating-an-inappropriate-context.md b/prompts/skills/100-go-mistakes/references/mistake-61-propagating-an-inappropriate-context.md index a0f3e5a..8fa7b09 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-61-propagating-an-inappropriate-context.md +++ b/prompts/skills/100-go-mistakes/references/mistake-61-propagating-an-inappropriate-context.md @@ -1,7 +1,6 @@ # Mistake #61: Propagating an inappropriate context #### TL;DR -TL;DR Understanding the conditions when a context can be canceled should matter when propagating it: for example, an HTTP handler canceling the context when the response has been sent. diff --git a/prompts/skills/100-go-mistakes/references/mistake-62-starting-a-goroutine-without-knowing-when-to-stop-it.md b/prompts/skills/100-go-mistakes/references/mistake-62-starting-a-goroutine-without-knowing-when-to-stop-it.md index b9dcbf4..6438377 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-62-starting-a-goroutine-without-knowing-when-to-stop-it.md +++ b/prompts/skills/100-go-mistakes/references/mistake-62-starting-a-goroutine-without-knowing-when-to-stop-it.md @@ -1,7 +1,6 @@ # Mistake #62: Starting a goroutine without knowing when to stop it #### TL;DR -TL;DR Avoiding leaks means being mindful that whenever a goroutine is started, you should have a plan to stop it eventually. diff --git a/prompts/skills/100-go-mistakes/references/mistake-63-not-being-careful-with-goroutines-and-loop-variables.md b/prompts/skills/100-go-mistakes/references/mistake-63-not-being-careful-with-goroutines-and-loop-variables.md index 47cc181..ed7d1c3 100644 --- a/prompts/skills/100-go-mistakes/references/mistake-63-not-being-careful-with-goroutines-and-loop-variables.md +++ b/prompts/skills/100-go-mistakes/references/mistake-63-not-being-careful-with-goroutines-and-loop-variables.md @@ -1,