Full Code of teivah/100-go-mistakes for AI

master ab425833b58d cached
208 files
3.6 MB
956.5k tokens
712 symbols
1 requests
Download .txt
Showing preview only (3,819K chars total). Download the full file or copy to clipboard to get everything.
Repository: teivah/100-go-mistakes
Branch: master
Commit: ab425833b58d
Files: 208
Total size: 3.6 MB

Directory structure:
gitextract_0hghv22e/

├── .gitattributes
├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── community_mistake.md
│   │   └── erratum.md
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── docs/
│   ├── 20-slice.md
│   ├── 28-maps-memory-leaks.md
│   ├── 5-interface-pollution.md
│   ├── 56-concurrency-faster.md
│   ├── 89-benchmarks.md
│   ├── 9-generics.md
│   ├── 92-false-sharing.md
│   ├── 98-profiling-execution-tracing.md
│   ├── CNAME
│   ├── book.md
│   ├── chapter-1.md
│   ├── external.md
│   ├── index.md
│   ├── ja.md
│   ├── pt-br.md
│   ├── stylesheets/
│   │   └── extra.css
│   └── zh.md
├── go.mod
├── go.sum
├── includes/
│   └── abbreviations.md
├── justfile
├── mkdocs.yml
├── overrides/
│   ├── main.html
│   └── partials/
│       └── comments.html
├── site/
│   ├── 20-slice/
│   │   └── index.html
│   ├── 28-maps-memory-leaks/
│   │   └── index.html
│   ├── 404.html
│   ├── 5-interface-pollution/
│   │   └── index.html
│   ├── 56-concurrency-faster/
│   │   └── index.html
│   ├── 89-benchmarks/
│   │   └── index.html
│   ├── 9-generics/
│   │   └── index.html
│   ├── 92-false-sharing/
│   │   └── index.html
│   ├── 98-profiling-execution-tracing/
│   │   └── index.html
│   ├── CNAME
│   ├── assets/
│   │   └── javascripts/
│   │       └── lunr/
│   │           ├── tinyseg.js
│   │           └── wordcut.js
│   ├── book/
│   │   └── index.html
│   ├── chapter-1/
│   │   └── index.html
│   ├── external/
│   │   └── index.html
│   ├── index.html
│   ├── ja/
│   │   └── index.html
│   ├── pt-br/
│   │   └── index.html
│   ├── search/
│   │   └── search_index.json
│   ├── sitemap.xml
│   ├── stylesheets/
│   │   └── extra.css
│   └── zh/
│       └── index.html
└── src/
    ├── 02-code-project-organization/
    │   ├── 1-variable-shadowing/
    │   │   └── main.go
    │   ├── 10-type-embedding/
    │   │   └── main.go
    │   ├── 11-functional-options/
    │   │   ├── builder/
    │   │   │   └── main.go
    │   │   ├── config-struct/
    │   │   │   └── main.go
    │   │   └── functional-options/
    │   │       └── main.go
    │   ├── 13-utility-packages/
    │   │   └── stringset.go
    │   ├── 2-nested-code/
    │   │   └── main.go
    │   ├── 3-init-functions/
    │   │   ├── db/
    │   │   │   └── main.go
    │   │   ├── main/
    │   │   │   └── main.go
    │   │   └── redis/
    │   │       └── redis.go
    │   ├── 5-interface-pollution/
    │   │   ├── copy/
    │   │   │   ├── main.go
    │   │   │   └── main_test.go
    │   │   ├── decoupling/
    │   │   │   ├── with.go
    │   │   │   └── without.go
    │   │   └── restricting-behavior/
    │   │       └── main.go
    │   ├── 6-interface-producer/
    │   │   ├── client/
    │   │   │   └── client.go
    │   │   └── store/
    │   │       └── store.go
    │   ├── 8-any/
    │   │   ├── main.go
    │   │   └── store/
    │   │       ├── after.go
    │   │       └── before.go
    │   └── 9-generics/
    │       └── main.go
    ├── 03-data-types/
    │   ├── 17-octal-literals/
    │   │   └── main.go
    │   ├── 18-integer-overflows/
    │   │   └── main.go
    │   ├── 19-floating-points/
    │   │   └── main.go
    │   ├── 20-slice-length-cap/
    │   │   └── main.go
    │   ├── 21-slice-init/
    │   │   ├── main.go
    │   │   └── main_test.go
    │   ├── 22-nil-empty-slice/
    │   │   ├── json/
    │   │   │   └── main.go
    │   │   └── slice-init/
    │   │       └── main.go
    │   ├── 23-checking-slice-empty/
    │   │   └── main.go
    │   ├── 24-slice-copy/
    │   │   └── main.go
    │   ├── 25-slice-append/
    │   │   └── main.go
    │   ├── 26-slice-memory-leak/
    │   │   ├── capacity-leak/
    │   │   │   └── main.go
    │   │   └── slice-pointers/
    │   │       └── main.go
    │   ├── 27-map-init/
    │   │   └── main_test.go
    │   ├── 28-map-memory-leak/
    │   │   └── main.go
    │   └── 29-comparing-values/
    │       └── main.go
    ├── 04-control-structures/
    │   ├── 30-range-loop-element-copied/
    │   │   ├── concepts/
    │   │   │   └── main.go
    │   │   └── value-copy/
    │   │       └── main.go
    │   ├── 31-range-loop-arg-evaluation/
    │   │   ├── arrays/
    │   │   │   └── main.go
    │   │   ├── channels/
    │   │   │   └── main.go
    │   │   └── concepts/
    │   │       └── main.go
    │   ├── 32-range-loop-pointers/
    │   │   ├── concepts/
    │   │   │   └── main.go
    │   │   └── customer-store/
    │   │       └── main.go
    │   ├── 33-map-iteration/
    │   │   └── main.go
    │   ├── 34-break/
    │   │   └── main.go
    │   └── 35-defer-loop/
    │       └── main.go
    ├── 05-strings/
    │   ├── 36-rune/
    │   │   └── main.go
    │   ├── 37-string-iteration/
    │   │   └── main.go
    │   ├── 38-trim/
    │   │   └── main.go
    │   ├── 39-string-concat/
    │   │   ├── main.go
    │   │   └── main_test.go
    │   ├── 40-string-conversion/
    │   │   └── main.go
    │   └── 41-substring-memory-leak/
    │       └── main.go
    ├── 06-functions-methods/
    │   ├── 42-receiver/
    │   │   ├── pointer/
    │   │   │   └── main.go
    │   │   ├── struct-with-pointer/
    │   │   │   └── main.go
    │   │   └── value/
    │   │       └── main.go
    │   ├── 43-named-result-parameters/
    │   │   └── main.go
    │   ├── 44-side-effects-named-result-parameters/
    │   │   └── main.go
    │   ├── 45-nil-receiver/
    │   │   └── main.go
    │   ├── 46-function-input/
    │   │   ├── main.go
    │   │   └── main_test.go
    │   └── 47-defer-evaluation/
    │       ├── args/
    │       │   └── main.go
    │       └── receiver/
    │           ├── pointer/
    │           │   └── main.go
    │           └── value/
    │               └── main.go
    ├── 07-error-management/
    │   ├── 48-panic/
    │   │   └── main.go
    │   ├── 49-error-wrapping/
    │   │   └── main.go
    │   ├── 50-compare-error-type/
    │   │   └── main.go
    │   ├── 51-comparing-error-value/
    │   │   └── main.go
    │   ├── 52-handling-error-twice/
    │   │   └── main.go
    │   ├── 53-not-handling-error/
    │   │   └── main.go
    │   └── 54-defer-errors/
    │       └── main.go
    ├── 08-concurrency-foundations/
    │   ├── 56-faster/
    │   │   ├── main.go
    │   │   └── main_test.go
    │   ├── 58-races/
    │   │   ├── memory-model/
    │   │   │   └── main.go
    │   │   └── races/
    │   │       └── main.go
    │   ├── 59-workload-type/
    │   │   └── main.go
    │   └── 60-contexts/
    │       ├── flight/
    │       │   └── flight.go
    │       └── main.go
    ├── 09-concurrency-practice/
    │   ├── 61-inappropriate-context/
    │   │   └── main.go
    │   ├── 62-starting-goroutine/
    │   │   ├── listing1/
    │   │   │   └── main.go
    │   │   ├── listing2/
    │   │   │   └── main.go
    │   │   └── listing3/
    │   │       └── main.go
    │   ├── 63-goroutines-loop-variables/
    │   │   └── main.go
    │   ├── 64-select-behavior/
    │   │   └── main.go
    │   ├── 66-nil-channels/
    │   │   └── main.go
    │   ├── 68-string-formatting/
    │   │   └── main.go
    │   ├── 69-data-race-append/
    │   │   └── main.go
    │   ├── 70-mutex-slices-maps/
    │   │   └── main.go
    │   ├── 71-wait-group/
    │   │   └── main.go
    │   ├── 72-cond/
    │   │   └── main.go
    │   ├── 73-errgroup/
    │   │   └── main.go
    │   └── 74-copying-sync/
    │       └── main.go
    ├── 10-standard-lib/
    │   ├── 75-wrong-time-duration/
    │   │   └── main.go
    │   ├── 76-time-after/
    │   │   └── main.go
    │   ├── 77-json-handling/
    │   │   ├── map-any/
    │   │   │   └── main.go
    │   │   ├── monotonic-clock/
    │   │   │   └── main.go
    │   │   └── type-embedding/
    │   │       └── main.go
    │   ├── 78-sql/
    │   │   ├── null-values/
    │   │   │   └── main.go
    │   │   ├── prepared-statements/
    │   │   │   └── main.go
    │   │   ├── rows-iterations-errors/
    │   │   │   └── main.go
    │   │   └── sql-open/
    │   │       └── main.go
    │   ├── 79-closing-resources/
    │   │   ├── http/
    │   │   │   └── main.go
    │   │   ├── os-file/
    │   │   │   └── main.go
    │   │   └── sql-rows/
    │   │       └── main.go
    │   ├── 80-http-return/
    │   │   └── main.go
    │   └── 81-default-http-client-server/
    │       ├── client/
    │       │   └── main.go
    │       └── server/
    │           └── main.go
    ├── 11-testing/
    │   ├── 82-categorizing-tests/
    │   │   ├── build-tags/
    │   │   │   └── db_test.go
    │   │   └── short-mode/
    │   │       └── main_test.go
    │   ├── 85-table-driven-tests/
    │   │   ├── main.go
    │   │   └── main_test.go
    │   ├── 86-sleeping/
    │   │   ├── main.go
    │   │   └── main_test.go
    │   ├── 87-time-api/
    │   │   ├── listing1/
    │   │   │   ├── main.go
    │   │   │   └── main_test.go
    │   │   ├── listing2/
    │   │   │   ├── main.go
    │   │   │   └── main_test.go
    │   │   ├── listing3/
    │   │   │   ├── main.go
    │   │   │   └── main_test.go
    │   │   └── listing4/
    │   │       ├── main.go
    │   │       └── main_test.go
    │   ├── 88-utility-package/
    │   │   ├── httptest/
    │   │   │   ├── main.go
    │   │   │   └── main_test.go
    │   │   └── iotest/
    │   │       ├── main.go
    │   │       └── main_test.go
    │   ├── 89-benchmark/
    │   │   ├── compiler-optimizations/
    │   │   │   ├── main.go
    │   │   │   └── main_test.go
    │   │   ├── observer-effect/
    │   │   │   ├── main.go
    │   │   │   └── main_test.go
    │   │   ├── timer/
    │   │   │   └── main_test.go
    │   │   └── wrong-assumptions/
    │   │       └── main_test.go
    │   └── 90-testing-features/
    │       ├── different-package/
    │       │   ├── main.go
    │       │   └── main_test.go
    │       ├── setup-teardown/
    │       │   └── main_test.go
    │       └── utility-function/
    │           ├── main.go
    │           └── main_test.go
    └── 12-optimizations/
        ├── 91-cpu-caches/
        │   ├── cache-line/
        │   │   ├── main.go
        │   │   └── main_test.go
        │   ├── predictability/
        │   │   ├── main.go
        │   │   └── main_test.go
        │   └── slice-structs/
        │       ├── main.go
        │       └── main_test.go
        ├── 92-false-sharing/
        │   ├── main.go
        │   └── main_test.go
        ├── 93-instruction-level-parallelism/
        │   ├── main.go
        │   └── main_test.go
        ├── 94-data-alignment/
        │   ├── main.go
        │   └── main_test.go
        ├── 95-stack-heap/
        │   ├── main.go
        │   └── main_test.go
        └── 96-reduce-allocations/
            ├── compiler/
            │   └── main.go
            └── sync-pool/
                └── main.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitattributes
================================================
*.html linguist-detectable=false
*.js linguist-detectable=false
*.css linguist-detectable=false


================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: teivah
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']


================================================
FILE: .github/ISSUE_TEMPLATE/community_mistake.md
================================================
---
name: Community mistake
about: Propose a new community mistake
title: ''
labels: community mistake
---

**Describe the mistake**

Describe the mistake and the context.

**Solution**

One or multiple solutions to avoid the mistake.


================================================
FILE: .github/ISSUE_TEMPLATE/erratum.md
================================================
---
name: Erratum
about: Suggest a book correction
title: ''
labels: erratum
---

**Describe the book error**

Describe a factual error in the book and suggest a correction. 


================================================
FILE: .github/workflows/ci.yml
================================================
name: ci 
on:
  push:
    branches:
      - master 
      - main
permissions:
  contents: write
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v4
        with:
          python-version: 3.x
      - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV 
      - uses: actions/cache@v3
        with:
          key: mkdocs-material-${{ env.cache_id }}
          path: .cache
          restore-keys: |
            mkdocs-material-
      - run: pip install mkdocs-material
      - run: pip install pillow cairosvg
      - run: pip install mkdocs-glightbox
      - run: mkdocs gh-deploy --force


================================================
FILE: .gitignore
================================================
.idea
*.out
100-go-mistakes.iml

================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to 100 Go Mistakes

First of all, thank you for taking the time to contribute! 🎉

We aim to create a collaborative space that could become the canonical place to find common mistakes to avoid in Go.

If you want to participate, please look at the open issues. The 100 Go Mistakes repository is a documentation project, so all the content that can be enriched is in the 
[/docs](https://github.com/teivah/100-go-mistakes/tree/master/docs) folder. The main file is [index.md](https://github.com/teivah/100-go-mistakes/blob/master/docs/index.md), 
the one containing the main content of [100go.co](https://100go.co/).

If you submit a PR, please do not care about the HTML generation (the website uses MkDocs), I'll take care of it. Said differently, most PRs should
only contain modifications to .md files.


================================================
FILE: LICENSE.md
================================================
# Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International

Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible.

### Using Creative Commons Public Licenses

Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses.

* __Considerations for licensors:__ Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. [More considerations for licensors](http://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensors).

* __Considerations for the public:__ By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. [More considerations for the public](http://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensees).

## Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License

By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.

### Section 1 – Definitions.

a. __Adapted Material__ means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.

b. __Copyright and Similar Rights__ means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.

e. __Effective Technological Measures__ means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.

f. __Exceptions and Limitations__ means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.

h. __Licensed Material__ means the artistic or literary work, database, or other material to which the Licensor applied this Public License.

i. __Licensed Rights__ means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.

h. __Licensor__ means the individual(s) or entity(ies) granting rights under this Public License.

i. __NonCommercial__ means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange.

j. __Share__ means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.

k. __Sui Generis Database Rights__ means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.

l. __You__ means the individual or entity exercising the Licensed Rights under this Public License. __Your__ has a corresponding meaning.

### Section 2 – Scope.

a. ___License grant.___

1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:

   A. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and

   B. produce and reproduce, but not Share, Adapted Material for NonCommercial purposes only.

2. __Exceptions and Limitations.__ For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.

3. __Term.__ The term of this Public License is specified in Section 6(a).

4. __Media and formats; technical modifications allowed.__ The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.

5. __Downstream recipients.__

   A. __Offer from the Licensor – Licensed Material.__ Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.

   B. __No downstream restrictions.__ You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.

6. __No endorsement.__ Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).

b. ___Other rights.___

1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.

2. Patent and trademark rights are not licensed under this Public License.

3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes.

### Section 3 – License Conditions.

Your exercise of the Licensed Rights is expressly made subject to the following conditions.

a. ___Attribution.___

1. If You Share the Licensed Material, You must:

   A. retain the following if it is supplied by the Licensor with the Licensed Material:

   i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);

   ii. a copyright notice;

   iii. a notice that refers to this Public License;

   iv. a notice that refers to the disclaimer of warranties;

   v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable;

   B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and

   C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.

   For the avoidance of doubt, You do not have permission under this Public License to Share Adapted Material.

2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.

3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.

### Section 4 – Sui Generis Database Rights.

Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:

a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only and provided You do not Share Adapted Material;

b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and

c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.

For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.

### Section 5 – Disclaimer of Warranties and Limitation of Liability.

a. __Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.__

b. __To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.__

c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.

### Section 6 – Term and Termination.

a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.

b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:

1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or

2. upon express reinstatement by the Licensor.

For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.

c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.

d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License.

### Section 7 – Other Terms and Conditions.

a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.

b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.

### Section 8 – Interpretation.

a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.

b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.

c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.

d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.

> Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at [creativecommons.org/policies](http://creativecommons.org/policies), Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses.
>
> Creative Commons may be contacted at [creativecommons.org](http://creativecommons.org).


================================================
FILE: README.md
================================================
# 100 Go Mistakes and How to Avoid Them

Source code and community space of 📖 _100 Go Mistakes and How to Avoid Them_, published by Manning in August 2022.

Read online: [100go.co](https://100go.co).

## Powered by

[![JetBrains logo.](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg)](https://jb.gg/OpenSource)

================================================
FILE: docs/20-slice.md
================================================
---
title: Not understanding slice length and capacity (#20)
comments: true
hide:
- toc
---

# Not understanding slice length and capacity

![](img/20-slice.png)

It’s pretty common for Go developers to mix slice length and capacity or not understand them thoroughly. Assimilating these two concepts is essential for efficiently handling core operations such as slice initialization and adding elements with append, copying, or slicing. This misunderstanding can lead to using slices suboptimally or even to memory leaks.

In Go, a slice is backed by an array. That means the slice’s data is stored contiguously in an array data structure. A slice also handles the logic of adding an element if the backing array is full or shrinking the backing array if it’s almost empty.

Internally, a slice holds a pointer to the backing array plus a length and a capacity. The length is the number of elements the slice contains, whereas the capacity is the number of elements in the backing array, counting from the first element in the slice. Let’s go through a few examples to make things clearer. First, let’s initialize a slice with a given length and capacity:

```go
s := make([]int, 3, 6) // Three-length, six-capacity slice
```

The first argument, representing the length, is mandatory. However, the second argument representing the capacity is optional. Figure 1 shows the result of this code in memory.

<figure markdown>
  ![](img/slice-1.png)
  <figcaption>Figure 1: A three-length, six-capacity slice.</figcaption>
</figure>

In this case, `make` creates an array of six elements (the capacity). But because the length was set to 3, Go initializes only the first three elements. Also, because the slice is an `[]int` type, the first three elements are initialized to the zeroed value of an `int`: 0. The grayed elements are allocated but not yet used.

If we print this slice, we get the elements within the range of the length, `[0 0 0]`. If we set `s[1]` to 1, the second element of the slice updates without impacting its length or capacity. Figure 2 illustrates this.

<figure markdown>
  ![](img/slice-2.png)
  <figcaption>Figure 2: Updating the slice’s second element: s[1] = 1.</figcaption>
</figure>

However, accessing an element outside the length range is forbidden, even though it’s already allocated in memory. For example, `s[4]` = 0 would lead to the following panic:

```
panic: runtime error: index out of range [4] with length 3
```

How can we use the remaining space of the slice? By using the `append` built-in function:

```go
s = append(s, 2)
```

This code appends to the existing `s` slice a new element. It uses the first grayed element (which was allocated but not yet used) to store element 2, as figure 3 shows.

<figure markdown>
  ![](img/slice-3.png)
  <figcaption>Figure 3: Appending an element to s.</figcaption>
</figure>

The length of the slice is updated from 3 to 4 because the slice now contains four elements. Now, what happens if we add three more elements so that the backing array isn’t large enough?

```go
s = append(s, 3, 4, 5)
fmt.Println(s)
```

If we run this code, we see that the slice was able to cope with our request:

```
[0 1 0 2 3 4 5]
```

Because an array is a fixed-size structure, it can store the new elements until element 4. When we want to insert element 5, the array is already full: Go internally creates another array by doubling the capacity, copying all the elements, and then inserting element 5. Figure 4 shows this process.

<figure markdown>
  ![](img/slice-4.png)
  <figcaption>Figure 4: Because the initial backing array is full, Go creates another array and copies all the elements.</figcaption>
</figure>

The slice now references the new backing array. What will happen to the previous backing array? If it’s no longer referenced, it’s eventually freed by the garbage collector (GC) if allocated on the heap. (We discuss heap memory in mistake #95, “[Not understanding stack vs. heap](https://100go.co#not-understanding-stack-vs-heap-95),” and we look at how the GC works in mistake #99, “[Not understanding how the GC works](https://100go.co#not-understanding-how-the-gc-works-99).”)

What happens with slicing? Slicing is an operation done on an array or a slice, providing a half-open range; the first index is included, whereas the second is excluded. The following example shows the impact, and figure 5 displays the result in memory:

```go
s1 := make([]int, 3, 6) // Three-length, six-capacity slice
s2 := s1[1:3] // Slicing from indices 1 to 3
```

<figure markdown>
  ![](img/slice-5.png)
  <figcaption>Figure 5: The slices s1 and s2 reference the same backing array with different lengths and capacities.</figcaption>
</figure>

First, `s1` is created as a three-length, six-capacity slice. When `s2` is created by slicing `s1`, both slices reference the same backing array. However, `s2` starts from a different index, 1. Therefore, its length and capacity (a two-length, five-capacity slice) differ from s1. If we update `s1[1]` or `s2[0]`, the change is made to the same array, hence, visible in both slices, as figure 6 shows.

<figure markdown>
  ![](img/slice-6.png)
  <figcaption>Figure 6: Because s1 and s2 are backed by the same array, updating a common element makes the change visible in both slices.</figcaption>
</figure>

Now, what happens if we append an element to `s2`? Does the following code change `s1` as well?

```go
s2 = append(s2, 2)
```

The shared backing array is modified, but only the length of `s2` changes. Figure 7 shows the result of appending an element to `s2`.

<figure markdown>
  ![](img/slice-7.png)
  <figcaption>Figure 7: Appending an element to s2.</figcaption>
</figure>

`s1` remains a three-length, six-capacity slice. Therefore, if we print `s1` and `s2`, the added element is only visible for `s2`:

```go
s1=[0 1 0], s2=[1 0 2]
```

It’s important to understand this behavior so that we don’t make wrong assumptions while using append.

???+ note

    In these examples, the backing array is internal and not available directly to the Go developer. The only exception is when a slice is created from slicing an existing array.

One last thing to note: what if we keep appending elements to `s2` until the backing array is full? What will the state be, memory-wise? Let’s add three more elements so that the backing array will not have enough capacity:

```go
s2 = append(s2, 3)
s2 = append(s2, 4) // At this stage, the backing is already full
s2 = append(s2, 5)
```

This code leads to creating another backing array. Figure 8 displays the results in memory.

<figure markdown>
  ![](img/slice-8.png)
  <figcaption>Figure 8: Appending elements to s2 until the backing array is full.</figcaption>
</figure>

`s1` and `s2` now reference two different arrays. As `s1` is still a three-length, six-capacity slice, it still has some available buffer, so it keeps referencing the initial array. Also, the new backing array was made by copying the initial one from the first index of `s2`. That’s why the new array starts with element 1, not 0.

To summarize, 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. Adding an element to a full slice (length == capacity) leads to creating a new backing array with a new capacity, copying all the elements from the previous array, and updating the slice pointer to the new array.


================================================
FILE: docs/28-maps-memory-leaks.md
================================================
---
title: Maps and memory leaks (#28)
comments: true
hide:
- toc
---

# Maps and memory leaks

![](img/28-maps-memory-leaks.png)

When working with maps in Go, we need to understand some important characteristics of how a map grows and shrinks. Let’s delve into this to prevent issues that can cause memory leaks.

First, to view a concrete example of this problem, let’s design a scenario where we will work with the following map:

```go
m := make(map[int][128]byte)
```

Each value of m is an array of 128 bytes. We will do the following:

1. Allocate an empty map.
2. Add 1 million elements.
3. Remove all the elements, and run a Garbage Collection (GC).

After each step, we want to print the size of the heap (using a `printAlloc` utility function). This shows us how this example behaves memory-wise:

```go
func main() {
	n := 1_000_000
	m := make(map[int][128]byte)
	printAlloc()

	for i := 0; i < n; i++ { // Adds 1 million elements
		m[i] = [128]byte{}
	}
	printAlloc()

	for i := 0; i < n; i++ { // Deletes 1 million elements
		delete(m, i)
	}

	runtime.GC() // Triggers a manual GC
	printAlloc()
	runtime.KeepAlive(m) // Keeps a reference to m so that the map isn’t collected
}

func printAlloc() {
	var m runtime.MemStats
	runtime.ReadMemStats(&m)
	fmt.Printf("%d MB\n", m.Alloc/(1024*1024))
}
```

We allocate an empty map, add 1 million elements, remove 1 million elements, and then run a GC. We also make sure to keep a reference to the map using [`runtime.KeepAlive`](https://pkg.go.dev/runtime#KeepAlive) so that the map isn’t collected as well. Let’s run this example:

```
0 MB   <-- After m is allocated
461 MB <-- After we add 1 million elements
293 MB <-- After we remove 1 million elements
```

What can we observe? At first, the heap size is minimal. Then it grows significantly after having added 1 million elements to the map. But if we expected the heap size to decrease after removing all the elements, this isn’t how maps work in Go. In the end, even though the GC has collected all the elements, the heap size is still 293 MB. So the memory shrunk, but not as we might have expected. What’s the rationale? We need to delve into how a map works in Go.

A map provides an unordered collection of key-value pairs in which all the keys are distinct. In Go, a map is based on the hash table data structure: an array where each element is a pointer to a bucket of key-value pairs, as shown in figure 1.

<figure markdown>
  ![](img/map-leak-1.png)
  <figcaption>Figure 1: A hash table example with a focus on bucket 0.</figcaption>
</figure>

Each bucket is a fixed-size array of eight elements. In the case of an insertion into a bucket that is already full (a bucket overflow), Go creates another bucket of eight elements and links the previous one to it. Figure 2 shows an example:

<figure markdown>
  ![](img/map-leak-2.png)
  <figcaption>Figure 2: In case of a bucket overflow, Go allocates a new bucket and links the previous bucket to it.</figcaption>
</figure>


Under the hood, a Go map is a pointer to a [runtime.hmap](https://github.com/golang/go/blob/0262ea1ff9ac3b9fd268a48fcaaa6811c20cbea2/src/runtime/map.go#L117-L131) struct. This struct contains multiple fields, including a B field, giving the number of buckets in the map:

```go
type hmap struct {
    B uint8 // log_2 of # of buckets
            // (can hold up to loadFactor * 2^B items)
    // ...
}
```

After adding 1 million elements, the value of B equals 18, which means 2¹⁸ = 262,144 buckets. When we remove 1 million elements, what’s the value of B? Still 18. Hence, the map still contains the same number of buckets.

The reason is that the number of buckets in a map cannot shrink. Therefore, removing elements from a map doesn’t impact the number of existing buckets; it just zeroes the slots in the buckets. A map can only grow and have more buckets; it never shrinks.

In the previous example, we went from 461 MB to 293 MB because the elements were collected, but running the GC didn’t impact the map itself. Even the number of extra buckets (the buckets created because of overflows) remains the same.

Let’s take a step back and discuss when the fact that a map cannot shrink can be a problem. Imagine building a cache using a `map[int][128]byte`. This map holds per customer ID (the `int`), a sequence of 128 bytes. Now, suppose we want to save the last 1,000 customers. The map size will remain constant, so we shouldn’t worry about the fact that a map cannot shrink.

However, let’s say we want to store one hour of data. Meanwhile, our company has decided to have a big promotion for Black Friday: in one hour, we may have millions of customers connected to our system. But a few days after Black Friday, our map will contain the same number of buckets as during the peak time. This explains why we can experience high memory consumption that doesn’t significantly decrease in such a scenario.

What are the solutions if we don’t want to manually restart our service to clean the amount of memory consumed by the map? One solution could be to re-create a copy of the current map at a regular pace. For example, every hour, we can build a new map, copy all the elements, and release the previous one. The main drawback of this option is that following the copy and until the next garbage collection, we may consume twice the current memory for a short period.

Another solution would be to change the map type to store an array pointer: `map[int]*[128]byte`. It doesn’t solve the fact that we will have a significant number of buckets; however, each bucket entry will reserve the size of a pointer for the value instead of 128 bytes (8 bytes on 64-bit systems and 4 bytes on 32-bit systems).

Coming back to the original scenario, let’s compare the memory consumption for each map type following each step. The following table shows the comparison.

| Step  | `map[int][128]byte`  | `map[int]*[128]byte`  |
|---|---|---|
|  Allocate an empty map | 0 MB  | 0 MB  |
|  Add 1 million elements | 461 MB  |  182 MB |
| Remove all the elements and run a GC  |  293 MB | 38 MB  |

???+ note

    If a key or a value is over 128 bytes, Go won’t store it directly in the map bucket. Instead, Go stores a pointer to reference the key or the value.

As we have seen, adding n elements to a map and then deleting all the elements means keeping the same number of buckets in memory. So, we must remember that because a Go map can only grow in size, so does its memory consumption. There is no automated strategy to shrink it. If this leads to high memory consumption, we can try different options such as forcing Go to re-create the map or using pointers to check if it can be optimized.


================================================
FILE: docs/5-interface-pollution.md
================================================
---
title: Interface pollution (#5)
comments: true
hide:
- toc
status: new
---

# Interface pollution

![](img/interface-pollution.jpeg)

Interfaces are one of the cornerstones of the Go language when designing and structuring our code. However, like many tools or concepts, abusing them is generally not a good idea. Interface pollution is about overwhelming our code with unnecessary abstractions, making it harder to understand. It’s a common mistake made by developers coming from another language with different habits. Before delving into the topic, let’s refresh our minds about Go’s interfaces. Then, we will see when it’s appropriate to use interfaces and when it may be considered pollution.

## Concepts

An interface provides a way to specify the behavior of an object. We use interfaces to create common abstractions that multiple objects can implement. What makes Go interfaces so different is that they are satisfied implicitly. There is no explicit keyword like `implements` to mark that an object X implements interface Y.

To understand what makes interfaces so powerful, we will dig into two popular ones from the standard library: `io.Reader` and `io.Writer`. The `io` package provides abstractions for I/O primitives. Among these abstractions, `io.Reader` relates to reading data from a data source and `io.Writer` to writing data to a target, as represented in the next figure:

<figure markdown>
  ![](img/ioreaderwriter.svg)
</figure>

The `io.Reader` contains a single Read method:

```go
type Reader interface {
    Read(p []byte) (n int, err error)
}
```

Custom implementations of the `io.Reader` interface should accept a slice of bytes, filling it with its data and returning either the number of bytes read or an error.

On the other hand, `io.Writer` defines a single method, Write:

```go
type Writer interface {
    Write(p []byte) (n int, err error)
}
```

Custom implementations of `io.Writer` should write the data coming from a slice to a target and return either the number of bytes written or an error. Therefore, both interfaces provide fundamental abstractions:

* `io.Reader` reads data from a source
* `io.Writer` writes data to a target

What is the rationale for having these two interfaces in the language? What is the point of creating these abstractions?

Let’s assume we need to implement a function that should copy the content of one file to another. We could create a specific function that would take as input two `*os.File`. Or, we can choose to create a more generic function using `io.Reader` and `io.Writer` abstractions:

```go
func copySourceToDest(source io.Reader, dest io.Writer) error {
    // ...
}
```

This function would work with `*os.File` parameters (as `*os.File` implements both `io.Reader` and `io.Writer`) and any other type that would implement these interfaces. For example, we could create our own `io.Writer` that writes to a database, and the code would remain the same. It increases the genericity of the function; hence, its reusability.

Furthermore, writing a unit test for this function is easier because, instead of having to handle files, we can use the `strings` and `bytes` packages that provide helpful implementations:

```go
func TestCopySourceToDest(t *testing.T) {
    const input = "foo"
    source := strings.NewReader(input) // Creates an io.Reader
    dest := bytes.NewBuffer(make([]byte, 0)) // Creates an io.Writer

    err := copySourceToDest(source, dest) // Calls copySourceToDest from a *strings.Reader and a *bytes.Buffer
    if err != nil {
        t.FailNow()
    }

    got := dest.String()
    if got != input {
        t.Errorf("expected: %s, got: %s", input, got)
    }
}
```

In the example, source is a `*strings.Reader`, whereas dest is a `*bytes.Buffer`. Here, we test the behavior of `copySourceToDest` without creating any files.

While designing interfaces, the granularity (how many methods the interface contains) is also something to keep in mind. A [known proverb](https://www.youtube.com/watch?v=PAAkCSZUG1c&t=318s) in Go relates to how big an interface should be:

!!! quote "Rob Pike"

    The bigger the interface, the weaker the abstraction.

Indeed, adding methods to an interface can decrease its level of reusability. `io.Reader` and `io.Writer` are powerful abstractions because they cannot get any simpler. Furthermore, we can also combine fine-grained interfaces to create higher-level abstractions. This is the case with `io.ReadWriter`, which combines the reader and writer behaviors:

```go
type ReadWriter interface {
    Reader
    Writer
}
```

???+ note

    As Einstein said, “_Everything should be made as simple as possible, but no simpler._” Applied to interfaces, this denotes that finding the perfect granularity for an interface isn’t necessarily a straightforward process.

Let’s now discuss common cases where interfaces are recommended.

## When to use interfaces

When should we create interfaces in Go? Let’s look at three concrete use cases where interfaces are usually considered to bring value. Note that the goal isn’t to be exhaustive because the more cases we add, the more they would depend on the context. However, these three cases should give us a general idea:

* Common behavior 
* Decoupling
* Restricting behavior

### Common behavior

The first option we will discuss is to use interfaces when multiple types implement a common behavior. In such a case, we can factor out the behavior inside an interface. If we look at the standard library, we can find many examples of such a use case. For example, sorting a collection can be factored out via three methods:

* Retrieving the number of elements in the collection
* Reporting whether one element must be sorted before another 
* Swapping two elements

Hence, the following interface was added to the `sort` package:

```go
type Interface interface {
    Len() int // Number of elements
    Less(i, j int) bool // Checks two elements
    Swap(i, j int) // Swaps two elements
}
```

This interface has a strong potential for reusability because it encompasses the common behavior to sort any collection that is index-based.

Throughout the `sort` package, we can find dozens of implementations. If at some point we compute a collection of integers, for example, and we want to sort it, are we necessarily interested in the implementation type? Is it important whether the sorting algorithm is a merge sort or a quicksort? In many cases, we don’t care. Hence, the sorting behavior can be abstracted, and we can depend on the `sort.Interface`.

Finding the right abstraction to factor out a behavior can also bring many benefits. For example, the `sort` package provides utility functions that also rely on `sort.Interface`, such as checking whether a collection is already sorted. For instance:

```go
func IsSorted(data Interface) bool {
    n := data.Len()
    for i := n - 1; i > 0; i-- {
        if data.Less(i, i-1) {
            return false
        }
    }
    return true
}
```

Because `sort.Interface` is the right level of abstraction, it makes it highly valuable.

Let’s now see another main use case when using interfaces.

### Decoupling

Another important use case is about decoupling our code from an implementation. If we rely on an abstraction instead of a concrete implementation, the implementation itself can be replaced with another without even having to change our code. This is the Liskov Substitution Principle (the L in Robert C. Martin’s [SOLID](https://en.wikipedia.org/wiki/SOLID) design principles).

One benefit of decoupling can be related to unit testing. Let’s assume we want to implement a `CreateNewCustomer` method that creates a new customer and stores it. We decide to rely on the concrete implementation directly (let’s say a `mysql.Store` struct):

```go
type CustomerService struct {
    store mysql.Store // Depends on the concrete implementation
}

func (cs CustomerService) CreateNewCustomer(id string) error {
    customer := Customer{id: id}
    return cs.store.StoreCustomer(customer)
}
```

Now, what if we want to test this method? Because `customerService` relies on the actual implementation to store a `Customer`, we are obliged to test it through integration tests, which requires spinning up a MySQL instance (unless we use an alternative technique such as [`go-sqlmock`](https://github.com/DATA-DOG/go-sqlmock), but this isn’t the scope of this section). Although integration tests are helpful, that’s not always what we want to do. To give us more flexibility, we should decouple `CustomerService` from the actual implementation, which can be done via an interface like so:

```go
type customerStorer interface { // Creates a storage abstraction
    StoreCustomer(Customer) error
}

type CustomerService struct {
    storer customerStorer // Decouples CustomerService from the actual implementation
}

func (cs CustomerService) CreateNewCustomer(id string) error {
    customer := Customer{id: id}
    return cs.storer.StoreCustomer(customer)
}
```

Because storing a customer is now done via an interface, this gives us more flexibility in how we want to test the method. For instance, we can:

* Use the concrete implementation via integration tests 
* Use a mock (or any kind of test double) via unit tests 
* Or both

Let’s now discuss another use case: to restrict a behavior.

### Restricting behavior

The last use case we will discuss can be pretty counterintuitive at first sight. It’s about restricting a type to a specific behavior. Let’s imagine we implement a custom configuration package to deal with dynamic configuration. We create a specific container for `int` configurations via an `IntConfig` struct that also exposes two methods: `Get` and `Set`. Here’s how that code would look:

```go
type IntConfig struct {
    // ...
}

func (c *IntConfig) Get() int {
    // Retrieve configuration
}

func (c *IntConfig) Set(value int) {
    // Update configuration
}
```

Now, suppose we receive an `IntConfig` that holds some specific configuration, such as a threshold. Yet, in our code, we are only interested in retrieving the configuration value, and we want to prevent updating it. How can we enforce that, semantically, this configuration is read-only, if we don’t want to change our configuration package? By creating an abstraction that restricts the behavior to retrieving only a config value:

```go
type intConfigGetter interface {
    Get() int
}
```

Then, in our code, we can rely on `intConfigGetter` instead of the concrete implementation:

```go
type Foo struct {
    threshold intConfigGetter
}

func NewFoo(threshold intConfigGetter) Foo { // Injects the configuration getter
    return Foo{threshold: threshold}
}

func (f Foo) Bar()  {
    threshold := f.threshold.Get() // Reads the configuration
    // ...
}
```

In this example, the configuration getter is injected into the `NewFoo` factory method. It doesn’t impact a client of this function because it can still pass an `IntConfig` struct as it implements `intConfigGetter`. Then, we can only read the configuration in the `Bar` method, not modify it. Therefore, we can also use interfaces to restrict a type to a specific behavior for various reasons, such as semantics enforcement.

In this section, we saw three potential use cases where interfaces are generally considered as bringing value: factoring out a common behavior, creating some decoupling, and restricting a type to a certain behavior. Again, this list isn’t exhaustive, but it should give us a general understanding of when interfaces are helpful in Go.

Now, let’s finish this section and discuss the problems with interface pollution.

## Interface pollution

It’s fairly common to see interfaces being overused in Go projects. Perhaps the developer’s background was C# or Java, and they found it natural to create interfaces before concrete types. However, this isn’t how things should work in Go.

As we discussed, interfaces are made to create abstractions. And the main caveat when programming meets abstractions is remembering that **abstractions should be discovered, not created**. What does this mean? It means we shouldn’t start creating abstractions in our code if there is no immediate reason to do so. We shouldn’t design with interfaces but wait for a concrete need. Said differently, we should create an interface when we need it, not when we foresee that we could need it.

What’s the main problem if we overuse interfaces? The answer is that they make the code flow more complex. Adding a useless level of indirection doesn’t bring any value; it creates a worthless abstraction making the code more difficult to read, understand, and reason about. If we don’t have a strong reason for adding an interface and it’s unclear how an interface makes a code better, we should challenge this interface’s purpose. Why not call the implementation directly?

???+ note

    We may also experience performance overhead when calling a method through an interface. It requires a lookup in a hash table’s data structure to find the concrete type an interface points to. But this isn’t an issue in many contexts as the overhead is minimal.

In summary, we should be cautious when creating abstractions in our code—abstractions should be discovered, not created. It’s common for us, software developers, to overengineer our code by trying to guess what the perfect level of abstraction is, based on what we think we might need later. This process should be avoided because, in most cases, it pollutes our code with unnecessary abstractions, making it more complex to read.

!!! quote "Rob Pike"

    Don’t design with interfaces, discover them.

Let’s not try to solve a problem abstractly but solve what has to be solved now. Last, but not least, if it’s unclear how an interface makes the code better, we should probably consider removing it to make our code simpler.


================================================
FILE: docs/56-concurrency-faster.md
================================================
---
title: Thinking concurrency is always faster (#56)
comments: true
hide:
- toc
---

# Thinking concurrency is always faster

![](img/56-concurrency-faster.png)

A misconception among many developers is believing that a concurrent solution is always faster than a sequential one. This couldn’t be more wrong. The overall performance of a solution depends on many factors, such as the efficiency of our code structure (concurrency), which parts can be tackled in parallel, and the level of contention among the computation units. This post reminds us about some fundamental knowledge of concurrency in Go; then we will see a concrete example where a concurrent solution isn’t necessarily faster.

## Go Scheduling

A thread is the smallest unit of processing that an OS can perform. If a process wants to execute multiple actions simultaneously, it spins up multiple threads. These threads can be:

* _Concurrent_ — Two or more threads can start, run, and complete in overlapping time periods.
* _Parallel_ — The same task can be executed multiple times at once.

The OS is responsible for scheduling the thread’s processes optimally so that:

* All the threads can consume CPU cycles without being starved for too much time.
* The workload is distributed as evenly as possible among the different CPU cores.

???+ note

    The word thread can also have a different meaning at a CPU level. Each physical core can be composed of multiple logical cores (the concept of [hyper-threading](https://en.wikipedia.org/wiki/Hyper-threading)), and a logical core is also called a thread. In this post, when we use the word thread, we mean the unit of processing, not a logical core.

A CPU core executes different threads. When it switches from one thread to another, it executes an operation called _context switching_. The active thread consuming CPU cycles was in an _executing_ state and moves to a _runnable_ state, meaning it’s ready to be executed pending an available core. Context switching is considered an expensive operation because the OS needs to save the current execution state of a thread before the switch (such as the current register values).

As Go developers, we can’t create threads directly, but we can create goroutines, which can be thought of as application-level threads. However, whereas an OS thread is context-switched on and off a CPU core by the OS, a goroutine is context-switched on and off an OS thread by the Go runtime. Also, compared to an OS thread, a goroutine has a smaller memory footprint: 2 KB for goroutines from Go 1.4. An OS thread depends on the OS, but, for example, on Linux/x86–32, the default size is 2 MB (see https://man7.org/linux/man-pages/man3/pthread_create.3.html). Having a smaller size makes context switching faster.

???+ note

    Context switching a goroutine versus a thread is about 80% to 90% faster, depending on the architecture.

Let’s now discuss how the Go scheduler works to overview how goroutines are handled. Internally, the Go scheduler uses the following terminology (see [proc.go](https://github.com/golang/go/blob/go1.17.6/src/runtime/proc.go#L22)):

* _G_ — Goroutine
* _M_ — OS thread (stands for machine)
* _P_ — CPU core (stands for processor)

Each OS thread (M) is assigned to a CPU core (P) by the OS scheduler. Then, each goroutine (G) runs on an M. The GOMAXPROCS variable defines the limit of Ms in charge of executing user-level code simultaneously. But if a thread is blocked in a system call (for example, I/O), the scheduler can spin up more Ms. As of Go 1.5, GOMAXPROCS is by default equal to the number of available CPU cores.

A goroutine has a simpler lifecycle than an OS thread. It can be doing one of the following:

* _Executing_ — The goroutine is scheduled on an M and executing its instructions.
* _Runnable_ — The goroutine is waiting to be in an executing state.
* _Waiting_ — The goroutine is stopped and pending something completing, such as a system call or a synchronization operation (such as acquiring a mutex).

There’s one last stage to understand about the implementation of Go scheduling: when a goroutine is created but cannot be executed yet; for example, all the other Ms are already executing a G. In this scenario, what will the Go runtime do about it? The answer is queuing. The Go runtime handles two kinds of queues: one local queue per P and a global queue shared among all the Ps.

Figure 1 shows a given scheduling situation on a four-core machine with GOMAXPROCS equal to 4. The parts are the logical cores (Ps), goroutines (Gs), OS threads (Ms), local queues, and global queue:

<figure markdown>
  ![](img/go-scheduler.png)
  <figcaption>Figure 1: An example of the current state of a Go application executed on a four-core machine. Goroutines that aren’t in an executing state are either runnable (pending being executed) or waiting (pending a blocking operation)</figcaption>
</figure>

First, we can see five Ms, whereas GOMAXPROCS is set to 4. But as we mentioned, if needed, the Go runtime can create more OS threads than the GOMAXPROCS value.

P0, P1, and P3 are currently busy executing Go runtime threads. But P2 is presently idle as M3 is switched off P2, and there’s no goroutine to be executed. This isn’t a good situation because six runnable goroutines are pending being executed, some in the global queue and some in other local queues. How will the Go runtime handle this situation? Here’s the scheduling implementation in pseudocode (see [proc.go](https://github.com/golang/go/blob/go1.17.6/src/runtime/proc.go#L3291)):

```
runtime.schedule() {
    // Only 1/61 of the time, check the global runnable queue for a G.
    // If not found, check the local queue.
    // If not found,
    //     Try to steal from other Ps.
    //     If not, check the global runnable queue.
    //     If not found, poll network.
}
```

Every sixty-first execution, the Go scheduler will check whether goroutines from the global queue are available. If not, it will check its local queue. Meanwhile, if both the global and local queues are empty, the Go scheduler can pick up goroutines from other local queues. This principle in scheduling is called _work stealing_, and it allows an underutilized processor to actively look for another processor’s goroutines and _steal_ some.

One last important thing to mention: prior to Go 1.14, the scheduler was cooperative, which meant a goroutine could be context-switched off a thread only in specific blocking cases (for example, channel send or receive, I/O, waiting to acquire a mutex). Since Go 1.14, the Go scheduler is now preemptive: when a goroutine is running for a specific amount of time (10 ms), it will be marked preemptible and can be context-switched off to be replaced by another goroutine. This allows a long-running job to be forced to share CPU time.

Now that we understand the fundamentals of scheduling in Go, let’s look at a concrete example: implementing a merge sort in a parallel manner.

## Parallel Merge Sort

First, let’s briefly review how the merge sort algorithm works. Then we will implement a parallel version. Note that the objective isn’t to implement the most efficient version but to support a concrete example showing why concurrency isn’t always faster.

The merge sort algorithm works by breaking a list repeatedly into two sublists until each sublist consists of a single element and then merging these sublists so that the result is a sorted list (see figure 2). Each split operation splits the list into two sublists, whereas the merge operation merges two sublists into a sorted list.

<figure markdown>
  ![](img/mergesort.png)
  <figcaption>Figure 2: Applying the merge sort algorithm repeatedly breaks each list into two sublists. Then the algorithm uses a merge operation such that the resulting list is sorted</figcaption>
</figure>

Here is the sequential implementation of this algorithm. We don’t include all of the code as it’s not the main point of this section:

```go
func sequentialMergesort(s []int) {
    if len(s) <= 1 {
        return
    }

    middle := len(s) / 2
    sequentialMergesort(s[:middle]) // First half
    sequentialMergesort(s[middle:]) // Second half
    merge(s, middle) // Merges the two halves
}

func merge(s []int, middle int) {
    // ...
}
```

This algorithm has a structure that makes it open to concurrency. Indeed, as each _sequentialMergesort_ operation works on an independent set of data that doesn’t need to be fully copied (here, an independent view of the underlying array using slicing), we could distribute this workload among the CPU cores by spinning up each _sequentialMergesort_ operation in a different goroutine. Let’s write a first parallel implementation:

```go
func parallelMergesortV1(s []int) {
    if len(s) <= 1 {
        return
    }

    middle := len(s) / 2

    var wg sync.WaitGroup
    wg.Add(2)

    go func() { // Spins up the first half of the work in a goroutine
        defer wg.Done()
        parallelMergesortV1(s[:middle])
    }()

    go func() { // Spins up the second half of the work in a goroutine
        defer wg.Done()
        parallelMergesortV1(s[middle:])
    }()

    wg.Wait()
    merge(s, middle) // Merges the halves
}
```

In this version, each half of the workload is handled in a separate goroutine. The parent goroutine waits for both parts by using _sync.WaitGroup_. Hence, we call the Wait method before the merge operation.

We now have a parallel version of the merge sort algorithm. Therefore, if we run a benchmark to compare this version against the sequential one, the parallel version should be faster, correct? Let’s run it on a four-core machine with 10,000 elements:

```
Benchmark_sequentialMergesort-4       2278993555 ns/op
Benchmark_parallelMergesortV1-4      17525998709 ns/op
```

Surprisingly, the parallel version is almost an order of magnitude slower. How can we explain this result? How is it possible that a parallel version that distributes a workload across four cores is slower than a sequential version running on a single machine? Let’s analyze the problem.

If we have a slice of, say, 1,024 elements, the parent goroutine will spin up two goroutines, each in charge of handling a half consisting of 512 elements. Each of these goroutines will spin up two new goroutines in charge of handling 256 elements, then 128, and so on, until we spin up a goroutine to compute a single element.

If the workload that we want to parallelize is too small, meaning we’re going to compute it too fast, the benefit of distributing a job across cores is destroyed: the time it takes to create a goroutine and have the scheduler execute it is much too high compared to directly merging a tiny number of items in the current goroutine. Although goroutines are lightweight and faster to start than threads, we can still face cases where a workload is too small.

So what can we conclude from this result? Does it mean the merge sort algorithm cannot be parallelized? Wait, not so fast.

Let’s try another approach. Because merging a tiny number of elements within a new goroutine isn’t efficient, let’s define a threshold. This threshold will represent how many elements a half should contain in order to be handled in a parallel manner. If the number of elements in the half is fewer than this value, we will handle it sequentially. Here’s a new version:

```go
const max = 2048 // Defines the threshold

func parallelMergesortV2(s []int) {
    if len(s) <= 1 {
        return
    }

    if len(s) <= max {
        sequentialMergesort(s) // Calls our initial sequential version
    } else { // If bigger than the threshold, keeps the parallel version
        middle := len(s) / 2

        var wg sync.WaitGroup
        wg.Add(2)

        go func() {
            defer wg.Done()
            parallelMergesortV2(s[:middle])
        }()

        go func() {
            defer wg.Done()
            parallelMergesortV2(s[middle:])
        }()

        wg.Wait()
        merge(s, middle)
    }
}
```

If the number of elements in the s slice is smaller than max, we call the sequential version. Otherwise, we keep calling our parallel implementation. Does this approach impact the result? Yes, it does:

```
Benchmark_sequentialMergesort-4       2278993555 ns/op
Benchmark_parallelMergesortV1-4      17525998709 ns/op
Benchmark_parallelMergesortV2-4       1313010260 ns/op
```

Our v2 parallel implementation is more than 40% faster than the sequential one, thanks to this idea of defining a threshold to indicate when parallel should be more efficient than sequential.

???+ note

    Why did I set the threshold to 2,048? Because it was the optimal value for this specific workload on my machine. In general, such magic values should be defined carefully with benchmarks (running on an execution environment similar to production). It’s also pretty interesting to note that running the same algorithm in a programming language that doesn’t implement the concept of goroutines has an impact on the value. For example, running the same example in Java using threads means an optimal value closer to 8,192. This tends to illustrate how goroutines are more efficient than threads.

## Conclusion

We have seen throughout this post the fundamental concepts of scheduling in Go: the differences between a thread and a goroutine and how the Go runtime schedules goroutines. Meanwhile, using the parallel merge sort example, we illustrated that concurrency isn’t always necessarily faster. As we have seen, spinning up goroutines to handle minimal workloads (merging only a small set of elements) demolishes the benefit we could get from parallelism.

So, where should we go from here? We must keep in mind that concurrency isn’t always faster and shouldn’t be considered the default way to go for all problems. First, it makes things more complex. Also, modern CPUs have become incredibly efficient at executing sequential code and predictable code. For example, a superscalar processor can parallelize instruction execution over a single core with high efficiency.

Does this mean we shouldn’t use concurrency? Of course not. However, it’s essential to keep these conclusions in mind. If we’re not sure that a parallel version will be faster, the right approach may be to start with a simple sequential version and build from there using profiling (mistake #98, “[Not using Go diagnostics tooling](https://100go.co/98-profiling-execution-tracing/)”) and benchmarks (mistake #89, “[Writing inaccurate benchmarks](https://100go.co/89-benchmarks/)”), for example. It can be the only way to ensure that a concurrent implementation is worth it.


================================================
FILE: docs/89-benchmarks.md
================================================
---
title: Writing inaccurate benchmarks (#89)
comments: true
hide:
- toc
---

# Writing inaccurate benchmarks

![](img/89-benchmarks.png)

In general, we should never guess about performance. When writing optimizations, so many factors may come into play that even if we have a strong opinion about the results, it’s rarely a bad idea to test them. However, writing benchmarks isn’t straightforward. It can be pretty simple to write inaccurate benchmarks and make wrong assumptions based on them. The goal of this post is to examine four common and concrete traps leading to inaccuracy:

* Not resetting or pausing the timer
* Making wrong assumptions about micro-benchmarks
* Not being careful about compiler optimizations
* Being fooled by the observer effect

## General concepts

Before discussing these traps, let’s briefly review how benchmarks work in Go. The skeleton of a benchmark is as follows:

```go
func BenchmarkFoo(b *testing.B) {
	for i := 0; i < b.N; i++ {
		foo()
	}
}
```

The function name starts with the `Benchmark` prefix. The function under test (foo) is called within the `for` loop. `b.N` represents a variable number of iterations. When running a benchmark, Go tries to make it match the requested benchmark time. The benchmark time is set by default to 1 second and can be changed with the `-benchtime` flag. `b.N` starts at 1; if the benchmark completes in under 1 second, `b.N` is increased, and the benchmark runs again until `b.N` roughly matches benchtime:

```
$ go test -bench=.
cpu: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz
BenchmarkFoo-4                73          16511228 ns/op
```

Here, the benchmark took about 1 second, and `foo` was executed 73 times, for an average execution time of 16,511,228 nanoseconds. We can change the benchmark time using `-benchtime`:

```
$ go test -bench=. -benchtime=2s
BenchmarkFoo-4               150          15832169 ns/op
```

`foo` was executed roughly twice more than during the previous benchmark.

Next, let’s look at some common traps.

## Not resetting or pausing the timer

In some cases, we need to perform operations before the benchmark loop. These operations may take quite a while (for example, generating a large slice of data) and may significantly impact the benchmark results:

```go
func BenchmarkFoo(b *testing.B) {
	expensiveSetup()
	for i := 0; i < b.N; i++ {
		functionUnderTest()
	}
}
```

In this case, we can use the `ResetTimer` method before entering the loop:

```go
func BenchmarkFoo(b *testing.B) {
	expensiveSetup()
	b.ResetTimer() // Reset the benchmark timer
	for i := 0; i < b.N; i++ {
		functionUnderTest()
	}
}
```

Calling `ResetTimer` zeroes the elapsed benchmark time and memory allocation counters since the beginning of the test. This way, an expensive setup can be discarded from the test results.

What if we have to perform an expensive setup not just once but within each loop iteration?

```go
func BenchmarkFoo(b *testing.B) {
	for i := 0; i < b.N; i++ {
		expensiveSetup()
		functionUnderTest()
	}
}
```

We can’t reset the timer, because that would be executed during each loop iteration. But we can stop and resume the benchmark timer, surrounding the call to `expensiveSetup`:

```go
func BenchmarkFoo(b *testing.B) {
	for i := 0; i < b.N; i++ {
		b.StopTimer() // Pause the benchmark timer
		expensiveSetup()
		b.StartTimer() // Resume the benchmark timer
		functionUnderTest()
	}
}
```

Here, we pause the benchmark timer to perform the expensive setup and then resume the timer.

???+ note

    There’s one catch to remember about this approach: if the function under test is too fast to execute compared to the setup function, the benchmark may take too long to complete. The reason is that it would take much longer than 1 second to reach `benchtime`. Calculating the benchmark time is based solely on the execution time of `functionUnderTest`. So, if we wait a significant time in each loop iteration, the benchmark will be much slower than 1 second. If we want to keep the benchmark, one possible mitigation is to decrease `benchtime`.

We must be sure to use the timer methods to preserve the accuracy of a benchmark.

## Making wrong assumptions about micro-benchmarks

A micro-benchmark measures a tiny computation unit, and it can be extremely easy to make wrong assumptions about it. Let’s say, for example, that we aren’t sure whether to use `atomic.StoreInt32` or `atomic.StoreInt64` (assuming that the values we handle will always fit in 32 bits). We want to write a benchmark to compare both functions:

```go
func BenchmarkAtomicStoreInt32(b *testing.B) {
	var v int32
	for i := 0; i < b.N; i++ {
		atomic.StoreInt32(&v, 1)
	}
}

func BenchmarkAtomicStoreInt64(b *testing.B) {
	var v int64
	for i := 0; i < b.N; i++ {
		atomic.StoreInt64(&v, 1)
	}
}
```

If we run this benchmark, here’s some example output:

```
cpu: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz
BenchmarkAtomicStoreInt32
BenchmarkAtomicStoreInt32-4    197107742           5.682 ns/op
BenchmarkAtomicStoreInt64
BenchmarkAtomicStoreInt64-4    213917528           5.134 ns/op
```

We could easily take this benchmark for granted and decide to use `atomic.StoreInt64` because it appears to be faster. Now, for the sake of doing a fair benchmark, we reverse the order and test `atomic.StoreInt64` first, followed by `atomic.StoreInt32`. Here is some example output:

```
BenchmarkAtomicStoreInt64
BenchmarkAtomicStoreInt64-4    224900722           5.434 ns/op
BenchmarkAtomicStoreInt32
BenchmarkAtomicStoreInt32-4    230253900           5.159 ns/op
```

This time, `atomic.StoreInt32` has better results. What happened?

In the case of micro-benchmarks, many factors can impact the results, such as machine activity while running the benchmarks, power management, thermal scaling, and better cache alignment of a sequence of instructions. We must remember that many factors, even outside the scope of our Go project, can impact the results.

???+ note

    We should make sure the machine executing the benchmark is idle. However, external processes may run in the background, which may affect benchmark results. For that reason, tools such as `perflock` can limit how much CPU a benchmark can consume. For example, we can run a benchmark with 70% of the total available CPU, giving 30% to the OS and other processes and reducing the impact of the machine activity factor on the results.

One option is to increase the benchmark time using the `-benchtime` option. Similar to the law of large numbers in probability theory, if we run a benchmark a large number of times, it should tend to approach its expected value (assuming we omit the benefits of instructions caching and similar mechanics).

Another option is to use external tools on top of the classic benchmark tooling. For instance, the `benchstat` tool, which is part of the `golang.org/x` repository, allows us to compute and compare statistics about benchmark executions.

Let’s run the benchmark 10 times using the `-count` option and pipe the output to a specific file:

```
$ go test -bench=. -count=10 | tee stats.txt
cpu: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz
BenchmarkAtomicStoreInt32-4     234935682                5.124 ns/op
BenchmarkAtomicStoreInt32-4     235307204                5.112 ns/op
// ...
BenchmarkAtomicStoreInt64-4     235548591                5.107 ns/op
BenchmarkAtomicStoreInt64-4     235210292                5.090 ns/op
// ...
```

We can then run `benchstat` on this file:

```
$ benchstat stats.txt
name                time/op
AtomicStoreInt32-4  5.10ns ± 1%
AtomicStoreInt64-4  5.10ns ± 1%
```

The results are the same: both functions take on average 5.10 nanoseconds to complete. We also see the percent variation between the executions of a given benchmark: ± 1%. This metric tells us that both benchmarks are stable, giving us more confidence in the computed average results. Therefore, instead of concluding that `atomic.StoreInt32` is faster or slower, we can conclude that its execution time is similar to that of `atomic.StoreInt64` for the usage we tested (in a specific Go version on a particular machine).

In general, we should be cautious about micro-benchmarks. Many factors can significantly impact the results and potentially lead to wrong assumptions. Increasing the benchmark time or repeating the benchmark executions and computing stats with tools such as `benchstat` can be an efficient way to limit external factors and get more accurate results, leading to better conclusions.

Let’s also highlight that we should be careful about using the results of a micro-benchmark executed on a given machine if another system ends up running the application. The production system may act quite differently from the one on which we ran the micro-benchmark.

## Not being careful about compiler optimizations

Another common mistake related to writing benchmarks is being fooled by compiler optimizations, which can also lead to wrong benchmark assumptions. In this section, we look at Go issue 14813 (https://github.com/golang/go/issues/14813, also discussed by Go project member Dave Cheney) with a population count function (a function that counts the number of bits set to 1):

```go
const m1 = 0x5555555555555555
const m2 = 0x3333333333333333
const m4 = 0x0f0f0f0f0f0f0f0f
const h01 = 0x0101010101010101

func popcnt(x uint64) uint64 {
	x -= (x >> 1) & m1
	x = (x & m2) + ((x >> 2) & m2)
	x = (x + (x >> 4)) & m4
	return (x * h01) >> 56
}
```

This function takes and returns a `uint64`. To benchmark this function, we can write the following:

```go
func BenchmarkPopcnt1(b *testing.B) {
	for i := 0; i < b.N; i++ {
		popcnt(uint64(i))
	}
}
```

However, if we execute this benchmark, we get a surprisingly low result:

```
cpu: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz
BenchmarkPopcnt1-4      1000000000               0.2858 ns/op
```

A duration of 0.28 nanoseconds is roughly one clock cycle, so this number is unreasonably low. The problem is that the developer wasn’t careful enough about compiler optimizations. In this case, the function under test is simple enough to be a candidate for inlining: an optimization that replaces a function call with the body of the called function and lets us prevent a function call, which has a small footprint. Once the function is inlined, the compiler notices that the call has no side effects and replaces it with the following benchmark:

```go
func BenchmarkPopcnt1(b *testing.B) {
	for i := 0; i < b.N; i++ {
		// Empty
	}
}
```

The benchmark is now empty — which is why we got a result close to one clock cycle. To prevent this from happening, a best practice is to follow this pattern:

1. During each loop iteration, assign the result to a local variable (local in the context of the benchmark function).
2. Assign the latest result to a global variable.

In our case, we write the following benchmark:

```go
var global uint64 // Define a global variable

func BenchmarkPopcnt2(b *testing.B) {
	var v uint64 // Define a local variable
	for i := 0; i < b.N; i++ {
		v = popcnt(uint64(i)) // Assign the result to the local variable
	}
	global = v // Assign the result to the global variable
}
```

`global` is a global variable, whereas v is a local variable whose scope is the benchmark function. During each loop iteration, we assign the result of `popcnt` to the local variable. Then we assign the latest result to the global variable.

???+ note

    Why not assign the result of the popcnt call directly to global to simplify the test? Writing to a global variable is slower than writing to a local variable (these concepts are discussed in 100 Go Mistakes, mistake #95: “[Not understanding stack vs. heap](https://100go.co#not-understanding-stack-vs-heap-95)”). Therefore, we should write each result to a local variable to limit the footprint during each loop iteration.

If we run these two benchmarks, we now get a significant difference in the results:

```
cpu: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz
BenchmarkPopcnt1-4      1000000000               0.2858 ns/op
BenchmarkPopcnt2-4      606402058                1.993 ns/op
```

`BenchmarkPopcnt2` is the accurate version of the benchmark. It guarantees that we avoid the inlining optimizations, which can artificially lower the execution time or even remove the call to the function under test. Relying on the results of `BenchmarkPopcnt1` could have led to wrong assumptions.

Let’s remember the pattern to avoid compiler optimizations fooling benchmark results: assign the result of the function under test to a local variable, and then assign the latest result to a global variable. This best practice also prevents us from making incorrect assumptions.

## Being fooled by the observer effect

In physics, the observer effect is the disturbance of an observed system by the act of observation. This effect can also be seen in benchmarks and can lead to wrong assumptions about results. Let’s look at a concrete example and then try to mitigate it.

We want to implement a function receiving a matrix of `int64` elements. This matrix has a fixed number of 512 columns, and we want to compute the total sum of the first eight columns, as shown in figure 1.

<figure markdown>
  ![](img/matrix.png)
  <figcaption>Figure 1: Computing the sum of the first eight columns.</figcaption>
</figure>

For the sake of optimizations, we also want to determine whether varying the number of columns has an impact, so we also implement a second function with 513 columns. The implementation is the following:

```go
func calculateSum512(s [][512]int64) int64 {
	var sum int64
	for i := 0; i < len(s); i++ { // Iterate over each row
		for j := 0; j < 8; j++ { // Iterate over the first eight columns
			sum += s[i][j] // Increment sum
		}
	}
	return sum
}

func calculateSum513(s [][513]int64) int64 {
	// Same implementation as calculateSum512
}
```

We iterate over each row and then over the first eight columns, and we increment a sum variable that we return. The implementation in `calculateSum513` remains the same.

We want to benchmark these functions to decide which one is the most performant given a fixed number of rows:

```go
const rows = 1000

var res int64

func BenchmarkCalculateSum512(b *testing.B) {
	var sum int64
	s := createMatrix512(rows) // Create a matrix of 512 columns
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		sum = calculateSum512(s) // Create a matrix of 512 columns
	}
	res = sum
}

func BenchmarkCalculateSum513(b *testing.B) {
	var sum int64
	s := createMatrix513(rows) // Create a matrix of 513 columns
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		sum = calculateSum513(s) // Calculate the sum
	}
	res = sum
}
```

We want to create the matrix only once, to limit the footprint on the results. Therefore, we call `createMatrix512` and `createMatrix513` outside of the loop. We may expect the results to be similar as again we only want to iterate on the first eight columns, but this isn’t the case (on my machine):

```
cpu: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz
BenchmarkCalculateSum512-4        81854             15073 ns/op
BenchmarkCalculateSum513-4       161479              7358 ns/op
```

The second benchmark with 513 columns is about 50% faster. Again, because we iterate only over the first eight columns, this result is quite surprising.

To understand this difference, we need to understand the basics of CPU caches. In a nutshell, a CPU is composed of different caches (usually L1, L2, and L3). These caches reduce the average cost of accessing data from the main memory. In some conditions, the CPU can fetch data from the main memory and copy it to L1. In this case, the CPU tries to fetch into L1 the matrix’s subset that `calculateSum` is interested in (the first eight columns of each row). However, the matrix fits in memory in one case (513 columns) but not in the other case (512 columns).

???+ note

    This isn’t in the scope of this post to explain why, but we look at this problem in 100 Go Mistakes, mistake #91: “[Not understanding CPU caches.](https://100go.co#not-understanding-cpu-caches-91)”

Coming back to the benchmark, the main issue is that we keep reusing the same matrix in both cases. Because the function is repeated thousands of times, we don’t measure the function’s execution when it receives a plain new matrix. Instead, we measure a function that gets a matrix that already has a subset of the cells present in the cache. Therefore, because `calculateSum513` leads to fewer cache misses, it has a better execution time.

This is an example of the observer effect. Because we keep observing a repeatedly called CPU-bound function, CPU caching may come into play and significantly affect the results. In this example, to prevent this effect, we should create a matrix during each test instead of reusing one:

```go
func BenchmarkCalculateSum512(b *testing.B) {
	var sum int64
	for i := 0; i < b.N; i++ {
		b.StopTimer()
		s := createMatrix512(rows) // Create a new matrix during each loop iteration
		b.StartTimer()
		sum = calculateSum512(s)
	}
	res = sum
}
```

A new matrix is now created during each loop iteration. If we run the benchmark again (and adjust `benchtime` — otherwise, it takes too long to execute), the results are closer to each other:

```
cpu: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz
BenchmarkCalculateSum512-4         1116             33547 ns/op
BenchmarkCalculateSum513-4          998             35507 ns/op
```

Instead of making the incorrect assumption that calculateSum513 is faster, we see that both benchmarks lead to similar results when receiving a new matrix.

As we have seen in this post, because we were reusing the same matrix, CPU caches significantly impacted the results. To prevent this, we had to create a new matrix during each loop iteration. In general, we should remember that observing a function under test may lead to significant differences in results, especially in the context of micro-benchmarks of CPU-bound functions where low-level optimizations matter. Forcing a benchmark to re-create data during each iteration can be a good way to prevent this effect.

================================================
FILE: docs/9-generics.md
================================================
---
title: Being confused about when to use generics (#9)
comments: true
hide:
- toc
---

# Being confused about when to use generics

Generics is a fresh addition to the language. In a nutshell, it allows writing code with types that can be specified later and instantiated when needed. However, it can be pretty easy to be confused about when to use generics and when not to. Throughout this post, we will describe the concept of generics in Go and then delve into common use and misuses.

## Concepts

Consider the following function that extracts all the keys from a `map[string]int` type:

```go
func getKeys(m map[string]int) []string {
    var keys []string
    for k := range m {
        keys = append(keys, k)
    }
    return keys
}
```

What if we would like to use a similar feature for another map type such as a `map[int]string`? Before generics, Go developers had a couple of options: using code generation, reflection, or duplicating code.

For example, we could write two functions, one for each map type, or even try to extend `getKeys` to accept different map types:

```go
func getKeys(m any) ([]any, error) {
    switch t := m.(type) {
    default:
        return nil, fmt.Errorf("unknown type: %T", t)
    case map[string]int:
        var keys []any
        for k := range t {
            keys = append(keys, k)
        }
        return keys, nil
    case map[int]string:
        // Copy the extraction logic
    }
}
```

We can start noticing a couple of issues:

* First, it increases boilerplate code. Indeed, whenever we want to add a case, it will require duplicating the `range` loop.
*  Meanwhile, the function now accepts an empty interface, which means we are losing some of the benefits of Go being a typed language. Indeed, checking whether a type is supported is done at runtime instead of compile-time. Hence, we also need to return an error if the provided type is unknown.
* Last but not least, as the key type can be either `int` or `string`, we are obliged to return a slice of empty interfaces to factor out key types. This approach increases the effort on the caller-side as the client may also have to perform a type check of the keys or extra conversion.

Thanks to generics, we can now refactor this code using type parameters.

Type parameters are generic types we can use with functions and types. For example, the following function accepts a type parameter:

```go
func foo[T any](t T) {
    // ...
}
```

When calling `foo`, we will pass a type argument of any type. Passing a type argument is called instantiation because the work is done at compile time which keeps type safety as part of the core language features and avoids runtime overheads.

Let’s get back to the `getKeys` function and use type parameters to write a generic version that would accept any kind of map:

```go
func getKeys[K comparable, V any](m map[K]V) []K {
  var keys []K
  for k := range m {
    keys = append(keys, k)
  }
  return keys
}
```

To handle the map, we defined two kinds of type parameters. First, the values can be of any type: `V any`. However, in Go, the map keys can’t be of any type. For example, we cannot use slices:

```go
var m map[[]byte]int
```

This code leads to a compilation error: `invalid map key type []byte`. Therefore, instead of accepting any key type, we are obliged to restrict type arguments so that the key type meets specific requirements. Here, being comparable (we can use `==` or `!=`). Hence, we defined `K` as `comparable` instead of `any`.

Restricting type arguments to match specific requirements is called a constraint. A constraint is an interface type that can contain:

* A set of behaviors (methods)
* But also arbitrary type

Let’s see a concrete example for the latter. Imagine we don’t want to accept any `comparable` type for map key type. For instance, we would like to restrict it to either `int` or `string` types. We can define a custom constraint this way:

```go
type customConstraint interface {
   ~int | ~string // Define a custom type that will restrict types to int and string
}

// Change the type parameter K to be custom
func getKeys[K customConstraint, V any](m map[K]V) []K {
   // Same implementation
}
```

First, we define a `customConstraint` interface to restrict the types to be either `int` or `string` using the union operator `|` (we will discuss the use of `~` a bit later). Then, `K` is now a `customConstraint` instead of a `comparable` as before.

Now, the signature of `getKeys` enforces that we can call it with a map of any value type, but the key type has to be an `int` or a `string`. For example, on the caller-side:

```go
m = map[string]int{
   "one":   1,
   "two":   2,
   "three": 3,
}
keys := getKeys(m)
```

Note that Go can infer that `getKeys` is called with a `string` type argument. The previous call was similar to this:

```go
keys := getKeys[string](m)
```

???+ note

    What’s the difference between a constraint using `~int` or `int`? Using `int` restricts it to that type, whereas `~int` restricts all the types whose underlying type is an `int`.

    To illustrate it, let’s imagine a constraint where we would like to restrict a type to any `int` type implementing the `String() string` method:

    ```go
    type customConstraint interface {
       ~int
       String() string
    }
    ```

    Using this constraint will restrict type arguments to custom types like this one:

    ```go
    type customInt int

    func (i customInt) String() string {
       return strconv.Itoa(int(i))
    }
    ```

    As `customInt` is an `int` and implements the `String() string` method, the `customInt` type satisfies the constraint defined.

    However, if we change the constraint to contain an `int` instead of an `~int`, using `customInt` would lead to a compilation error because the `int` type doesn’t implement `String() string`.

Let’s also note the `constraints` package contains a set of common constraints such as `Signed` that includes all the signed integer types. Let’s ensure that a constraint doesn’t already exist in this package before creating a new one.

So far, we have discussed examples using generics for functions. However, we can also use generics with data structures.

For example, we will create a linked list containing values of any type. Meanwhile, we will write an `Add` method to append a node:

```go
type Node[T any] struct { // Use type parameter
   Val  T
   next *Node[T]
}

func (n *Node[T]) Add(next *Node[T]) { // Instantiate type receiver
   n.next = next
}
```

We use type parameters to define `T` and use both fields in `Node`. Regarding the method, the receiver is instantiated. Indeed, because `Node` is generic, it has to follow also the type parameter defined.

One last thing to note about type parameters: they can’t be used on methods, only on functions. For example, the following method wouldn’t compile:

```go
type Foo struct {}

func (Foo) bar[T any](t T) {}
```

```
./main.go:29:15: methods cannot have type parameters
```

Now, let’s delve into concrete cases where we should and shouldn’t use generics.

## Common uses and misuses

So when are generics useful? Let’s discuss a couple of common uses where generics **are** recommended:

* Data structures. For example, we can use generics to factor out the element type if we implement a binary tree, a linked list, or a heap.
* Functions working with slices, maps, and channels of any type. For example, a function to merge two channels would work with any channel type. Hence, we could use type parameters to factor out the channel type:

  ```go
  func merge[T any](ch1, ch2 <-chan T) <-chan T {
      // ...
  }
  ```

* Meanwhile, instead of factoring out a type, we can factor out behaviors. For example, the `sort` package contains functions to sort different slice types such as `sort.Ints` or `sort.Float64s`. Using type parameters, we can factor out the sorting behaviors that rely on three methods, `Len`, `Less`, and `Swap`:

  ```go
  type sliceFn[T any] struct { // Use type parameter
     s       []T
     compare func(T, T) bool // Compare two T elements
  }

  func (s sliceFn[T]) Len() int           { return len(s.s) }
  func (s sliceFn[T]) Less(i, j int) bool { return s.compare(s.s[i], s.s[j]) }
  func (s sliceFn[T]) Swap(i, j int)      { s.s[i], s.s[j] = s.s[j], s.s[i] }
  ```

Conversely, when is it recommended **not** to use generics?

* When just calling a method of the type argument. For example, consider a function that receives an `io.Writer` and call the `Write` method:

  ```go
  func foo[T io.Writer](w T) {
     b := getBytes()
     _, _ = w.Write(b)
  }
  ```

* When it makes our code more complex. Generics are never mandatory, and as Go developers, we have been able to live without them for more than a decade. If writing generic functions or structures we figure out that it doesn’t make our code clearer, we should probably reconsider our decision for this particular use case.

## Conclusion

Though generics can be very helpful in particular conditions, we should be cautious about when to use them and not use them.

In general, when we want to answer when not to use generics, we can find similarities with when not to use interfaces. Indeed, generics introduce a form of abstraction, and we have to remember that unnecessary abstractions introduce complexity.

Let’s not pollute our code with needless abstractions, and let’s focus on solving concrete problems for now. It means that we shouldn’t use type parameters prematurely. Let’s wait until we are about to write boilerplate code to consider using generics.



================================================
FILE: docs/92-false-sharing.md
================================================
---
title: Writing concurrent code that leads to false sharing (#92)
comments: true
hide:
- toc
status: new
---

# Writing concurrent code that leads to false sharing

![](img/false-sharing.jpeg)

In previous sections, we have discussed the fundamental concepts of CPU caching. We have seen that some specific caches (typically, L1 and L2) aren’t shared among all the logical cores but are specific to a physical core. This specificity has some concrete impacts such as concurrency and the concept of false sharing, which can lead to a significant performance decrease. Let’s look at what false sharing is via an example and then see how to prevent it.

In this example, we use two structs, `Input` and `Result`:

```go
type Input struct {
    a int64
    b int64
}

type Result struct {
    sumA int64
    sumB int64
}
```

The goal is to implement a `count` function that receives a slice of `Input` and computes the following:

* The sum of all the `Input.a` fields into `Result.sumA`
* The sum of all the `Input.b` fields into `Result.sumB`

For the sake of the example, we implement a concurrent solution with one goroutine that computes `sumA` and another that computes `sumB`:

```go
func count(inputs []Input) Result {
    wg := sync.WaitGroup{}
    wg.Add(2)

    result := Result{} // Init the result struct

    go func() {
        for i := 0; i < len(inputs); i++ {
            result.sumA += inputs[i].a // Computes sumA
        }
        wg.Done()
    }()

    go func() {
        for i := 0; i < len(inputs); i++ {
            result.sumB += inputs[i].b // Computes sumB
        }
        wg.Done()
    }()

    wg.Wait()
    return result
}
```

We spin up two goroutines: one that iterates over each a field and another that iterates over each b field. This example is fine from a concurrency perspective. For instance, it doesn’t lead to a data race, because each goroutine increments its own variable. But this example illustrates the false sharing concept that degrades expected performance.

Let’s look at the main memory. Because `sumA` and `sumB` are allocated contiguously, in most cases (seven out of eight), both variables are allocated to the same memory block:

<figure markdown>
  ![](img/false-sharing-1.svg)
  <figcaption>In this example, sumA and sumB are part of the same memory block.</figcaption>
</figure>


Now, let’s assume that the machine contains two cores. In most cases, we should eventually have two threads scheduled on different cores. So if the CPU decides to copy this memory block to a cache line, it is copied twice:

<figure markdown>
  ![](img/false-sharing-2.svg)
  <figcaption>Each block is copied to a cache line on both code 0 and core 1.</figcaption>
</figure>

Both cache lines are replicated because L1D (L1 data) is per core. Recall that in our example, each goroutine updates its own variable: `sumA` on one side, and `sumB` on the other side:

<figure markdown>
  ![](img/false-sharing-3.svg)
  <figcaption>Each goroutine updates its own variable.</figcaption>
</figure>

Because these cache lines are replicated, one of the goals of the CPU is to guarantee cache coherency. For example, if one goroutine updates `sumA` and another reads `sumA` (after some synchronization), we expect our application to get the latest value.

However, our example doesn’t do exactly this. Both goroutines access their own variables, not a shared one. We might expect the CPU to know about this and understand that it isn’t a conflict, but this isn’t the case. When we write a variable that’s in a cache, the granularity tracked by the CPU isn’t the variable: it’s the cache line.

When a cache line is shared across multiple cores and at least one goroutine is a writer, the entire cache line is invalidated. This happens even if the updates are logically independent (for example, `sumA` and `sumB`). This is the problem of false sharing, and it degrades performance.

???+ note

    Internally, a CPU uses the [MESI protocol](https://en.wikipedia.org/wiki/MESI_protocol) to guarantee cache coherency. It tracks each cache line, marking it modified, exclusive, shared, or invalid (MESI).

One of the most important aspects to understand about memory and caching is that sharing memory across cores isn’t real—it’s an illusion. This understanding comes from the fact that we don’t consider a machine a black box; instead, we try to have mechanical sympathy with underlying levels.

So how do we solve false sharing? There are two main solutions.

The first solution is to use the same approach we’ve shown but ensure that `sumA` and `sumB` aren’t part of the same cache line. For example, we can update the `Result` struct to add _padding_ between the fields. Padding is a technique to allocate extra memory. Because an `int64` requires an 8-byte allocation and a cache line 64 bytes long, we need 64 – 8 = 56 bytes of padding:

```go hl_lines="3"
type Result struct {
    sumA int64
    _    [56]byte // Padding
    sumB int64
}
```

The next figure shows a possible memory allocation. Using padding, `sumA` and `sumB` will always be part of different memory blocks and hence different cache lines.

<figure markdown>
  ![](img/false-sharing-4.svg)
  <figcaption>sumA and sumB are part of different memory blocks.</figcaption>
</figure>

If we benchmark both solutions (with and without padding), we see that the padding solution is significantly faster (about 40% on my machine). This is an important improvement that results from the addition of padding between the two fields to prevent false sharing.

The second solution is to rework the structure of the algorithm. For example, instead of having both goroutines share the same struct, we can make them communicate their local result via channels. The result benchmark is roughly the same as with padding.

In summary, we must remember that sharing memory across goroutines is an illusion at the lowest memory levels. False sharing occurs when a cache line is shared across two cores when at least one goroutine is a writer. If we need to optimize an application that relies on concurrency, we should check whether false sharing applies, because this pattern is known to degrade application performance. We can prevent false sharing with either padding or communication.


================================================
FILE: docs/98-profiling-execution-tracing.md
================================================
---
title: Not using Go diagnostics tooling (#98)
comments: true
hide:
- toc
---

# Not using Go diagnostics tooling

![](img/98-profiling-execution-tracing.png)

Go offers a few excellent diagnostics tools to help us get insights into how an application performs. This post focuses on the most important ones: profiling and the execution tracer. Both tools are so important that they should be part of the core toolset of any Go developer who is interested in optimization. First, let’s discuss profiling.

## Profiling

Profiling provides insights into the execution of an application. It allows us to resolve performance issues, detect contention, locate memory leaks, and more. These insights can be collected via several profiles:

* `CPU`— Determines where an application spends its time
* `Goroutine`— Reports the stack traces of the ongoing goroutines
* `Heap`— Reports heap memory allocation to monitor current memory usage and check for possible memory leaks
* `Mutex`— Reports lock contentions to see the behaviors of the mutexes used in our code and whether an application spends too much time in locking calls
* `Block`— Shows where goroutines block waiting on synchronization primitives

Profiling is achieved via instrumentation using a tool called a profiler, in Go: `pprof`. First, let’s understand how and when to enable `pprof`; then, we discuss the most critical profile types.

### Enabling pprof

There are several ways to enable `pprof`. For example, we can use the `net/http/pprof` package to serve the profiling data via HTTP:

```go
package main

import (
    "fmt"
    "log"
    "net/http"
    _ "net/http/pprof" // Blank import to pprof
)

func main() {
    // Exposes an HTTP endpoint
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "")
    })
    log.Fatal(http.ListenAndServe(":80", nil))
}
```

Importing `net/http/pprof` leads to a side effect that allows us to reach the pprof URL: [http://host/debug/pprof](http://host/debug/pprof). Note that enabling `pprof` is safe even in production ([https://go.dev/doc/diagnostics#profiling](https://go.dev/doc/diagnostics#profiling)). The profiles that impact performance, such as CPU profiling, aren’t enabled by default, nor do they run continuously: they are activated only for a specific period.

Now that we have seen how to expose a `pprof` endpoint, let’s discuss the most common profiles.

### CPU Profiling

The CPU profiler relies on the OS and signaling. When it is activated, the application asks the OS to interrupt it every 10 ms by default via a `SIGPROF` signal. When the application receives a `SIGPROF`, it suspends the current activity and transfers the execution to the profiler. The profiler collects data such as the current goroutine activity and aggregates execution statistics that we can retrieve. Then it stops, and the execution resumes until the next `SIGPROF`.

We can access the /debug/pprof/profile endpoint to activate CPU profiling. Accessing this endpoint executes CPU profiling for 30 seconds by default. For 30 seconds, our application is interrupted every 10 ms. Note that we can change these two default values: we can use the `seconds` parameter to pass to the endpoint how long the profiling should last (for example, /debug/pprof/profile?seconds=15), and we can change the interruption rate (even to less than 10 ms). But in most cases, 10 ms should be enough, and in decreasing this value (meaning increasing the rate), we should be careful not to harm performance. After 30 seconds, we download the results of the CPU profiler.

???+ note

    We can also enable the CPU profiler using the `-cpuprofile` flag, such as when running a benchmark. For example, the following command produces the same type of file that can be downloaded via /debug/ pprof/profile.
    
    ```
    $ go test -bench=. -cpuprofile profile.out
    ```

From this file, we can navigate to the results using `go tool`:

```
$ go tool pprof -http=:8080 <file>
```

This command opens a web UI showing the call graph. The next figure shows an example taken from an application. The larger the arrow, the more it was a hot path. We can then navigate into this graph and get execution insights.

<figure markdown>
  ![](img/screen-pprof-cpu.png)
  <figcaption>Figure 1: The call graph of an application during 30 seconds.</figcaption>
</figure>

For example, the graph in the next figure tells us that during 30 seconds, 0.06 seconds were spent in the `decode` method (`*FetchResponse` receiver). Of these 0.06 seconds, 0.02 were spent in `RecordBatch.decode` and 0.01 in `makemap` (creating a map).

<figure markdown>
  ![](img/screen-pprof-sarama.png)
  <figcaption>Figure 2: Example call graph.</figcaption>
</figure>

We can also access this kind of information from the web UI with different representations. For example, the Top view sorts the functions per execution time, and Flame Graph visualizes the execution time hierarchy. The UI can even display the expensive parts of the source code line by line.

???+ note

    We can also delve into profiling data via a command line. However, we focus on the web UI in this post.

Thanks to this data, we can get a general idea of how an application behaves:

* Too many calls to `runtime.mallogc` can mean an excessive number of small heap allocations that we can try to minimize.
* Too much time spent in channel operations or mutex locks can indicate excessive contention that is harming the application’s performance.
* Too much time spent on `syscall.Read` or `syscall.Write` means the application spends a significant amount of time in Kernel mode. Working on I/O buffering may be an avenue for improvement.

These are the kinds of insights we can get from the CPU profiler. It’s valuable to understand the hottest code path and identify bottlenecks. But it won’t determine more than the configured rate because the CPU profiler is executed at a fixed pace (by default, 10 ms). To get finer-grained insights, we should use tracing, which we discuss later in this post.

???+ note

    We can also attach labels to the different functions. For example, imagine a common function called from different clients. To track the time spent for both clients, we can use `pprof.Labels`.

### Heap Profiling

Heap profiling allows us to get statistics about the current heap usage. Like CPU profiling, heap profiling is sample-based. We can change this rate, but we shouldn’t be too granular because the more we decrease the rate, the more effort heap profiling will require to collect data. By default, samples are profiled at one allocation for every 512 KB of heap allocation.

If we reach /debug/pprof/heap/, we get raw data that can be hard to read. However, we can download a heap profile using /debug/pprof/heap/?debug=0 and then open it with `go tool` (the same command as in the previous section) to navigate into the data using the web UI.

The next figure shows an example of a heap graph. Calling the `MetadataResponse.decode` method leads to allocating 1536 KB of heap data (which represents 6.32% of the total heap). However, 0 out of these 1536 KB were allocated by this function directly, so we need to inspect the second call. The `TopicMetadata.decode` method allocated 512 KB out of the 1536 KB; the rest — 1024 KB — were allocated in another method.

<figure markdown>
  ![](img/screen-pprof-heap.png)
  <figcaption>Figure 3: A heap graph.</figcaption>
</figure>

This is how we can navigate the call chain to understand what part of an application is responsible for most of the heap allocations. We can also look at different sample types:

* `alloc_objects`— Total number of objects allocated
* `alloc_space`— Total amount of memory allocated
* `inuse_object`s — Number of objects allocated and not yet released
* `inuse_space`— Amount of memory allocated and not yet released

Another very helpful capability with heap profiling is tracking memory leaks. With a GC-based language, the usual procedure is the following:

1. Trigger a GC.
2. Download heap data.
3. Wait for a few seconds/minutes.
4. Trigger another GC.
5. Download another heap data.
6. Compare.

Forcing a GC before downloading data is a way to prevent false assumptions. For example, if we see a peak of retained objects without running a GC first, we cannot be sure whether it’s a leak or objects that the next GC will collect.

Using `pprof`, we can download a heap profile and force a GC in the meantime. The procedure in Go is the following:

1. Go to /debug/pprof/heap?gc=1 (trigger the GC and download the heap profile).
2. Wait for a few seconds/minutes.
3. Go to /debug/pprof/heap?gc=1 again.
4. Use go tool to compare both heap profiles:

```
$ go tool pprof -http=:8080 -diff_base <file2> <file1>
```

The next figure shows the kind of data we can access. For example, the amount of heap memory held by the newTopicProducer method (top left) has decreased (–513 KB). In contrast, the amount held by updateMetadata (bottom right) has increased (+512 KB). Slow increases are normal. The second heap profile may have been calculated in the middle of a service call, for example. We can repeat this process or wait longer; the important part is to track steady increases in allocations of a specific object.

<figure markdown>
  ![](img/screen-pprof-heap-diff.png)
  <figcaption>Figure 4: The differences between the two heap profiles.</figcaption>
</figure>

???+ note

    Another type of profiling related to the heap is `allocs`, which reports allocations. Heap profiling shows the current state of the heap memory. To get insights about past memory allocations since the application started, we can use allocations profiling. As discussed, because stack allocations are cheap, they aren’t part of this profiling, which only focuses on the heap.

### Goroutine Profiling

The `goroutine` profile reports the stack trace of all the current goroutines in an application. We can download a file using /debug/pprof/goroutine/?debug=0 and use go tool again. The next figure shows the kind of information we can get.

<figure markdown>
  ![](img/screen-pprof-goroutines.png)
  <figcaption>Figure 5: Goroutine graph.</figcaption>
</figure>

We can see the current state of the application and how many goroutines were created per function. In this case, `withRecover` has created 296 ongoing goroutines (63%), and 29 were related to a call to `responseFeeder`.

This kind of information is also beneficial if we suspect goroutine leaks. We can look at goroutine profiler data to know which part of a system is the suspect.

### Block Profiling

The `block` profile reports where ongoing goroutines block waiting on synchronization primitives. Possibilities include

* Sending or receiving on an unbuffered channel
* Sending to a full channel
* Receiving from an empty channel
* Mutex contention
* Network or filesystem waits

Block profiling also records the amount of time a goroutine has been waiting and is accessible via /debug/pprof/block. This profile can be extremely helpful if we suspect that performance is being harmed by blocking calls.

The `block` profile isn’t enabled by default: we have to call `runtime.SetBlockProfileRate` to enable it. This function controls the fraction of goroutine blocking events that are reported. Once enabled, the profiler will keep collecting data in the background even if we don’t call the /debug/pprof/block endpoint. Let’s be cautious if we want to set a high rate so we don’t harm performance.

???+ note

    If we face a deadlock or suspect that goroutines are in a blocked state, the full goroutine stack dump (/debug/pprof/goroutine/?debug=2) creates a dump of all the current goroutine stack traces. This can be helpful as a first analysis step. For example, the following dump shows a Sarama goroutine blocked for 1,420 minutes on a channel-receive operation:

    ```
    goroutine 2494290 [chan receive, 1420 minutes]:
    github.com/Shopify/sarama.(*syncProducer).SendMessages(0xc00071a090,
    [CA]{0xc0009bb800, 0xfb, 0xfb})
    /app/vendor/github.com/Shopify/sarama/sync_producer.go:117 +0x149
    ```

### Mutex Profiling

The last profile type is related to blocking but only regarding mutexes. If we suspect that our application spends significant time waiting for locking mutexes, thus harming execution, we can use mutex profiling. It’s accessible via /debug/pprof/mutex.

This profile works in a manner similar to that for blocking. It’s disabled by default: we have to enable it using `runtime.SetMutexProfileFraction`, which controls the fraction of mutex contention events reported.

Following are a few additional notes about profiling:

* We haven’t mentioned the `threadcreate` profile because it’s been broken since 2013 ([https://github.com/golang/go/issues/6104](https://github.com/golang/go/issues/6104)).
* Be sure to enable only one profiler at a time: for example, do not enable CPU and heap profiling simultaneously. Doing so can lead to erroneous observations.
* `pprof` is extensible, and we can create our own custom profiles using `pprof.Profile`.

We have seen the most important profiles that we can enable to help us understand how an application performs and possible avenues for optimization. In general, enabling `pprof` is recommended, even in production, because in most cases it offers an excellent balance between its footprint and the amount of insight we can get from it. Some profiles, such as the CPU profile, lead to performance penalties but only during the time they are enabled.

Let’s now look at the execution tracer.

## Execution Tracer

The execution tracer is a tool that captures a wide range of runtime events with `go tool` to make them available for visualization. It is helpful for the following:

* Understanding runtime events such as how the GC performs
* Understanding how goroutines execute
* Identifying poorly parallelized execution

Let’s try it with an example given the Concurrency isn’t Always Faster in Go section. We discussed two parallel versions of the merge sort algorithm. The issue with the first version was poor parallelization, leading to the creation of too many goroutines. Let’s see how the tracer can help us in validating this statement.

We will write a benchmark for the first version and execute it with the -trace flag to enable the execution tracer:

```
$ go test -bench=. -v -trace=trace.out
```

???+ note

    We can also download a remote trace file using the /debug/pprof/ trace?debug=0 pprof endpoint.

This command creates a trace.out file that we can open using go tool:

```
$ go tool trace trace.out
2021/11/26 21:36:03 Parsing trace...
2021/11/26 21:36:31 Splitting trace...
2021/11/26 21:37:00 Opening browser. Trace viewer is listening on
    http://127.0.0.1:54518
```

The web browser opens, and we can click View Trace to see all the traces during a specific timeframe, as shown in the next figure. This figure represents about 150 ms. We can see multiple helpful metrics, such as the goroutine count and the heap size. The heap size grows steadily until a GC is triggered. We can also observe the activity of the Go application per CPU core. The timeframe starts with user-level code; then a “stop the
world” is executed, which occupies the four CPU cores for approximately 40 ms.

<figure markdown>
  ![](img/tracing.png)
  <figcaption>Figure 6: Showing goroutine activity and runtime events such as a GC phase.</figcaption>
</figure>

Regarding concurrency, we can see that this version uses all the available CPU cores on the machine. However, the next figure zooms in on a portion of 1 ms. Each bar corresponds to a single goroutine execution. Having too many small bars doesn’t look right: it means execution that is poorly parallelized.

<figure markdown>
  ![](img/screen-mergesort1.png)
  <figcaption>Figure 7: Too many small bars mean poorly parallelized execution.</figcaption>
</figure>

The next figure zooms even closer to see how these goroutines are orchestrated. Roughly 50% of the CPU time isn’t spent executing application code. The white spaces represent the time the Go runtime takes to spin up and orchestrate new goroutines.

<figure markdown>
  ![](img/screen-mergesort11.png)
  <figcaption>Figure 8: About 50% of CPU time is spent handling goroutine switches.</figcaption>
</figure>

Let’s compare this with the second parallel implementation, which was about an order of magnitude faster. The next figure again zooms to a 1 ms timeframe.

<figure markdown>
  ![](img/screen-mergesort2.png)
  <figcaption>Figure 9: The number of white spaces has been significantly reduced, proving that the CPU is more fully occupied.</figcaption>
</figure>

Each goroutine takes more time to execute, and the number of white spaces has been significantly reduced. Hence, the CPU is much more occupied executing application code than it was in the first version. Each millisecond of CPU time is spent more efficiently, explaining the benchmark differences.

Note that the granularity of the traces is per goroutine, not per function like CPU profiling. However, it’s possible to define user-level tasks to get insights per function or group of functions using the `runtime/trace` package.

For example, imagine a function that computes a Fibonacci number and then writes it to a global variable using atomic. We can define two different tasks:

```go
var v int64
// Creates a fibonacci task
ctx, fibTask := trace.NewTask(context.Background(), "fibonacci")
trace.WithRegion(ctx, "main", func() {
    v = fibonacci(10)
})
fibTask.End()

// Creates a store task
ctx, fibStore := trace.NewTask(ctx, "store")
trace.WithRegion(ctx, "main", func() {
    atomic.StoreInt64(&result, v)
})
fibStore.End()
```

Using `go tool`, we can get more precise information about how these two tasks perform. In the previous trace UI, we can see the boundaries for each task per goroutine. In User-Defined Tasks, we can follow the duration distribution:

<figure markdown>
  ![](img/screen-tracing-user-level.png)
  <figcaption>Figure 10: Distribution of user-level tasks.</figcaption>
</figure>



We see that in most cases, the `fibonacci` task is executed in less than 15 microseconds, whereas the `store` task takes less than 6309 nanoseconds.

In the previous section, we discussed the kinds of information we can get from CPU profiling. What are the main differences compared to the data we can get from user-level traces?

* CPU profiling:
    * Sample-based
    * Per function
    * Doesn’t go below the sampling rate (10 ms by default)
* User-level traces:
    * Not sample-based
    * Per-goroutine execution (unless we use the `runtime/trace` package)
    * Time executions aren’t bound by any rate
    
In summary, the execution tracer is a powerful tool for understanding how an application performs. As we have seen with the merge sort example, we can identify poorly parallelized execution. However, the tracer’s granularity remains per goroutine unless we manually use `runtime/trace` compared to a CPU profile, for example. We can use both profiling and the execution tracer to get the most out of the standard Go diagnostics tools when optimizing an application.


================================================
FILE: docs/CNAME
================================================
100go.co


================================================
FILE: docs/book.md
================================================
---
hide:
- toc
---

# 100 Go Mistakes and How to Avoid Them

![](img/cover.png)

## Description

If you're a Go developer looking to improve your skills, the _100 Go Mistakes and How to Avoid Them_ book is for you. With a focus on practical examples, this book covers a wide range of topics from concurrency and error handling to testing and code organization. You'll learn to write more idiomatic, efficient, and maintainable code and become a proficient Go developer.

Read a [summary](index.md) of the 100 mistakes or the [first chapter](chapter-1.md).

## Quotes and Ratings

!!! quote "Krystian (Goodreads user)"

    This is an **exceptional book**. Usually, if a book contains either high-quality explanations or is written succinctly, I consider myself lucky to have found it. This one combines these two characteristics, which is **super rare**. It's another Go book for me and I still had quite a lot of "a-ha!" moments while reading it, and all of that without the unnecessary fluff, just straight to the point.

!!! quote "Akash Chetty"

    The book is completely **exceptional**, especially the examples carved out for each topic are really great. There is one topic that I struggled to understand is Concurrency but the way it is explained in this book is truly an art of genius.

!!! quote "Neeraj Shah"

    This should be the **required reading** for all Golang developers before they touch code in **Production**... It's the Golang equivalent of the legendary 'Effective Java' by Joshua Bloch.

!!! quote "Anupam Sengupta"

    Not having this will be the **101st mistake** a Go programmer could make.

<figure markdown>
  ![](img/ratings-goodreads.png){width="300"}
  ![](img/ratings-amazon.png){width="300"}
  ![](img/ratings-manning.png)
  <figcaption>Manning, Goodreads, and Amazon reviews: 4.7/5 avg rating</figcaption>
</figure>

## Where to Buy?

* _100 Go Mistakes and How to Avoid Them_ (🇬🇧 edition: paper, digital, or audiobook)
	* [Manning](https://www.manning.com/books/100-go-mistakes-and-how-to-avoid-them) (please make sure to use my personal discount code for -35%: `au35har`)
	* [O’Reilly](https://www.oreilly.com/library/view/100-go-mistakes/9781617299599/)
	* Amazon: [.com](https://www.amazon.com/dp/1617299596), [.co.uk](https://www.amazon.co.uk/dp/B0BBSNJR6B), [.de](https://www.amazon.de/dp/B0BBHQD8BQ), [.fr](https://www.amazon.fr/100-Mistakes-How-Avoid-Them/dp/1617299596), [.in](https://www.amazon.in/dp/B0BBHQD8BQ), [.co.jp](https://www.amazon.co.jp/dp/B0BBHQD8BQ), [.es](https://www.amazon.es/dp/B0BBHQD8BQ), [.it](https://www.amazon.it/dp/B0BBHQD8BQ), [.com.br](https://www.amazon.com.br/dp/B0BBHQD8BQ)

* _Go言語100Tips 開発者にありがちな間違いへの対処法_ (🇯🇵 edition: paper or digital)
	* Amazon: [.co.jp](https://www.amazon.co.jp/exec/obidos/ASIN/4295017531/)

* _100个Go语言典型错误_ (🇨🇳 edition: paper or digital)
    * [Douban.com](https://read.douban.com/ebook/455919353/)
    
* _Go 100가지 실수 패턴과 솔루션_ (🇰🇷 edition: paper or digital)
    * [Yes24.com](https://m.yes24.com/Goods/Detail/124158773)

<figure markdown>
  ![](img/cover-en.jpg){width="200"}
  ![](img/cover-jp.jpg){width="200"}
  ![](img/cover-cn.jpg){width="170"}
  ![](img/cover-kr.png){width="200"}
  <figcaption>Covers (English, Japanese, Chinese, and Korean)</figcaption>
</figure>

## About the Author

[Teiva Harsanyi](https://teivah.dev) is a senior software engineer at Google. He has worked in various domains, including insurance, transportation, and safety-critical industries like air traffic management. He is passionate about Go and how to design and implement reliable systems.


================================================
FILE: docs/chapter-1.md
================================================
---
title: Read the First Chapter
hide:
- toc
---

# Go: Simple to learn but hard to master

This chapter covers

* What makes Go an efficient, scalable, and productive language
* Exploring why Go is simple to learn but hard to master
* Presenting the common types of mistakes made by developers

Making mistakes is part of everyone’s life. As Albert Einstein once said,

!!! quote "Albert Einstein"

    A person who never made a mistake never tried anything new.

What matters in the end isn’t the number of mistakes we make, but our capacity to learn from them. This assertion also applies to programming. The seniority we acquire in a language isn’t a magical process; it involves making many mistakes and learning from them. The purpose of this book is centered around this idea. It will help you, the reader, become a more proficient Go developer by looking at and learning from 100 common mistakes people make in many areas of the language.

This chapter presents a quick refresher as to why Go has become mainstream over the years. We’ll discuss why, despite Go being considered simple to learn, mastering its nuances can be challenging. Finally, we’ll introduce the concepts this book covers.

## Go outline

If you are reading this book, it’s likely that you’re already sold on Go. Therefore, this section provides a brief reminder about what makes Go such a powerful language.

Software engineering has evolved considerably during the past decades. Most modern systems are no longer written by a single person but by teams consisting of multiple programmers—sometimes even hundreds, if not thousands. Nowadays, code must be readable, expressive, and maintainable to guarantee a system’s durability over the years. Meanwhile, in our fast-moving world, maximizing agility and reducing the time to market is critical for most organizations. Programming should also follow this trend, and companies strive to ensure that software engineers are as productive as possible when reading, writing, and maintaining code.

In response to these challenges, Google created the Go programming language in 2007. Since then, many organizations have adopted the language to support various use cases: APIs, automation, databases, CLIs (command-line interfaces), and so on. Many today consider Go the language of the cloud.

Feature-wise, Go has no type inheritance, no exceptions, no macros, no partial functions, no support for lazy variable evaluation or immutability, no operator overloading, no pattern matching, and on and on. Why are these features missing from the language? The official [Go FAQ](https://go.dev/doc/faq) gives us some insight:

!!! quote "Go FAQ"

    Why does Go not have feature X? Your favorite feature may be missing because it doesn’t fit, because it affects compilation speed or clarity of design, or because it would make the fundamental system model too difficult.

Judging the quality of a programming language via its number of features is probably not an accurate metric. At least, it’s not an objective of Go. Instead, Go utilizes a few essential characteristics when adopting a language at scale for an organization. These include the following:

* _Stability_—Even though Go receives frequent updates (including improvements and security patches), it remains a stable language. Some may even consider this one of the best features of the language.
* _Expressivity_—We can define expressivity in a programming language by how naturally and intuitively we can write and read code. A reduced number of keywords and limited ways to solve common problems make Go an expressive language for large codebases.
* _Compilation_—As developers, what can be more exasperating than having to wait for a build to test our application? Targeting fast compilation times has always been a conscious goal for the language designers. This, in turn, enables productivity.
* _Safety_—Go is a strong, statically typed language. Hence, it has strict compiletime rules, which ensure the code is type-safe in most cases.

Go was built from the ground up with solid features such as outstanding concurrency primitives with goroutines and channels. There’s not a strong need to rely on external libraries to build efficient concurrent applications. Observing how important concurrency is these days also demonstrates why Go is such a suitable language for the present and probably for the foreseeable future.

Some also consider Go a simple language. And, in a sense, this isn’t necessarily wrong. For example, a newcomer can learn the language’s main features in less than a day. So why read a book centered on the concept of mistakes if Go is simple?

## Simple doesn’t mean easy

There is a subtle difference between simple and easy. _Simple_, applied to a technology, means not complicated to learn or understand. However, _easy_ means that we can achieve anything without much effort. Go is simple to learn but not necessarily easy to master.

Let’s take concurrency, for example. In 2019, a study focusing on concurrency bugs was published: [Understanding Real-World Concurrency Bugs in Go](https://songlh.github.io/paper/go-study.pdf). This study was the first systematic analysis of concurrency bugs. It focused on multiple popular Go repositories such as Docker, gRPC, and Kubernetes. One of the most important takeaways from this study is that most of the blocking bugs are caused by inaccurate use of the message-passing paradigm via channels, despite the belief that message passing is easier to handle and less error-prone than sharing memory.

What should be an appropriate reaction to such a takeaway? Should we consider that the language designers were wrong about message passing? Should we reconsider how we deal with concurrency in our project? Of course not.

It’s not a question of confronting message passing versus sharing memory and determining the winner. However, it’s up to us as Go developers to thoroughly understand how to use concurrency, its implications on modern processors, when to favor one approach over the other, and how to avoid common traps. This example highlights that although a concept such as channels and goroutines can be simple to learn, it isn’t an easy topic in practice.

This leitmotif—simple doesn’t mean easy—can be generalized to many aspects of Go, not only concurrency. Hence, to be proficient Go developers, we must have a thorough understanding of many aspects of the language, which requires time, effort, and mistakes.

This book aims to help accelerate our journey toward proficiency by delving into 100 Go mistakes.

## 100 Go mistakes

Why should we read a book about common Go mistakes? Why not deepen our knowledge with an ordinary book that would dig into different topics?

In a 2011 article, neuroscientists proved that the best time for brain growth is when we’re facing mistakes. [^1] Haven’t we all experienced the process of learning from a mistake and recalling that occasion after months or even years, when some context related to it? As presented in another article, by Janet Metcalfe, this happens because mistakes have a facilitative effect. [^2] The main idea is that we can remember not only the error but also the context surrounding the mistake. This is one of the reasons why learning from mistakes is so efficient.

To strengthen this facilitative effect, this book accompanies each mistake as much as possible with real-world examples. This book isn’t only about theory; it also helps us get better at avoiding mistakes and making more well-informed, conscious decisions because we now understand the rationale behind them.

!!! quote "Unknown"

    Tell me and I forget. Teach me and I remember. Involve me and I learn.

This book presents seven main categories of mistakes. Overall, the mistakes can be classified as

* Bugs
* Needless complexity
* Weaker readability
* Suboptimal or unidiomatic organization 
* Lack of API convenience
* Under-optimized code
* Lack of productivity

We introduce each mistake category next.

### Bugs

The first type of mistake and probably the most obvious is software bugs. In 2020, a study conducted by Synopsys estimated the cost of software bugs in the U.S. alone to be over $2 trillion. [^3]

Furthermore, bugs can also lead to tragic impacts. We can, for example, mention cases such as Therac-25, a radiation therapy machine produced by Atomic Energy of Canada Limited (AECL). Because of a race condition, the machine gave its patients radiation doses that were hundreds of times greater than expected, leading to the death of three patients. Hence, software bugs aren’t only about money. As developers, we should remember how impactful our jobs are.

This book covers plenty of cases that could lead to various software bugs, including data races, leaks, logic errors, and other defects. Although accurate tests should be a way to discover such bugs as early as possible, we may sometimes miss cases because of different factors such as time constraints or complexity. Therefore, as a Go developer, it’s essential to make sure we avoid common bugs.

### Needless complexity

The next category of mistakes is related to unnecessary complexity. A significant part of software complexity comes from the fact that, as developers, we strive to think about imaginary futures. Instead of solving concrete problems right now, it can be tempting to build evolutionary software that could tackle whatever future use case arises. However, this leads to more drawbacks than benefits in most cases because it can make a codebase more complex to understand and reason about.

Getting back to Go, we can think of plenty of use cases where developers might be tempted to design abstractions for future needs, such as interfaces or generics. This book discusses topics where we should remain careful not to harm a codebase with needless complexity.

### Weaker readability

Another kind of mistake is to weaken readability. As Robert C. Martin wrote in his book _Clean Code: A Handbook of Agile Software Craftsmanship_, the ratio of time spent reading versus writing is well over 10 to 1. Most of us started to program on solo projects where readability wasn’t that important. However, today’s software engineering is programming with a time dimension: making sure we can still work with and maintain an application months, years, or perhaps even decades later.

When programming in Go, we can make many mistakes that can harm readability. These mistakes may include nested code, data type representations, or not using named result parameters in some cases. Throughout this book, we will learn how to write readable code and care for future readers (including our future selves).

### Suboptimal or unidiomatic organization

Be it while working on a new project or because we acquire inaccurate reflexes, another type of mistake is organizing our code and a project suboptimally and unidiomatically. Such issues can make a project harder to reason about and maintain. This book covers some of these common mistakes in Go. For example, we’ll look at how to structure a project and deal with utility packages or init functions. All in all, looking at these mistakes should help us organize our code and projects more efficiently and idiomatically.

### Lack of API convenience

Making common mistakes that weaken how convenient an API is for our clients is another type of mistake. If an API isn’t user-friendly, it will be less expressive and, hence, harder to understand and more error-prone.

We can think about many situations such as overusing any types, using the wrong creational pattern to deal with options, or blindly applying standard practices from object-oriented programming that affect the usability of our APIs. This book covers common mistakes that prevent us from exposing convenient APIs for our users.

### Under-optimized code

Under-optimized code is another type of mistake made by developers. It can happen for various reasons, such as not understanding language features or even a lack of fundamental knowledge. Performance is one of the most obvious impacts of this mistake, but not the only one.

We can think about optimizing code for other goals, such as accuracy. For example, this book provides some common techniques to ensure that floating-point operations are accurate. Meanwhile, we will cover plenty of cases that can negatively impact performance code because of poorly parallelized executions, not knowing how to reduce allocations, or the impacts of data alignment, for example. We will tackle optimization via different prisms.

### Lack of productivity

In most cases, what’s the best language we can choose when working on a new project? The one we’re the most productive with. Being comfortable with how a language works and exploiting it to get the best out of it is crucial to reach proficiency.

In this book, we will cover many cases and concrete examples that will help us to be more productive while working in Go. For instance, we’ll look at writing efficient tests to ensure that our code works, relying on the standard library to be more effective, and getting the best out of the profiling tools and linters. Now, it’s time to delve into those 100 common Go mistakes.

## Summary

* Go is a modern programming language that enables developer productivity, which is crucial for most companies today.
* Go is simple to learn but not easy to master. This is why we need to deepen our knowledge to make the most effective use of the language.
* Learning via mistakes and concrete examples is a powerful way to be proficient in a language. This book will accelerate our path to proficiency by exploring 100 common mistakes.

[^1]: J. S. Moser, H. S. Schroder, et al., “Mind Your Errors: Evidence for a Neural Mechanism Linking Growth Mindset to Adaptive Posterror Adjustments,” Psychological Science, vol. 22, no. 12, pp. 1484–1489, Dec. 2011. 
[^2]: J. Metcalfe, “Learning from Errors,” Annual Review of Psychology, vol. 68, pp. 465–489, Jan. 2017.
[^3]: Synopsys, “The Cost of Poor Software Quality in the US: A 2020 Report.” 2020. [https://news.synopsys.com/2021-01-06-Synopsys-Sponsored-CISQ-Research-Estimates-Cost-of-Poor-Software-Quality-in-the-US-2-08-Trillion-in-2020](https://news.synopsys.com/2021-01-06-Synopsys-Sponsored-CISQ-Research-Estimates-Cost-of-Poor-Software-Quality-in-the-US-2-08-Trillion-in-2020). 


================================================
FILE: docs/external.md
================================================
# External Resources

## English

### The Best Golang Book | Prime Reacts

<iframe width="560" height="315" src="https://www.youtube.com/embed/a-lYYYr-5a8?si=hWm7um5GS19KVbog" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

### Book Review: 100 Go Mistakes (And How to Avoid Them)

<iframe width="560" height="315" src="https://www.youtube.com/embed/tcRYU9g5wtw?si=2s10hXwxL7ButfRm" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

[Post](https://boldlygo.tech/posts/2023-08-09-review-100-go-mistakes/)

### The Most Useful Book for a Go Programmer?

<iframe width="560" height="315" src="https://www.youtube.com/embed/8pqgv4_Yjq0?si=CunG1j2Uh2isXm4b" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

### How to make mistakes in Go - Go Time #190

<iframe width="560" height="315" src="https://www.youtube.com/embed/VGOgDqDe30E?si=HErewJsxDjie92AU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

* [Episode](https://changelog.com/gotime/190)
* [Spotify](https://open.spotify.com/episode/0K1DImrxHCy6E7zVY4AxMZ?si=akroInsPQ1mM5B5V2tHLUw&dl_branch=1)

### Go is AMAZING

<iframe width="560" height="315" src="https://www.youtube.com/embed/iQBdVqnB0Ss?si=6lX1-oj28s_OZPwp&amp;start=281" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

### 8LU - 100% Test Coverage

<iframe width="560" height="315" src="https://www.youtube.com/embed/V3FBDav6wgQ?si=iA58zxZHQewKF6Jz&amp;start=1210" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

### Some Tips I learned from 100 Mistakes in Go

[Post](https://raygervais.dev/articles/2023/04/100_mistakes_in_go/)

### What can be summarized from 100 Go Mistakes?

[Post](https://www.sobyte.net/post/2023-05/summarized-from-100-go-mistakes/)

### Book review: 100 Go Mistakes and How to Avoid Them

[Post](https://schfkt.dev/blog/book-10-go-mistakes/)

## Chinese

### 深度阅读之《100 Go Mistakes and How to Avoid Them

[Post](https://qcrao.com/post/100-go-mistakes-reading-notes/)

### 100 Go Mistakes 随记

[Post](https://zhuanlan.zhihu.com/p/592602656)

### 我为什么放弃Go语言?

[Post](https://juejin.cn/post/7241452578125824061)

## Japanese

### 最近読んだGo言語の本の紹介:100 Go Mistakes and How to Avoid Them

[Post](https://qiita.com/kentaro_suzuki/items/c9c31dc81217f237433c)

### 『100 Go Mistakes and How to Avoid Them』を読む

[Post](https://zenn.dev/yukibobier/books/066f07c8a59fa0)

## Portuguese

### Um ÓTIMO livro para programadores Go

<iframe width="560" height="315" src="https://www.youtube.com/embed/34XShL_jWD4?si=WQZR3QexpwEZ9-EU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>


================================================
FILE: docs/index.md
================================================
---
comments: true
description: Summary of the mistakes in the 100 Go Mistakes book.
status: new
---

# Common Go Mistakes

???+ tip "The Coder Cafe"

    If you enjoyed my book, you may be interested in my latest project: [The Coder Cafe](https://thecoder.cafe?rd=100go.co), a newsletter for coders.

    > Feeling overwhelmed by the endless stream of tech content? At The Coder Cafe, we serve timeless concepts with your coffee. Written by a Google SWE and published author, we help you grow as an engineer, one coffee at a time.

    <center><a href="https://thecoder.cafe?rd=100go.co"><img src="../img/thecodercafe.png" alt="" style="width:480px;height:240px;"></a></center>

This page is a summary of the mistakes in the [100 Go Mistakes and How to Avoid Them book](book.md). Meanwhile, it's also open to the community. If you believe that a common Go mistake should be added, please create an [issue](https://github.com/teivah/100-go-mistakes/issues/new?assignees=&labels=community+mistake&template=community_mistake.md&title=).

![](img/inside-cover.png)

???+ warning "Beta"

    You're viewing a beta version enriched with significantly more content. However, this version is not yet complete, and I'm looking for volunteers to help me summarize the remaining mistakes ([GitHub issue #43](https://github.com/teivah/100-go-mistakes/issues/43)).

    Progress:
    <progress value="81" max="100"/>

## Code and Project Organization

### Unintended variable shadowing (#1)

???+ info "TL;DR"

    Avoiding shadowed variables can help prevent mistakes like referencing the wrong variable or confusing readers.

Variable shadowing occurs when a variable name is redeclared in an inner block, but this practice is prone to mistakes. Imposing a rule to forbid shadowed variables depends on personal taste. For example, sometimes it can be convenient to reuse an existing variable name like `err` for errors. Yet, in general, we should remain cautious because we now know that we can face a scenario where the code compiles, but the variable that receives the value is not the one expected.

[:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/1-variable-shadowing/main.go)

### Unnecessary nested code (#2)

???+ info "TL;DR"

    Avoiding nested levels and keeping the happy path aligned on the left makes building a mental code model easier.

In general, the more nested levels a function requires, the more complex it is to read and understand. Let’s see some different applications of this rule to optimize our code for readability:

* When an `if` block returns, we should omit the `else` block in all cases. For example, we shouldn’t write:

```go
if foo() {
    // ...
    return true
} else {
    // ...
}
```

Instead, we omit the `else` block like this:

```go
if foo() {
    // ...
    return true
}
// ...
```

* We can also follow this logic with a non-happy path:

```go
if s != "" {
    // ...
} else {
    return errors.New("empty string")
}
```

  Here, an empty `s` represents the non-happy path. Hence, we should flip the
  condition like so:

```go
if s == "" {
    return errors.New("empty string")
}
// ...
```

Writing readable code is an important challenge for every developer. Striving to reduce the number of nested blocks, aligning the happy path on the left, and returning as early as possible are concrete means to improve our code’s readability.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/2-nested-code/main.go)

### Misusing init functions (#3)

???+ info "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.

An init function is a function used to initialize the state of an application. It takes no arguments and returns no result (a `func()` function). When a package is initialized, all the constant and variable declarations in the package are evaluated. Then, the init functions are executed.

Init functions can lead to some issues:

* They can limit error management.
* They can complicate how to implement tests (for example, an external dependency must be set up, which may not be necessary for the scope of unit tests).
* If the initialization requires us to set a state, that has to be done through global variables.

We should be cautious with init functions. They can be helpful in some situations, however, such as defining static configuration. Otherwise, and in most cases, we should handle initializations through ad hoc functions.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/3-init-functions/)

### Overusing getters and setters (#4)

???+ info "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.

Data encapsulation refers to hiding the values or state of an object. Getters and setters are means to enable encapsulation by providing exported methods on top of unexported object fields.

In Go, there is no automatic support for getters and setters as we see in some languages. It is also considered neither mandatory nor idiomatic to use getters and setters to access struct fields. We shouldn’t overwhelm our code with getters and setters on structs if they don’t bring any value. We should be pragmatic and strive to find the right balance between efficiency and following idioms that are sometimes considered indisputable in other programming paradigms.

Remember that Go is a unique language designed for many characteristics, including simplicity. However, if we find a need for getters and setters or, as mentioned, foresee a future need while guaranteeing forward compatibility, there’s nothing wrong with using them.

### Interface pollution (#5)

???+ info "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.

Read the full section [here](5-interface-pollution.md).

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/5-interface-pollution/)

### Interface on the producer side (#6)

???+ info "TL;DR"

    Keeping interfaces on the client side avoids unnecessary abstractions.

Interfaces are satisfied implicitly in Go, which tends to be a gamechanger compared to languages with an explicit implementation. In most cases, the approach to follow is similar to what we described in the previous section: _abstractions should be discovered, not created_. This means that it’s not up to the producer to force a given abstraction for all the clients. Instead, it’s up to the client to decide whether it needs some form of abstraction and then determine the best abstraction level for its needs.

An interface should live on the consumer side in most cases. However, in particular contexts (for example, when we know—not foresee—that an abstraction will be helpful for consumers), we may want to have it on the producer side. If we do, we should strive to keep it as minimal as possible, increasing its reusability potential and making it more easily composable.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/6-interface-producer/)

### Returning interfaces (#7)

???+ info "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.

In most cases, we shouldn’t return interfaces but concrete implementations. Otherwise, it can make our design more complex due to package dependencies and can restrict flexibility because all the clients would have to rely on the same abstraction. Again, the conclusion is similar to the previous sections: if we know (not foresee) that an abstraction will be helpful for clients, we can consider returning an interface. Otherwise, we shouldn’t force abstractions; they should be discovered by clients. If a client needs to abstract an implementation for whatever reason, it can still do that on the client’s side.

### `any` says nothing (#8)

???+ info "TL;DR"

    Only use `any` if you need to accept or return any possible type, such as `json.Marshal`. Otherwise, `any` doesn’t provide meaningful information and can lead to compile-time issues by allowing a caller to call methods with any data type.

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.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/8-any/main.go)

### Being confused about when to use generics (#9)

???+ info "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.

Read the full section [here](9-generics.md).

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/9-generics/main.go)

### Not being aware of the possible problems with type embedding (#10)

???+ info "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.

When creating a struct, Go offers the option to embed types. But this can sometimes lead to unexpected behaviors if we don’t understand all the implications of type embedding. Throughout this section, we look at how to embed types, what these bring, and the possible issues.

In Go, a struct field is called embedded if it’s declared without a name. For example,

```go
type Foo struct {
    Bar // Embedded field
}

type Bar struct {
    Baz int
}
```

In the `Foo` struct, the `Bar` type is declared without an associated name; hence, it’s an embedded field.

We use embedding to promote the fields and methods of an embedded type. Because `Bar` contains a `Baz` field, this field is
promoted to `Foo`. Therefore, `Baz` becomes available from `Foo`.

What can we say about type embedding? First, let’s note that it’s rarely a necessity, and it means that whatever the use case, we can probably solve it as well without type embedding. Type embedding is mainly used for convenience: in most cases, to promote behaviors.

If we decide to use type embedding, we need to keep two main constraints in mind:

* It shouldn’t be used solely as some syntactic sugar to simplify accessing a field (such as `Foo.Baz()` instead of `Foo.Bar.Baz()`). If this is the only rationale, let’s not embed the inner type and use a field instead.
* It shouldn’t promote data (fields) or a behavior (methods) we want to hide from the outside: for example, if it allows clients to access a locking behavior that should remain private to the struct.

Using type embedding consciously by keeping these constraints in mind can help avoid boilerplate code with additional forwarding methods. However, let’s make sure we don’t do it solely for cosmetics and not promote elements that should remain hidden.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/10-type-embedding/main.go)

### Not using the functional options pattern (#11)

???+ info "TL;DR"

    To handle options conveniently and in an API-friendly manner, use the functional options pattern.

Although there are different implementations with minor variations, the main idea is as follows:

* An unexported struct holds the configuration: options.
* Each option is a function that returns the same type: `type Option func(options *options) error`. For example, `WithPort` accepts an `int` argument that represents the port and returns an `Option` type that represents how to update the `options` struct.

![](img/options.png)

```go
type options struct {
  port *int
}

type Option func(options *options) error

func WithPort(port int) Option {
  return func(options *options) error {
    if port < 0 {
      return errors.New("port should be positive")
    }
    options.port = &port
    return nil
  }
}

func NewServer(addr string, opts ...Option) ( *http.Server, error) {
  var options options
  for _, opt := range opts {
    err := opt(&options)
    if err != nil {
      return nil, err
    }
  }

  // At this stage, the options struct is built and contains the config
  // Therefore, we can implement our logic related to port configuration
  var port int
  if options.port == nil {
    port = defaultHTTPPort
  } else {
    if *options.port == 0 {
      port = randomPort()
    } else {
      port = *options.port
    }
  }

  // ...
}
```

The functional options pattern provides a handy and API-friendly way to handle options. Although the builder pattern can be a valid option, it has some minor downsides (having to pass a config struct that can be empty or a less handy way to handle error management) that tend to make the functional options pattern the idiomatic way to deal with these kind of problems in Go.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/11-functional-options/)

### Project misorganization (project structure and package organization) (#12)

Regarding the overall organization, there are different schools of thought. For example, should we organize our application by context or by layer? It depends on our preferences. We may favor grouping code per context (such as the customer context, the contract context, etc.), or we may favor following hexagonal architecture principles and group per technical layer. If the decision we make fits our use case, it cannot be a wrong decision, as long as we remain consistent with it.

Regarding packages, there are multiple best practices that we should follow. First, we should avoid premature packaging because it might cause us to overcomplicate a project. Sometimes, it’s better to use a simple organization and have our project evolve when we understand what it contains rather than forcing ourselves to make the perfect structure up front.
Granularity is another essential thing to consider. We should avoid having dozens of nano packages containing only one or two files. If we do, it’s because we have probably missed some logical connections across these packages, making our project harder for readers to understand. Conversely, we should also avoid huge packages that dilute the meaning of a package name.

Package naming should also be considered with care. As we all know (as developers), naming is hard. To help clients understand a Go project, we should name our packages after what they provide, not what they contain. Also, naming should be meaningful. Therefore, a package name should be short, concise, expressive, and, by convention, a single lowercase word.

Regarding what to export, the rule is pretty straightforward. We should minimize what should be exported as much as possible to reduce the coupling between packages and keep unnecessary exported elements hidden. If we are unsure whether to export an element or not, we should default to not exporting it. Later, if we discover that we need to export it, we can adjust our code. Let’s also keep in mind some exceptions, such as making fields exported so that a struct can be unmarshaled with encoding/json.

Organizing a project isn’t straightforward, but following these rules should help make it easier to maintain. However, remember that consistency is also vital to ease maintainability. Therefore, let’s make sure that we keep things as consistent as possible within a codebase.

???+ note

    In 2023, the Go team has published an official guideline for organizing / structuring a Go project: [go.dev/doc/modules/layout](https://go.dev/doc/modules/layout)

### Creating utility packages (#13)

???+ info "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.

Also, bear in mind that naming a package after what it provides and not what it contains can be an efficient way to increase its expressiveness.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/13-utility-packages/stringset.go)

### Ignoring package name collisions (#14)

???+ info "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.

Package collisions occur when a variable name collides with an existing package name, preventing the package from being reused. We should prevent variable name collisions to avoid ambiguity. If we face a collision, we should either find another meaningful name or use an import alias.

### Missing code documentation (#15)

???+ info "TL;DR"

    To help clients and maintainers understand your code’s purpose, document exported elements.

Documentation is an important aspect of coding. It simplifies how clients can consume an API but can also help in maintaining a project. In Go, we should follow some rules to make our code idiomatic:

First, every exported element must be documented. Whether it is a structure, an interface, a function, or something else, if it’s exported, it must be documented. The convention is to add comments, starting with the name of the exported element.

As a convention, each comment should be a complete sentence that ends with punctuation. Also bear in mind that when we document a function (or a method), we should highlight what the function intends to do, not how it does it; this belongs to the core of a function and comments, not documentation. Furthermore, the documentation should ideally provide enough information that the consumer does not have to look at our code to understand how to use an exported element.

When it comes to documenting a variable or a constant, we might be interested in conveying two aspects: its purpose and its content. The former should live as code documentation to be useful for external clients. The latter, though, shouldn’t necessarily be public.

To help clients and maintainers understand a package’s scope, we should also document each package. The convention is to start the comment with `// Package` followed by the package name. The first line of a package comment should be concise. That’s because it will appear in the package. Then, we can provide all the information we need in the following lines.

Documenting our code shouldn’t be a constraint. We should take the opportunity to make sure it helps clients and maintainers to understand the purpose of our code.

### Not using linters (#16)

???+ info "TL;DR"

    To improve code quality and consistency, use linters and formatters.

A linter is an automatic tool to analyze code and catch errors. The scope of this section isn’t to give an exhaustive list of the existing linters; otherwise, it will become deprecated pretty quickly. But we should understand and remember why linters are essential for most Go projects.

However, if you’re not a regular user of linters, here is a list that you may want to use daily:

* [https://golang.org/cmd/vet](https://golang.org/cmd/vet)—A standard Go analyzer
* [https://github.com/kisielk/errcheck](https://github.com/kisielk/errcheck)—An error checker
* [https://github.com/fzipp/gocyclo](https://github.com/fzipp/gocyclo)—A cyclomatic complexity analyzer
* [https://github.com/jgautheron/goconst](https://github.com/jgautheron/goconst)—A repeated string constants analyzer


Besides linters, we should also use code formatters to fix code style. Here is a list of some code formatters for you to try:

* [https://golang.org/cmd/gofmt](https://golang.org/cmd/gofmt)—A standard Go code formatter
* [https://godoc.org/golang.org/x/tools/cmd/goimports](https://godoc.org/golang.org/x/tools/cmd/goimports)—A standard Go imports formatter


Meanwhile, we should also look at golangci-lint ([https://github.com/golangci/golangci-lint](https://github.com/golangci/golangci-lint)). It’s a linting tool that provides a facade on top of many useful linters and formatters. Also, it allows running the linters in parallel to improve analysis speed, which is quite handy.

Linters and formatters are a powerful way to improve the quality and consistency of our codebase. Let’s take the time to understand which one we should use and make sure we automate their execution (such as a CI or Git precommit hook).

## Data Types

### Creating confusion with octal literals (#17)

???+ info "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`.

Octal numbers start with a 0 (e.g., `010` is equal to 8 in base 10). To improve readability and avoid potential mistakes for future code readers, we should make octal numbers explicit using the `0o` prefix (e.g., `0o10`).

We should also note the other integer literal representations:

* _Binary_—Uses a `0b` or `0B` prefix (for example, `0b100` is equal to 4 in base 10)
* _Hexadecimal_—Uses an `0x` or `0X` prefix (for example, `0xF` is equal to 15 in base 10)
* _Imaginary_—Uses an `i` suffix (for example, `3i`)

We can also use an underscore character (_) as a separator for readability. For example, we can write 1 billion this way: `1_000_000_000`. We can also use the underscore character with other representations (for example, `0b00_00_01`).

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/17-octal-literals/main.go)

### Neglecting integer overflows (#18)

???+ info "TL;DR"

    Because integer overflows and underflows are handled silently in Go, you can implement your own functions to catch them.

In Go, an integer overflow that can be detected at compile time generates a compilation error. For example,

```go
var counter int32 = math.MaxInt32 + 1
```

```shell
constant 2147483648 overflows int32
```

However, at run time, an integer overflow or underflow is silent; this does not lead to an application panic. It is essential to keep this behavior in mind, because it can lead to sneaky bugs (for example, an integer increment or addition of positive integers that leads to a negative result).

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/18-integer-overflows)

### Not understanding floating-points (#19)

???+ info "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.

In Go, there are two floating-point types (if we omit imaginary numbers): float32 and float64. The concept of a floating point was invented to solve the major problem with integers: their inability to represent fractional values. To avoid bad surprises, we need to know that floating-point arithmetic is an approximation of real arithmetic.

For that, we’ll look at a multiplication example:

```go
var n float32 = 1.0001
fmt.Println(n * n)
```

We may expect this code to print the result of 1.0001 * 1.0001 = 1.00020001, right? However, running it on most x86 processors prints 1.0002, instead.

Because Go’s `float32` and `float64` types are approximations, we have to bear a few rules in mind:

* When comparing two floating-point numbers, check that their difference is within an acceptable range.
* When performing additions or subtractions, group operations with a similar order of magnitude for better accuracy.
* To favor accuracy, if a sequence of operations requires addition, subtraction, multiplication, or division, perform the multiplication and division operations first.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/19-floating-points/main.go)

### Not understanding slice length and capacity (#20)

???+ info "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.

Read the full section [here](20-slice.md).

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/20-slice-length-cap/main.go)

### Inefficient slice initialization (#21)

???+ info "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.

While initializing a slice using `make`, we can provide a length and an optional capacity. Forgetting to pass an appropriate value for both of these parameters when it makes sense is a widespread mistake. Indeed, it can lead to multiple copies and additional effort for the GC to clean the temporary backing arrays. Performance-wise, there’s no good reason not to give the Go runtime a helping hand.

Our options are to allocate a slice with either a given capacity or a given length. Of these two solutions, we have seen that the second tends to be slightly faster. But using a given capacity and append can be easier to implement and read in some contexts.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/21-slice-init/main.go)

### Being confused about nil vs. empty slice (#22)

???+ info "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.

In Go, there is a distinction between nil and empty slices. A nil slice is equals to `nil`, whereas an empty slice has a length of zero. A nil slice is empty, but an empty slice isn’t necessarily `nil`. Meanwhile, a nil slice doesn’t require any allocation. We have seen throughout this section how to initialize a slice depending on the context by using

* `var s []string` if we aren’t sure about the final length and the slice can be empty
* `[]string(nil)` as syntactic sugar to create a nil and empty slice
* `make([]string, length)` if the future length is known

The last option, `[]string{}`, should be avoided if we initialize the slice without elements. Finally, let’s check whether the libraries we use make the distinctions between nil and empty slices to prevent unexpected behaviors.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/22-nil-empty-slice/)

### Not properly checking if a slice is empty (#23)

???+ info "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.

To determine whether a slice has elements, we can either do it by checking if the slice is nil or if its length is equal to 0. Checking the length is the best option to follow as it will cover both if the slice is empty or if the slice is nil.

Meanwhile, when designing interfaces, we should avoid distinguishing nil and empty slices, which leads to subtle programming errors. When returning slices, it should make neither a semantic nor a technical difference if we return a nil or empty slice. Both should mean the same thing for the callers. This principle is the same with maps. To check if a map is empty, check its length, not whether it’s nil.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/23-checking-slice-empty/main.go)

### Not making slice copies correctly (#24)

???+ info "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.

Copying elements from one slice to another is a reasonably frequent operation. When using copy, we must recall that the number of elements copied to the destination corresponds to the minimum between the two slices’ lengths. Also bear in mind that other alternatives exist to copy a slice, so we shouldn’t be surprised if we find them in a codebase.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/24-slice-copy/main.go)

### Unexpected side effects using slice append (#25)

???+ info "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.

???+ 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`.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/25-slice-append/main.go)

### Slices and memory leaks (#26)

???+ info "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.

#### Leaking capacity

Remember that slicing a large slice or array can lead to potential high memory consumption. The remaining space won’t be reclaimed by the GC, and we can keep a large backing array despite using only a few elements. Using a slice copy is the solution to prevent such a case.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/26-slice-memory-leak/capacity-leak)

#### Slice and pointers

When we use the slicing operation with pointers or structs with pointer fields, we need to know that the GC won’t reclaim these elements. In that case, the two options are to either perform a copy or explicitly mark the remaining elements or their fields to `nil`.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/26-slice-memory-leak/slice-pointers)

### Inefficient map initialization (#27)

???+ info "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.

A map provides an unordered collection of key-value pairs in which all the keys are distinct. In Go, a map is based on the hash table data structure. Internally, a hash table is an array of buckets, and each bucket is a pointer to an array of key-value pairs.

If we know up front the number of elements a map will contain, we should create it by providing an initial size. Doing this avoids potential map growth, which is quite heavy computation-wise because it requires reallocating enough space and rebalancing all the elements.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/27-map-init/main_test.go)

### Maps and memory leaks (#28)

???+ info "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.

Read the full section [here](28-maps-memory-leaks.md).

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/28-map-memory-leak/main.go)

### Comparing values incorrectly (#29)

???+ info "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.

It’s essential to understand how to use `==` and `!=` to make comparisons effectively. We can use these operators on operands that are comparable:

* _Booleans_—Compare whether two Booleans are equal.
* _Numerics (int, float, and complex types)_—Compare whether two numerics are equal.
* _Strings_—Compare whether two strings are equal.
* _Channels_—Compare whether two channels were created by the same call to make or if both are nil.
* _Interfaces_—Compare whether two interfaces have identical dynamic types and equal dynamic values or if both are nil.
* _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.

???+ note

    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.

If performance is crucial at run time, implementing our custom method might be the best solution.
One additional note: we must remember that the standard library has some existing comparison methods. For example, we can use the optimized `bytes.Compare` function to compare two slices of bytes. Before implementing a custom method, we need to make sure we don’t reinvent the wheel.


 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/29-comparing-values/main.go)

## Control Structures

### Ignoring that elements are copied in `range` loops (#30)

???+ info "TL;DR"

    The value element in a `range` loop is a copy. Therefore, to mutate a struct, for example, access it via its index or via a classic `for` loop (unless the element or the field you want to modify is a pointer).

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.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/30-range-loop-element-copied/)

### Ignoring how arguments are evaluated in `range` loops (channels and arrays) (#31)

???+ info "TL;DR"

    Understanding that the expression passed to the `range` operator is evaluated only once before the beginning of the loop can help you avoid common mistakes such as inefficient assignment in channel or slice iteration.

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:

```go
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.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/31-range-loop-arg-evaluation/)

### :warning: Ignoring the impacts of using pointer elements in `range` loops (#32)

???+ warning

    This mistake isn't relevant anymore from Go 1.22 ([details](https://go.dev/blog/loopvar-preview)).

### Making wrong assumptions during map iterations (ordering and map insert during iteration) (#33)

???+ info "TL;DR"

    To ensure predictable outputs when using maps, remember that a map data structure:

* Doesn’t order the data by keys
* Doesn’t preserve the insertion order
* Doesn’t have a deterministic iteration order
* Doesn’t guarantee that an element added during an iteration will be produced during this iteration

<!-- TODO -->

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/33-map-iteration/main.go)

### Ignoring how the `break` statement works (#34)

???+ info "TL;DR"

    Using `break` or `continue` with a label enforces breaking a specific statement. This can be helpful with `switch` or `select` statements inside loops.

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:

```go
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. In the previous example, it terminates the `switch` statement.

To break the loop instead of the `switch` statement, the most idiomatic way is to use a label:

```go hl_lines="1 8"
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.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/34-break/main.go)

### Using `defer` inside a loop (#35)

???+ info "TL;DR"

    Extracting loop logic inside a function leads to executing a `defer` statement at the end of each iteration.

The `defer` statement delays a call’s execution until the surrounding function returns. It’s mainly used to reduce boilerplate code. For example, if a resource has to be closed eventually, we can use `defer` to avoid repeating the closure calls before every single `return`.

One common mistake with `defer` is to forget that it schedules a function call when the _surrounding_ function returns. For example:

```go
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:

```go
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.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/35-defer-loop/main.go)

## Strings

### Not understanding the concept of rune (#36)

???+ info "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.

As runes are everywhere in Go, it's important to understand the following:

* A charset is a set of characters, whereas an encoding describes how to translate a charset into binary.
* In Go, a string references an immutable slice of arbitrary bytes.
* Go source code is encoded using UTF-8. Hence, all string literals are UTF-8 strings. But because a string can contain arbitrary bytes, if it’s obtained from somewhere else (not the source code), it isn’t guaranteed to be based on the UTF-8 encoding.
* A `rune` corresponds to the concept of a Unicode code point, meaning an item represented by a single value.
* Using UTF-8, a Unicode code point can be encoded into 1 to 4 bytes.
* Using `len()` on a string in Go returns the number of bytes, not the number of runes.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/36-rune/main.go)

### Inaccurate string iteration (#37)

???+ info "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`.

Iterating on a string is a common operation for developers. Perhaps we want to perform an operation for each rune in the string or implement a custom function to search for a specific substring. In both cases, we have to iterate on the different runes of a string. But it’s easy to get confused about how iteration works.

For example, consider the following example:

```go
s := "hêllo"
for i := range s {
    fmt.Printf("position %d: %c\n", i, s[i])
}
fmt.Printf("len=%d\n", len(s))
```

```
position 0: h
position 1: Ã
position 3: l
position 4: l
position 5: o
len=6
```

Let's highlight three points that might be confusing:

* The second rune is à in the output instead of ê.
* We jumped from position 1 to position 3: what is at position 2?
* len returns a count of 6, whereas s contains only 5 runes.

Let’s start with the last observation. We already mentioned that len returns the number of bytes in a string, not the number of runes. Because we assigned a string literal to `s`, `s` is a UTF-8 string. Meanwhile, the special character "ê" isn’t encoded in a single byte; it requires 2 bytes. Therefore, calling `len(s)` returns 6.

Meanwhile, in the previous example, we have to understand that we don't iterate over each rune; instead, we iterate over each starting index of a rune:

![](img/rune.png)

Printing `s[i]` doesn’t print the ith rune; it prints the UTF-8 representation of the byte at index `i`. Hence, we printed "hÃllo" instead of "hêllo".

If we want to print all the different runes, we can either use the value element of the `range` operator:

```go
s := "hêllo"
for i, r := range s {
    fmt.Printf("position %d: %c\n", i, r)
}
```

Or, we can convert the string into a slice of runes and iterate over it:

```go hl_lines="2"
s := "hêllo"
runes := []rune(s)
for i, r := range runes {
    fmt.Printf("position %d: %c\n", i, r)
}
```

Note that this solution introduces a run-time overhead compared to the previous one. Indeed, converting a string into a slice of runes requires allocating an additional slice and converting the bytes into runes: an O(n) time complexity with n the number of bytes in the string. Therefore, if we want to iterate over all the runes, we should use the first solution.

However, if we want to access the ith rune of a string with the first option, we don’t have access to the rune index; rather, we know the starting index of a rune in the byte sequence.

```go
s := "hêllo"
r := []rune(s)[4]
fmt.Printf("%c\n", r) // o
```

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/37-string-iteration/main.go)

### Misusing trim functions (#38)

???+ info "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.

For example:

```go
fmt.Println(strings.TrimRight("123oxo", "xo"))
```

The example prints 123:

![](img/trim.png)

Conversely, `strings.TrimLeft` removes all the leading runes contained in a set.

On the other side, `strings.TrimSuffix` / `strings.TrimPrefix` returns a string without the provided trailing suffix / prefix.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/38-trim/main.go)

### Under-optimized strings concatenation (#39)

???+ info "TL;DR"

    Concatenating a list of strings should be done with `strings.Builder` to prevent allocating a new string during each iteration.

Let’s consider a `concat` function that concatenates all the string elements of a slice using the `+=` operator:

```go
func concat(values []string) string {
    s := ""
    for _, value := range values {
        s += value
    }
    return s
}
```

During each iteration, the `+=` operator concatenates `s` with the value string. At first sight, this function may not look wrong. But with this implementation, we forget one of the core characteristics of a string: its immutability. Therefore, each iteration doesn’t update `s`; it reallocates a new string in memory, which significantly impacts the performance of this function.

Fortunately, there is a solution to deal with this problem, using `strings.Builder`:

```go hl_lines="2 4"
func concat(values []string) string {
    sb := strings.Builder{}
    for _, value := range values {
        _, _ = sb.WriteString(value)
    }
    return sb.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.

???+ 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.

Internally, `strings.Builder` holds a byte slice. Each call to `WriteString` results in a call to append on this slice. There are two impacts. First, this struct shouldn’t be used concurrently, as the calls to `append` would lead to race conditions. The second impact is something that we saw in [mistake #21, "Inefficient slice initialization"](#inefficient-slice-initialization-21): if the future length of a slice is already known, we should preallocate it. For that purpose, `strings.Builder` exposes a method `Grow(n int)` to guarantee space for another `n` bytes:

```go
func concat(values []string) string {
    total := 0
    for i := 0; i < len(values); i++ {
        total += len(values[i])
    }

    sb := strings.Builder{}
    sb.Grow(total) (2)
    for _, value := range values {
        _, _ = sb.WriteString(value)
    }
    return sb.String()
}
```

Let’s run a benchmark to compare the three versions (v1 using `+=`; v2 using `strings.Builder{}` without preallocation; and v3 using `strings.Builder{}` with preallocation). The input slice contains 1,000 strings, and each string contains 1,000 bytes:

```
BenchmarkConcatV1-4             16      72291485 ns/op
BenchmarkConcatV2-4           1188        878962 ns/op
BenchmarkConcatV3-4           5922        190340 ns/op
```

As we can see, the latest version is by far the most efficient: 99% faster than v1 and 78% faster than v2.

`strings.Builder` is the recommended solution to concatenate a list of strings. Usually, this solution should be used within a loop. Indeed, if we just have to concatenate a few strings (such as a name and a surname), using `strings.Builder` is not recommended as doing so will make the code a bit less readable than using the `+=` operator or `fmt.Sprintf`.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/39-string-concat/)

### Useless string conversions (#40)

???+ info "TL;DR"

    Remembering that the `bytes` package offers the same operations as the `strings` package can help avoid extra byte/string conversions.

When choosing to work with a string or a `[]byte`, most programmers tend to favor strings for convenience. But most I/O is actually done with `[]byte`. For example, `io.Reader`, `io.Writer`, and `io.ReadAll` work with `[]byte`, not strings.

When we’re wondering whether we should work with strings or `[]byte`, let’s recall that working with `[]byte` isn’t necessarily less convenient. Indeed, all the exported functions of the strings package also have alternatives in the `bytes` package: `Split`, `Count`, `Contains`, `Index`, and so on. Hence, whether we’re doing I/O or not, we should first check whether we could implement a whole workflow using bytes instead of strings and avoid the price of additional conversions.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/40-string-conversion/main.go)

### Substring and memory leaks (#41)

???+ info "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.

In mistake [#26, “Slices and memory leaks,”](#slice-and-memory-leaks--26-) we saw how slicing a slice or array may lead to memory leak situations. This principle also applies to string and substring operations.

We need to keep two things in mind while using the substring operation in Go. First, the interval provided is based on the number of bytes, not the number of runes. Second, a substring operation may lead to a memory leak as the resulting substring will share the same backing array as the initial string. The solutions to prevent this case from happening are to perform a string copy manually or to use `strings.Clone` from Go 1.18.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/41-substring-memory-leak/main.go)

## Functions and Methods

### Not knowing which type of receiver to use (#42)

???+ info "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.

Choosing between value and pointer receivers isn’t always straightforward. Let’s discuss some of the conditions to help us choose.

A receiver _must_ be a pointer

* If the method needs to mutate the receiver. This rule is also valid if the receiver is a slice and a method needs to append elements:

  ```go
  type slice []int

  func (s *slice) add(element int) {
      *s = append(*s, element)
  }
  ```

* If the method receiver contains a field that cannot be copied: for example, a type part of the sync package (see [#74, “Copying a sync type”](#copying-a-sync-type--74-)).

A receiver _should_ be a pointer

* If the receiver is a large object. Using a pointer can make the call more efficient, as doing so prevents making an extensive copy. When in doubt about how large is large, benchmarking can be the solution; it’s pretty much impossible to state a specific size, because it depends on many factors.

A receiver _must_ be a value

* If we have to enforce a receiver’s immutability.
* If the receiver is a map, function, or channel. Otherwise, a compilation error
  occurs.

A receiver _should_ be a value

* If the receiver is a slice that doesn’t have to be mutated.
* If the receiver is a small array or struct that is naturally a value type without mutable fields, such as `time.Time`.
* If the receiver is a basic type such as `int`, `float64`, or `string`.

Of course, it’s impossible to be exhaustive, as there will always be edge cases, but this section’s goal was to provide guidance to cover most cases. By default, we can choose to go with a value receiver unless there’s a good reason not to do so. In doubt, we should use a pointer receiver.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/42-receiver/)

### Never using named result parameters (#43)

???+ info "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.

When we return parameters in a function or a method, we can attach names to these parameters and use them as regular variables. When a result parameter is named, it’s initialized to its zero value when the function/method begins. With named result parameters, we can also call a naked return statement (without arguments). In that case, the current values of the result parameters are used as the returned values.

Here’s an example that uses a named result parameter `b`:

```go
func f(a int) (b int) {
    b = a
    return
}
```

In this example, we attach a name to the result parameter: `b`. When we call return without arguments, it returns the current value of `b`.

In some cases, named result parameters can also increase readability: for example, if two parameters have the same type. In other cases, they can also be used for convenience. Therefore, we should use named result parameters sparingly when there’s a clear benefit.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/43-named-result-parameters/main.go)

### Unintended side effects with named result parameters (#44)

???+ info "TL;DR"

    See [#43](#never-using-named-result-parameters-43).

We mentioned why named result parameters can be useful in some situations. But as these result parameters are initialized to their zero value, using them can sometimes lead to subtle bugs if we’re not careful enough. For example, can you spot what’s wrong with this code?

```go
func (l loc) getCoordinates(ctx context.Context, address string) (
    lat, lng float32, err error) {
    isValid := l.validateAddress(address) (1)
    if !isValid {
        return 0, 0, errors.New("invalid address")
    }

    if ctx.Err() != nil { (2)
        return 0, 0, err
    }

    // Get and return coordinates
}
```

The error might not be obvious at first glance. Here, the error returned in the `if ctx.Err() != nil` scope is `err`. But we haven’t assigned any value to the `err` variable. It’s still assigned to the zero value of an `error` type: `nil`. Hence, this code will always return a nil error.


When using named result parameters, we must recall that each parameter is initialized to its zero value. As we have seen in this section, this can lead to subtle bugs that aren’t always straightforward to spot while reading code. Therefore, let’s remain cautious when using named result parameters, to avoid potential side effects.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/44-side-effects-named-result-parameters/main.go)

### Returning a nil receiver (#45)

???+ info "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.

<!-- TODO -->

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/45-nil-receiver/main.go)

### Using a filename as a function input (#46)

???+ info "TL;DR"

    Designing functions to receive `io.Reader` types instead of filenames improves the reusability of a function and makes testing easier.

Accepting a filename as a function input to read from a file should, in most cases, be considered a code smell (except in specific functions such as `os.Open`). Indeed, it makes unit tests more complex because we may have to create multiple files. It also reduces the reusability of a function (although not all functions are meant to be reused). Using the `io.Reader` interface abstracts the data source. Regardless of whether the input is a file, a string, an HTTP request, or a gRPC request, the implementation can be reused and easily tested.

 [:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/46-function-input/)

### Ignoring how `defer` arguments and receivers are evaluated (argument evaluation, pointer, and value receivers) (#47)

???+ info "TL;DR"

    Passing a pointer to a `defer` function and wrapping a call inside a closure are two possible solutions to overcome the immediate evaluation of arguments and receivers.

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.

```go
const (
    StatusSuccess  = "success"
    StatusErrorFoo = "error_foo"
    StatusErrorBar = "error_bar"
)

func f() error {
    var 
Download .txt
gitextract_0hghv22e/

├── .gitattributes
├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── community_mistake.md
│   │   └── erratum.md
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── docs/
│   ├── 20-slice.md
│   ├── 28-maps-memory-leaks.md
│   ├── 5-interface-pollution.md
│   ├── 56-concurrency-faster.md
│   ├── 89-benchmarks.md
│   ├── 9-generics.md
│   ├── 92-false-sharing.md
│   ├── 98-profiling-execution-tracing.md
│   ├── CNAME
│   ├── book.md
│   ├── chapter-1.md
│   ├── external.md
│   ├── index.md
│   ├── ja.md
│   ├── pt-br.md
│   ├── stylesheets/
│   │   └── extra.css
│   └── zh.md
├── go.mod
├── go.sum
├── includes/
│   └── abbreviations.md
├── justfile
├── mkdocs.yml
├── overrides/
│   ├── main.html
│   └── partials/
│       └── comments.html
├── site/
│   ├── 20-slice/
│   │   └── index.html
│   ├── 28-maps-memory-leaks/
│   │   └── index.html
│   ├── 404.html
│   ├── 5-interface-pollution/
│   │   └── index.html
│   ├── 56-concurrency-faster/
│   │   └── index.html
│   ├── 89-benchmarks/
│   │   └── index.html
│   ├── 9-generics/
│   │   └── index.html
│   ├── 92-false-sharing/
│   │   └── index.html
│   ├── 98-profiling-execution-tracing/
│   │   └── index.html
│   ├── CNAME
│   ├── assets/
│   │   └── javascripts/
│   │       └── lunr/
│   │           ├── tinyseg.js
│   │           └── wordcut.js
│   ├── book/
│   │   └── index.html
│   ├── chapter-1/
│   │   └── index.html
│   ├── external/
│   │   └── index.html
│   ├── index.html
│   ├── ja/
│   │   └── index.html
│   ├── pt-br/
│   │   └── index.html
│   ├── search/
│   │   └── search_index.json
│   ├── sitemap.xml
│   ├── stylesheets/
│   │   └── extra.css
│   └── zh/
│       └── index.html
└── src/
    ├── 02-code-project-organization/
    │   ├── 1-variable-shadowing/
    │   │   └── main.go
    │   ├── 10-type-embedding/
    │   │   └── main.go
    │   ├── 11-functional-options/
    │   │   ├── builder/
    │   │   │   └── main.go
    │   │   ├── config-struct/
    │   │   │   └── main.go
    │   │   └── functional-options/
    │   │       └── main.go
    │   ├── 13-utility-packages/
    │   │   └── stringset.go
    │   ├── 2-nested-code/
    │   │   └── main.go
    │   ├── 3-init-functions/
    │   │   ├── db/
    │   │   │   └── main.go
    │   │   ├── main/
    │   │   │   └── main.go
    │   │   └── redis/
    │   │       └── redis.go
    │   ├── 5-interface-pollution/
    │   │   ├── copy/
    │   │   │   ├── main.go
    │   │   │   └── main_test.go
    │   │   ├── decoupling/
    │   │   │   ├── with.go
    │   │   │   └── without.go
    │   │   └── restricting-behavior/
    │   │       └── main.go
    │   ├── 6-interface-producer/
    │   │   ├── client/
    │   │   │   └── client.go
    │   │   └── store/
    │   │       └── store.go
    │   ├── 8-any/
    │   │   ├── main.go
    │   │   └── store/
    │   │       ├── after.go
    │   │       └── before.go
    │   └── 9-generics/
    │       └── main.go
    ├── 03-data-types/
    │   ├── 17-octal-literals/
    │   │   └── main.go
    │   ├── 18-integer-overflows/
    │   │   └── main.go
    │   ├── 19-floating-points/
    │   │   └── main.go
    │   ├── 20-slice-length-cap/
    │   │   └── main.go
    │   ├── 21-slice-init/
    │   │   ├── main.go
    │   │   └── main_test.go
    │   ├── 22-nil-empty-slice/
    │   │   ├── json/
    │   │   │   └── main.go
    │   │   └── slice-init/
    │   │       └── main.go
    │   ├── 23-checking-slice-empty/
    │   │   └── main.go
    │   ├── 24-slice-copy/
    │   │   └── main.go
    │   ├── 25-slice-append/
    │   │   └── main.go
    │   ├── 26-slice-memory-leak/
    │   │   ├── capacity-leak/
    │   │   │   └── main.go
    │   │   └── slice-pointers/
    │   │       └── main.go
    │   ├── 27-map-init/
    │   │   └── main_test.go
    │   ├── 28-map-memory-leak/
    │   │   └── main.go
    │   └── 29-comparing-values/
    │       └── main.go
    ├── 04-control-structures/
    │   ├── 30-range-loop-element-copied/
    │   │   ├── concepts/
    │   │   │   └── main.go
    │   │   └── value-copy/
    │   │       └── main.go
    │   ├── 31-range-loop-arg-evaluation/
    │   │   ├── arrays/
    │   │   │   └── main.go
    │   │   ├── channels/
    │   │   │   └── main.go
    │   │   └── concepts/
    │   │       └── main.go
    │   ├── 32-range-loop-pointers/
    │   │   ├── concepts/
    │   │   │   └── main.go
    │   │   └── customer-store/
    │   │       └── main.go
    │   ├── 33-map-iteration/
    │   │   └── main.go
    │   ├── 34-break/
    │   │   └── main.go
    │   └── 35-defer-loop/
    │       └── main.go
    ├── 05-strings/
    │   ├── 36-rune/
    │   │   └── main.go
    │   ├── 37-string-iteration/
    │   │   └── main.go
    │   ├── 38-trim/
    │   │   └── main.go
    │   ├── 39-string-concat/
    │   │   ├── main.go
    │   │   └── main_test.go
    │   ├── 40-string-conversion/
    │   │   └── main.go
    │   └── 41-substring-memory-leak/
    │       └── main.go
    ├── 06-functions-methods/
    │   ├── 42-receiver/
    │   │   ├── pointer/
    │   │   │   └── main.go
    │   │   ├── struct-with-pointer/
    │   │   │   └── main.go
    │   │   └── value/
    │   │       └── main.go
    │   ├── 43-named-result-parameters/
    │   │   └── main.go
    │   ├── 44-side-effects-named-result-parameters/
    │   │   └── main.go
    │   ├── 45-nil-receiver/
    │   │   └── main.go
    │   ├── 46-function-input/
    │   │   ├── main.go
    │   │   └── main_test.go
    │   └── 47-defer-evaluation/
    │       ├── args/
    │       │   └── main.go
    │       └── receiver/
    │           ├── pointer/
    │           │   └── main.go
    │           └── value/
    │               └── main.go
    ├── 07-error-management/
    │   ├── 48-panic/
    │   │   └── main.go
    │   ├── 49-error-wrapping/
    │   │   └── main.go
    │   ├── 50-compare-error-type/
    │   │   └── main.go
    │   ├── 51-comparing-error-value/
    │   │   └── main.go
    │   ├── 52-handling-error-twice/
    │   │   └── main.go
    │   ├── 53-not-handling-error/
    │   │   └── main.go
    │   └── 54-defer-errors/
    │       └── main.go
    ├── 08-concurrency-foundations/
    │   ├── 56-faster/
    │   │   ├── main.go
    │   │   └── main_test.go
    │   ├── 58-races/
    │   │   ├── memory-model/
    │   │   │   └── main.go
    │   │   └── races/
    │   │       └── main.go
    │   ├── 59-workload-type/
    │   │   └── main.go
    │   └── 60-contexts/
    │       ├── flight/
    │       │   └── flight.go
    │       └── main.go
    ├── 09-concurrency-practice/
    │   ├── 61-inappropriate-context/
    │   │   └── main.go
    │   ├── 62-starting-goroutine/
    │   │   ├── listing1/
    │   │   │   └── main.go
    │   │   ├── listing2/
    │   │   │   └── main.go
    │   │   └── listing3/
    │   │       └── main.go
    │   ├── 63-goroutines-loop-variables/
    │   │   └── main.go
    │   ├── 64-select-behavior/
    │   │   └── main.go
    │   ├── 66-nil-channels/
    │   │   └── main.go
    │   ├── 68-string-formatting/
    │   │   └── main.go
    │   ├── 69-data-race-append/
    │   │   └── main.go
    │   ├── 70-mutex-slices-maps/
    │   │   └── main.go
    │   ├── 71-wait-group/
    │   │   └── main.go
    │   ├── 72-cond/
    │   │   └── main.go
    │   ├── 73-errgroup/
    │   │   └── main.go
    │   └── 74-copying-sync/
    │       └── main.go
    ├── 10-standard-lib/
    │   ├── 75-wrong-time-duration/
    │   │   └── main.go
    │   ├── 76-time-after/
    │   │   └── main.go
    │   ├── 77-json-handling/
    │   │   ├── map-any/
    │   │   │   └── main.go
    │   │   ├── monotonic-clock/
    │   │   │   └── main.go
    │   │   └── type-embedding/
    │   │       └── main.go
    │   ├── 78-sql/
    │   │   ├── null-values/
    │   │   │   └── main.go
    │   │   ├── prepared-statements/
    │   │   │   └── main.go
    │   │   ├── rows-iterations-errors/
    │   │   │   └── main.go
    │   │   └── sql-open/
    │   │       └── main.go
    │   ├── 79-closing-resources/
    │   │   ├── http/
    │   │   │   └── main.go
    │   │   ├── os-file/
    │   │   │   └── main.go
    │   │   └── sql-rows/
    │   │       └── main.go
    │   ├── 80-http-return/
    │   │   └── main.go
    │   └── 81-default-http-client-server/
    │       ├── client/
    │       │   └── main.go
    │       └── server/
    │           └── main.go
    ├── 11-testing/
    │   ├── 82-categorizing-tests/
    │   │   ├── build-tags/
    │   │   │   └── db_test.go
    │   │   └── short-mode/
    │   │       └── main_test.go
    │   ├── 85-table-driven-tests/
    │   │   ├── main.go
    │   │   └── main_test.go
    │   ├── 86-sleeping/
    │   │   ├── main.go
    │   │   └── main_test.go
    │   ├── 87-time-api/
    │   │   ├── listing1/
    │   │   │   ├── main.go
    │   │   │   └── main_test.go
    │   │   ├── listing2/
    │   │   │   ├── main.go
    │   │   │   └── main_test.go
    │   │   ├── listing3/
    │   │   │   ├── main.go
    │   │   │   └── main_test.go
    │   │   └── listing4/
    │   │       ├── main.go
    │   │       └── main_test.go
    │   ├── 88-utility-package/
    │   │   ├── httptest/
    │   │   │   ├── main.go
    │   │   │   └── main_test.go
    │   │   └── iotest/
    │   │       ├── main.go
    │   │       └── main_test.go
    │   ├── 89-benchmark/
    │   │   ├── compiler-optimizations/
    │   │   │   ├── main.go
    │   │   │   └── main_test.go
    │   │   ├── observer-effect/
    │   │   │   ├── main.go
    │   │   │   └── main_test.go
    │   │   ├── timer/
    │   │   │   └── main_test.go
    │   │   └── wrong-assumptions/
    │   │       └── main_test.go
    │   └── 90-testing-features/
    │       ├── different-package/
    │       │   ├── main.go
    │       │   └── main_test.go
    │       ├── setup-teardown/
    │       │   └── main_test.go
    │       └── utility-function/
    │           ├── main.go
    │           └── main_test.go
    └── 12-optimizations/
        ├── 91-cpu-caches/
        │   ├── cache-line/
        │   │   ├── main.go
        │   │   └── main_test.go
        │   ├── predictability/
        │   │   ├── main.go
        │   │   └── main_test.go
        │   └── slice-structs/
        │       ├── main.go
        │       └── main_test.go
        ├── 92-false-sharing/
        │   ├── main.go
        │   └── main_test.go
        ├── 93-instruction-level-parallelism/
        │   ├── main.go
        │   └── main_test.go
        ├── 94-data-alignment/
        │   ├── main.go
        │   └── main_test.go
        ├── 95-stack-heap/
        │   ├── main.go
        │   └── main_test.go
        └── 96-reduce-allocations/
            ├── compiler/
            │   └── main.go
            └── sync-pool/
                └── main.go
Download .txt
SYMBOL INDEX (712 symbols across 155 files)

FILE: site/assets/javascripts/lunr/tinyseg.js
  function TinySegmenter (line 33) | function TinySegmenter() {

FILE: site/assets/javascripts/lunr/wordcut.js
  function s (line 1) | function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&re...
  function isMatch (line 421) | function isMatch(pat, offset, ch) {
  function replacer (line 704) | function replacer(key, value) {
  function truncate (line 717) | function truncate(s, n) {
  function getMessage (line 725) | function getMessage(self) {
  function fail (line 742) | function fail(actual, expected, message, operator, stackStartFunction) {
  function ok (line 762) | function ok(value, message) {
  function _deepEqual (line 793) | function _deepEqual(actual, expected) {
  function isArguments (line 838) | function isArguments(object) {
  function objEquiv (line 842) | function objEquiv(a, b) {
  function expectedException (line 911) | function expectedException(actual, expected) {
  function _throws (line 927) | function _throws(shouldThrow, block, expected, message) {
  function balanced (line 983) | function balanced(a, b, str) {
  function maybeMatch (line 998) | function maybeMatch(reg, str) {
  function range (line 1004) | function range(a, b, str) {
  function numeric (line 1053) | function numeric(str) {
  function escapeBraces (line 1059) | function escapeBraces(str) {
  function unescapeBraces (line 1067) | function unescapeBraces(str) {
  function parseCommaParts (line 1079) | function parseCommaParts(str) {
  function expandTop (line 1106) | function expandTop(str) {
  function identity (line 1123) | function identity(e) {
  function embrace (line 1127) | function embrace(str) {
  function isPadded (line 1130) | function isPadded(el) {
  function lte (line 1134) | function lte(i, y) {
  function gte (line 1137) | function gte(i, y) {
  function expand (line 1141) | function expand(str, isTop) {
  function EventEmitter (line 1283) | function EventEmitter() {
  function g (line 1425) | function g() {
  function isFunction (line 1548) | function isFunction(arg) {
  function isNumber (line 1552) | function isNumber(arg) {
  function isObject (line 1556) | function isObject(arg) {
  function isUndefined (line 1560) | function isUndefined(arg) {
  function ownProp (line 1576) | function ownProp (obj, field) {
  function alphasorti (line 1585) | function alphasorti (a, b) {
  function alphasort (line 1589) | function alphasort (a, b) {
  function setupIgnores (line 1593) | function setupIgnores (self, options) {
  function ignoreMap (line 1604) | function ignoreMap (pattern) {
  function setopts (line 1617) | function setopts (self, pattern, options) {
  function deprecationWarning (line 1684) | function deprecationWarning(options) {
  function finish (line 1700) | function finish (self) {
  function mark (line 1753) | function mark (self, p) {
  function makeAbs (line 1777) | function makeAbs (self, f) {
  function isIgnored (line 1794) | function isIgnored (self, path) {
  function childrenIgnored (line 1803) | function childrenIgnored (self, path) {
  function glob (line 1878) | function glob (pattern, options, cb) {
  function Glob (line 1916) | function Glob (pattern, options, cb) {
  function next (line 2003) | function next () {
  function lstatcb_ (line 2294) | function lstatcb_ (er, lstat) {
  function readdirCb (line 2336) | function readdirCb (self, abs, cb) {
  function lstatcb_ (line 2531) | function lstatcb_ (er, lstat) {
  function globSync (line 2589) | function globSync (pattern, options) {
  function GlobSync (line 2597) | function GlobSync (pattern, options) {
  function inflight (line 3041) | function inflight (key, cb) {
  function makeres (line 3051) | function makeres (key) {
  function slice (line 3082) | function slice (args) {
  function charSet (line 3156) | function charSet (s) {
  function filter (line 3167) | function filter (pattern, options) {
  function ext (line 3174) | function ext (a, b) {
  function minimatch (line 3208) | function minimatch (p, pattern, options) {
  function Minimatch (line 3226) | function Minimatch (pattern, options) {
  function make (line 3258) | function make () {
  function parseNegate (line 3314) | function parseNegate () {
  function braceExpand (line 3349) | function braceExpand (pattern, options) {
  function parse (line 3387) | function parse (pattern, isSub) {
  function makeRe (line 3758) | function makeRe () {
  function match (line 3816) | function match (f, partial) {
  function globUnescape (line 4033) | function globUnescape (s) {
  function regExpEscape (line 4037) | function regExpEscape (s) {
  function once (line 4062) | function once (fn) {
  function onceStrict (line 4072) | function onceStrict (fn) {
  function normalizeArray (line 4112) | function normalizeArray(parts, allowAboveRoot) {
  function trim (line 4221) | function trim(arr) {
  function filter (line 4294) | function filter (xs, f) {
  function posix (line 4317) | function posix(path) {
  function win32 (line 4321) | function win32(path) {
  function defaultSetTimout (line 4349) | function defaultSetTimout() {
  function defaultClearTimeout (line 4352) | function defaultClearTimeout () {
  function runTimeout (line 4375) | function runTimeout(fun) {
  function runClearTimeout (line 4400) | function runClearTimeout(marker) {
  function cleanUpNextTick (line 4432) | function cleanUpNextTick() {
  function drainQueue (line 4447) | function drainQueue() {
  function Item (line 4485) | function Item(fun, array) {
  function noop (line 4499) | function noop() {}
  function createReduce (line 4701) | function createReduce(dir) {
  function createPredicateIndexFinder (line 5137) | function createPredicateIndexFinder(dir) {
  function createIndexFinder (line 5167) | function createIndexFinder(dir, predicateFind, sortedIndex) {
  function collectNonEnumProps (line 5432) | function collectNonEnumProps(obj, keys) {
  function deprecated (line 6161) | function deprecated() {
  function inspect (line 6208) | function inspect(obj, opts) {
  function stylizeWithColor (line 6266) | function stylizeWithColor(str, styleType) {
  function stylizeNoColor (line 6278) | function stylizeNoColor(str, styleType) {
  function arrayToHash (line 6283) | function arrayToHash(array) {
  function formatValue (line 6294) | function formatValue(ctx, value, recurseTimes) {
  function formatPrimitive (line 6407) | function formatPrimitive(ctx, value) {
  function formatError (line 6426) | function formatError(value) {
  function formatArray (line 6431) | function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
  function formatProperty (line 6451) | function formatProperty(ctx, value, recurseTimes, visibleKeys, key, arra...
  function reduceToSingleString (line 6510) | function reduceToSingleString(output, base, braces) {
  function isArray (line 6533) | function isArray(ar) {
  function isBoolean (line 6538) | function isBoolean(arg) {
  function isNull (line 6543) | function isNull(arg) {
  function isNullOrUndefined (line 6548) | function isNullOrUndefined(arg) {
  function isNumber (line 6553) | function isNumber(arg) {
  function isString (line 6558) | function isString(arg) {
  function isSymbol (line 6563) | function isSymbol(arg) {
  function isUndefined (line 6568) | function isUndefined(arg) {
  function isRegExp (line 6573) | function isRegExp(re) {
  function isObject (line 6578) | function isObject(arg) {
  function isDate (line 6583) | function isDate(d) {
  function isError (line 6588) | function isError(e) {
  function isFunction (line 6594) | function isFunction(arg) {
  function isPrimitive (line 6599) | function isPrimitive(arg) {
  function objectToString (line 6611) | function objectToString(o) {
  function pad (line 6616) | function pad(n) {
  function timestamp (line 6625) | function timestamp() {
  function hasOwnProperty (line 6667) | function hasOwnProperty(obj, prop) {
  function wrappy (line 6679) | function wrappy (fn, cb) {

FILE: src/02-code-project-organization/1-variable-shadowing/main.go
  function main (line 8) | func main() {
  function listing1 (line 15) | func listing1() error {
  function listing2 (line 35) | func listing2() error {
  function listing3 (line 55) | func listing3() error {
  function listing4 (line 74) | func listing4() error {
  function createClientWithTracing (line 92) | func createClientWithTracing() (*http.Client, error) {
  function createDefaultClient (line 96) | func createDefaultClient() (*http.Client, error) {

FILE: src/02-code-project-organization/10-type-embedding/main.go
  type Foo (line 9) | type Foo struct
  type Bar (line 13) | type Bar struct
  function fooBar (line 17) | func fooBar() {
  type InMem (line 22) | type InMem struct
    method Get (line 31) | func (i *InMem) Get(key string) (int, bool) {
  function New (line 27) | func New() *InMem {
  type Logger (line 38) | type Logger struct
    method Write (line 42) | func (l Logger) Write(p []byte) (int, error) {
    method Close (line 46) | func (l Logger) Close() error {
  function main (line 50) | func main() {

FILE: src/02-code-project-organization/11-functional-options/builder/main.go
  constant defaultHTTPPort (line 8) | defaultHTTPPort = 8080
  type Config (line 10) | type Config struct
  type ConfigBuilder (line 14) | type ConfigBuilder struct
    method Port (line 18) | func (b *ConfigBuilder) Port(port int) *ConfigBuilder {
    method Build (line 23) | func (b *ConfigBuilder) Build() (Config, error) {
  function NewServer (line 41) | func NewServer(addr string, config Config) (*http.Server, error) {
  function client (line 45) | func client() error {
  function randomPort (line 62) | func randomPort() int {

FILE: src/02-code-project-organization/11-functional-options/config-struct/main.go
  type Config (line 3) | type Config struct
  function NewServer (line 7) | func NewServer(addr string, cfg Config) {
  function main (line 10) | func main() {

FILE: src/02-code-project-organization/11-functional-options/functional-options/main.go
  constant defaultHTTPPort (line 8) | defaultHTTPPort = 8080
  type options (line 10) | type options struct
  type Option (line 14) | type Option
  function WithPort (line 16) | func WithPort(port int) Option {
  function NewServer (line 26) | func NewServer(addr string, opts ...Option) (*http.Server, error) {
  function client (line 52) | func client() {
  function randomPort (line 56) | func randomPort() int {

FILE: src/02-code-project-organization/13-utility-packages/stringset.go
  type Set (line 3) | type Set
    method Sort (line 7) | func (s Set) Sort() []string { return nil }
  function New (line 5) | func New(...string) Set { return nil }

FILE: src/02-code-project-organization/2-nested-code/main.go
  function join1 (line 5) | func join1(s1, s2 string, max int) (string, error) {
  function join2 (line 26) | func join2(s1, s2 string, max int) (string, error) {
  function concatenate (line 43) | func concatenate(s1, s2 string) (string, error) {

FILE: src/02-code-project-organization/3-init-functions/db/main.go
  function init (line 11) | func init() {
  function createClient (line 24) | func createClient(dataSourceName string) (*sql.DB, error) {

FILE: src/02-code-project-organization/3-init-functions/main/main.go
  function init (line 9) | func init() {
  function init (line 13) | func init() {
  function main (line 17) | func main() {

FILE: src/02-code-project-organization/3-init-functions/redis/redis.go
  function init (line 5) | func init() {
  function Store (line 9) | func Store(key, value string) error {

FILE: src/02-code-project-organization/5-interface-pollution/copy/main.go
  function copySourceToDest (line 5) | func copySourceToDest(source io.Reader, dest io.Writer) error {

FILE: src/02-code-project-organization/5-interface-pollution/copy/main_test.go
  function TestCopySourceToDest (line 9) | func TestCopySourceToDest(t *testing.T) {

FILE: src/02-code-project-organization/5-interface-pollution/decoupling/with.go
  type customerStorer (line 3) | type customerStorer interface
  type CustomerService2 (line 7) | type CustomerService2 struct
    method CreateNewCustomer (line 11) | func (cs CustomerService2) CreateNewCustomer(id string) error {

FILE: src/02-code-project-organization/5-interface-pollution/decoupling/without.go
  type CustomerService (line 3) | type CustomerService struct
    method CreateNewCustomer (line 7) | func (cs CustomerService) CreateNewCustomer(id string) error {
  type Customer (line 12) | type Customer struct
  type Store (line 16) | type Store struct
    method StoreCustomer (line 18) | func (s Store) StoreCustomer(customer Customer) error {

FILE: src/02-code-project-organization/5-interface-pollution/restricting-behavior/main.go
  type IntConfig (line 3) | type IntConfig struct
    method Get (line 7) | func (c *IntConfig) Get() int {
    method Set (line 11) | func (c *IntConfig) Set(value int) {
  type intConfigGetter (line 15) | type intConfigGetter interface
  type Foo (line 19) | type Foo struct
    method Bar (line 27) | func (f Foo) Bar() {
  function NewFoo (line 23) | func NewFoo(threshold intConfigGetter) Foo {
  function main (line 32) | func main() {

FILE: src/02-code-project-organization/6-interface-producer/client/client.go
  type customersGetter (line 5) | type customersGetter interface

FILE: src/02-code-project-organization/6-interface-producer/store/store.go
  type CustomerStorage (line 3) | type CustomerStorage interface
  type Customer (line 12) | type Customer struct

FILE: src/02-code-project-organization/8-any/main.go
  function main (line 3) | func main() {
  function f (line 18) | func f() {}

FILE: src/02-code-project-organization/8-any/store/after.go
  method GetContract (line 3) | func (s *Store) GetContract(id string) (Contract, error) {
  method SetContract (line 7) | func (s *Store) SetContract(id string, contract Contract) error {
  method GetCustomer (line 11) | func (s *Store) GetCustomer(id string) (Customer, error) {
  method SetCustomer (line 15) | func (s *Store) SetCustomer(id string, customer Customer) error {

FILE: src/02-code-project-organization/8-any/store/before.go
  type Customer (line 3) | type Customer struct
  type Contract (line 7) | type Contract struct
  type Store (line 11) | type Store struct
    method Get (line 13) | func (s *Store) Get(id string) (any, error) {
    method Set (line 17) | func (s *Store) Set(id string, v any) error {

FILE: src/02-code-project-organization/9-generics/main.go
  function getKeys (line 8) | func getKeys(m any) ([]any, error) {
  function getKeysGenerics (line 25) | func getKeysGenerics[K comparable, V any](m map[K]V) []K {
  type customConstraint (line 33) | type customConstraint interface
  function getKeysWithConstraint (line 37) | func getKeysWithConstraint[K customConstraint, V any](m map[K]V) []K {
  type Node (line 41) | type Node struct
  method Add (line 46) | func (n *Node[T]) Add(next *Node[T]) {
  type SliceFn (line 50) | type SliceFn struct
  method Len (line 55) | func (s SliceFn[T]) Len() int           { return len(s.S) }
  method Less (line 56) | func (s SliceFn[T]) Less(i, j int) bool { return s.Compare(s.S[i], s.S[j...
  method Swap (line 57) | func (s SliceFn[T]) Swap(i, j int)      { s.S[i], s.S[j] = s.S[j], s.S[i] }
  function main (line 59) | func main() {

FILE: src/03-data-types/17-octal-literals/main.go
  function main (line 5) | func main() {

FILE: src/03-data-types/18-integer-overflows/main.go
  function Inc32 (line 5) | func Inc32(counter int32) int32 {
  function IncInt (line 12) | func IncInt(counter int) int {
  function IncUint (line 19) | func IncUint(counter uint) uint {
  function AddInt (line 26) | func AddInt(a, b int) int {
  function MultiplyInt (line 34) | func MultiplyInt(a, b int) int {

FILE: src/03-data-types/19-floating-points/main.go
  function main (line 5) | func main() {
  function f1 (line 16) | func f1(n int) float64 {
  function f2 (line 24) | func f2(n int) float64 {

FILE: src/03-data-types/20-slice-length-cap/main.go
  function main (line 5) | func main() {
  function print (line 34) | func print(s []int) {

FILE: src/03-data-types/21-slice-init/main.go
  function convertEmptySlice (line 3) | func convertEmptySlice(foos []Foo) []Bar {
  function convertGivenCapacity (line 12) | func convertGivenCapacity(foos []Foo) []Bar {
  function convertGivenLength (line 22) | func convertGivenLength(foos []Foo) []Bar {
  type Foo (line 32) | type Foo struct
  type Bar (line 34) | type Bar struct
  function fooToBar (line 36) | func fooToBar(foo Foo) Bar {

FILE: src/03-data-types/21-slice-init/main_test.go
  constant n (line 5) | n = 1_000_000
  function BenchmarkConvert_EmptySlice (line 9) | func BenchmarkConvert_EmptySlice(b *testing.B) {
  function BenchmarkConvert_GivenCapacity (line 19) | func BenchmarkConvert_GivenCapacity(b *testing.B) {
  function BenchmarkConvert_GivenLength (line 29) | func BenchmarkConvert_GivenLength(b *testing.B) {

FILE: src/03-data-types/22-nil-empty-slice/json/main.go
  function main (line 8) | func main() {
  type customer (line 26) | type customer struct

FILE: src/03-data-types/22-nil-empty-slice/slice-init/main.go
  function main (line 7) | func main() {
  function log (line 21) | func log(i int, s []string) {

FILE: src/03-data-types/23-checking-slice-empty/main.go
  function handleOperations1 (line 3) | func handleOperations1(id string) {
  function handleOperations2 (line 10) | func handleOperations2(id string) {
  function getOperations (line 17) | func getOperations(id string) []float32 {
  function handle (line 29) | func handle(operations []float32) {}

FILE: src/03-data-types/24-slice-copy/main.go
  function bad (line 5) | func bad() {
  function correct (line 15) | func correct() {
  function main (line 25) | func main() {

FILE: src/03-data-types/25-slice-append/main.go
  function main (line 5) | func main() {
  function listing1 (line 11) | func listing1() {
  function listing2 (line 18) | func listing2() {
  function listing3 (line 28) | func listing3() {
  function f (line 34) | func f(s []int) {

FILE: src/03-data-types/26-slice-memory-leak/capacity-leak/main.go
  function consumeMessages (line 8) | func consumeMessages() {
  function getMessageType (line 16) | func getMessageType(msg []byte) []byte {
  function getMessageTypeWithCopy (line 20) | func getMessageTypeWithCopy(msg []byte) []byte {
  function receiveMessage (line 26) | func receiveMessage() []byte {
  function storeMessageType (line 30) | func storeMessageType([]byte) {}
  function printAlloc (line 32) | func printAlloc() {

FILE: src/03-data-types/26-slice-memory-leak/slice-pointers/main.go
  type Foo (line 8) | type Foo struct
  function main (line 12) | func main() {
  function keepFirstTwoElementsOnly (line 29) | func keepFirstTwoElementsOnly(foos []Foo) []Foo {
  function keepFirstTwoElementsOnlyCopy (line 33) | func keepFirstTwoElementsOnlyCopy(foos []Foo) []Foo {
  function keepFirstTwoElementsOnlyMarkNil (line 39) | func keepFirstTwoElementsOnlyMarkNil(foos []Foo) []Foo {
  function printAlloc (line 46) | func printAlloc() {

FILE: src/03-data-types/27-map-init/main_test.go
  constant n (line 5) | n = 1_000_000
  function BenchmarkMapWithoutSize (line 9) | func BenchmarkMapWithoutSize(b *testing.B) {
  function BenchmarkMapWithSize (line 20) | func BenchmarkMapWithSize(b *testing.B) {

FILE: src/03-data-types/28-map-memory-leak/main.go
  function main (line 8) | func main() {
  function randBytes (line 31) | func randBytes() [128]byte {
  function printAlloc (line 35) | func printAlloc() {

FILE: src/03-data-types/29-comparing-values/main.go
  type customer1 (line 8) | type customer1 struct
  type customer2 (line 12) | type customer2 struct
    method equal (line 42) | func (a customer2) equal(b customer2) bool {
  function main (line 17) | func main() {

FILE: src/04-control-structures/30-range-loop-element-copied/concepts/main.go
  function main (line 5) | func main() {

FILE: src/04-control-structures/30-range-loop-element-copied/value-copy/main.go
  type account (line 8) | type account struct
  function main (line 12) | func main() {
  function createAccounts (line 38) | func createAccounts() []account {
  function createAccountsPtr (line 46) | func createAccountsPtr() []*account {
  function printAccountsPtr (line 54) | func printAccountsPtr(accounts []*account) {

FILE: src/04-control-structures/31-range-loop-arg-evaluation/arrays/main.go
  function listing1 (line 5) | func listing1() {
  function listing2 (line 15) | func listing2() {
  function listing3 (line 25) | func listing3() {

FILE: src/04-control-structures/31-range-loop-arg-evaluation/channels/main.go
  function main (line 5) | func main() {

FILE: src/04-control-structures/31-range-loop-arg-evaluation/concepts/main.go
  function main (line 3) | func main() {

FILE: src/04-control-structures/32-range-loop-pointers/concepts/main.go
  type Store (line 3) | type Store struct
    method Put (line 7) | func (s Store) Put(id string, foo *Foo) {
  type Foo (line 12) | type Foo struct
  function updateMapValue (line 14) | func updateMapValue(mapValue map[string]LargeStruct, id string) {
  function updateMapPointer (line 20) | func updateMapPointer(mapPointer map[string]*LargeStruct, id string) {
  type LargeStruct (line 24) | type LargeStruct struct

FILE: src/04-control-structures/32-range-loop-pointers/customer-store/main.go
  type Customer (line 5) | type Customer struct
  type Store (line 10) | type Store struct
    method storeCustomers (line 26) | func (s *Store) storeCustomers(customers []Customer) {
    method storeCustomers2 (line 33) | func (s *Store) storeCustomers2(customers []Customer) {
    method storeCustomers3 (line 40) | func (s *Store) storeCustomers3(customers []Customer) {
  function main (line 14) | func main() {
  function print (line 47) | func print(m map[string]*Customer) {

FILE: src/04-control-structures/33-map-iteration/main.go
  function listing1 (line 5) | func listing1() {
  function listing2 (line 21) | func listing2() {
  function copyMap (line 39) | func copyMap(m map[int]bool) map[int]bool {

FILE: src/04-control-structures/34-break/main.go
  function listing1 (line 8) | func listing1() {
  function listing2 (line 20) | func listing2() {
  function listing3 (line 33) | func listing3(ctx context.Context, ch <-chan int) {
  function listing4 (line 44) | func listing4(ctx context.Context, ch <-chan int) {

FILE: src/04-control-structures/35-defer-loop/main.go
  function readFiles1 (line 5) | func readFiles1(ch <-chan string) error {
  function readFiles2 (line 19) | func readFiles2(ch <-chan string) error {
  function readFile (line 28) | func readFile(path string) error {
  function readFiles3 (line 40) | func readFiles3(ch <-chan string) error {

FILE: src/05-strings/36-rune/main.go
  function main (line 5) | func main() {

FILE: src/05-strings/37-string-iteration/main.go
  function main (line 5) | func main() {
  function getIthRune (line 25) | func getIthRune(largeString string, i int) rune {

FILE: src/05-strings/38-trim/main.go
  function main (line 8) | func main() {

FILE: src/05-strings/39-string-concat/main.go
  function concat1 (line 5) | func concat1(values []string) string {
  function concat2 (line 13) | func concat2(values []string) string {
  function concat3 (line 21) | func concat3(values []string) string {

FILE: src/05-strings/39-string-concat/main_test.go
  function BenchmarkConcatV1 (line 7) | func BenchmarkConcatV1(b *testing.B) {
  function BenchmarkConcatV2 (line 17) | func BenchmarkConcatV2(b *testing.B) {
  function BenchmarkConcatV3 (line 27) | func BenchmarkConcatV3(b *testing.B) {
  function getInput (line 37) | func getInput() []string {

FILE: src/05-strings/40-string-conversion/main.go
  function getBytes1 (line 9) | func getBytes1(reader io.Reader) ([]byte, error) {
  function sanitize1 (line 17) | func sanitize1(s string) string {
  function getBytes2 (line 21) | func getBytes2(reader io.Reader) ([]byte, error) {
  function sanitize2 (line 29) | func sanitize2(b []byte) []byte {

FILE: src/05-strings/41-substring-memory-leak/main.go
  function main (line 9) | func main() {
  type store (line 19) | type store struct
    method handleLog1 (line 21) | func (s store) handleLog1(log string) error {
    method handleLog2 (line 31) | func (s store) handleLog2(log string) error {
    method handleLog3 (line 41) | func (s store) handleLog3(log string) error {
    method store (line 51) | func (s store) store(uuid string) {

FILE: src/06-functions-methods/42-receiver/pointer/main.go
  type customer (line 5) | type customer struct
    method add (line 9) | func (c *customer) add(operation float64) {
  function main (line 13) | func main() {

FILE: src/06-functions-methods/42-receiver/struct-with-pointer/main.go
  type customer (line 5) | type customer struct
    method add (line 13) | func (c customer) add(operation float64) {
  type data (line 9) | type data struct
  function main (line 17) | func main() {

FILE: src/06-functions-methods/42-receiver/value/main.go
  type customer (line 5) | type customer struct
    method add (line 9) | func (c customer) add(v float64) {
  function main (line 13) | func main() {

FILE: src/06-functions-methods/43-named-result-parameters/main.go
  function f (line 5) | func f(a int) (b int) {
  type locator (line 10) | type locator interface
  type loc (line 15) | type loc struct
    method getCoordinates (line 17) | func (l loc) getCoordinates(address string) (lat, lng float32, err err...
  function ReadFull (line 21) | func ReadFull(r io.Reader, buf []byte) (n int, err error) {

FILE: src/06-functions-methods/44-side-effects-named-result-parameters/main.go
  type loc (line 8) | type loc struct
    method getCoordinates1 (line 10) | func (l loc) getCoordinates1(ctx context.Context, address string) (
    method getCoordinates2 (line 25) | func (l loc) getCoordinates2(ctx context.Context, address string) (
    method getCoordinates3 (line 40) | func (l loc) getCoordinates3(ctx context.Context, address string) (
    method validateAddress (line 55) | func (l loc) validateAddress(address string) bool {

FILE: src/06-functions-methods/45-nil-receiver/main.go
  type MultiError (line 9) | type MultiError struct
    method Add (line 13) | func (m *MultiError) Add(err error) {
    method Error (line 17) | func (m *MultiError) Error() string {
  type Customer (line 21) | type Customer struct
    method Validate1 (line 26) | func (c Customer) Validate1() error {
    method Validate2 (line 43) | func (c Customer) Validate2() error {
  function main (line 63) | func main() {

FILE: src/06-functions-methods/46-function-input/main.go
  function countEmptyLinesInFile (line 9) | func countEmptyLinesInFile(filename string) (int, error) {
  function countEmptyLines (line 24) | func countEmptyLines(reader io.Reader) (int, error) {
  function main (line 32) | func main() {

FILE: src/06-functions-methods/46-function-input/main_test.go
  function TestCountEmptyLines (line 8) | func TestCountEmptyLines(t *testing.T) {

FILE: src/06-functions-methods/47-defer-evaluation/args/main.go
  constant StatusSuccess (line 6) | StatusSuccess  = "success"
  constant StatusErrorFoo (line 7) | StatusErrorFoo = "error_foo"
  constant StatusErrorBar (line 8) | StatusErrorBar = "error_bar"
  function main (line 11) | func main() {
  function f1 (line 17) | func f1() error {
  function f2 (line 36) | func f2() error {
  function f3 (line 55) | func f3() error {
  function notify (line 76) | func notify(status string) {
  function incrementCounter (line 80) | func incrementCounter(status string) {
  function notifyPtr (line 84) | func notifyPtr(status *string) {
  function incrementCounterPtr (line 88) | func incrementCounterPtr(status *string) {
  function foo (line 92) | func foo() error {
  function bar (line 96) | func bar() error {

FILE: src/06-functions-methods/47-defer-evaluation/receiver/pointer/main.go
  function main (line 5) | func main() {
  type Struct (line 11) | type Struct struct
    method print (line 15) | func (s *Struct) print() {

FILE: src/06-functions-methods/47-defer-evaluation/receiver/value/main.go
  function main (line 5) | func main() {
  type Struct (line 11) | type Struct struct
    method print (line 15) | func (s Struct) print() {

FILE: src/07-error-management/48-panic/main.go
  function main (line 5) | func main() {
  function f (line 15) | func f() {
  function checkWriteHeaderCode (line 21) | func checkWriteHeaderCode(code int) {

FILE: src/07-error-management/49-error-wrapping/main.go
  function bar (line 5) | func bar() error {
  type barError (line 9) | type barError struct
    method Error (line 11) | func (b barError) Error() string {
  function listing1 (line 15) | func listing1() error {
  type BarError (line 24) | type BarError struct
    method Error (line 28) | func (b BarError) Error() string {
  function listing2 (line 32) | func listing2() error {
  function listing3 (line 41) | func listing3() error {
  function listing4 (line 50) | func listing4() error {

FILE: src/07-error-management/50-compare-error-type/main.go
  type transientError (line 9) | type transientError struct
    method Error (line 13) | func (t transientError) Error() string {
  function handler (line 17) | func handler(w http.ResponseWriter, r *http.Request) {
  function getTransactionAmount1 (line 35) | func getTransactionAmount1(transactionID string) (float32, error) {
  function getTransactionAmountFromDB1 (line 47) | func getTransactionAmountFromDB1(id string) (float32, error) {
  function handler2 (line 51) | func handler2(w http.ResponseWriter, r *http.Request) {
  function getTransactionAmount2 (line 68) | func getTransactionAmount2(transactionID string) (float32, error) {
  function getTransactionAmountFromDB2 (line 79) | func getTransactionAmountFromDB2(transactionID string) (float32, error) {

FILE: src/07-error-management/51-comparing-error-value/main.go
  function listing1 (line 8) | func listing1() {
  function listing2 (line 19) | func listing2() {
  function query (line 30) | func query() error {

FILE: src/07-error-management/52-handling-error-twice/main.go
  type Route (line 8) | type Route struct
  function GetRoute1 (line 10) | func GetRoute1(srcLat, srcLng, dstLat, dstLng float32) (Route, error) {
  function validateCoordinates1 (line 26) | func validateCoordinates1(lat, lng float32) error {
  function GetRoute2 (line 38) | func GetRoute2(srcLat, srcLng, dstLat, dstLng float32) (Route, error) {
  function validateCoordinates2 (line 52) | func validateCoordinates2(lat, lng float32) error {
  function GetRoute3 (line 62) | func GetRoute3(srcLat, srcLng, dstLat, dstLng float32) (Route, error) {
  function getRoute (line 78) | func getRoute(lat, lng, lat2, lng2 float32) (Route, error) {

FILE: src/07-error-management/53-not-handling-error/main.go
  function listing1 (line 5) | func listing1() {
  function listing2 (line 11) | func listing2() {
  function notify (line 19) | func notify() error {

FILE: src/07-error-management/54-defer-errors/main.go
  constant query (line 8) | query = "..."
  function getBalance1 (line 10) | func getBalance1(db *sql.DB, clientID string) (float32, error) {
  function getBalance2 (line 21) | func getBalance2(db *sql.DB, clientID string) (float32, error) {
  function getBalance3 (line 32) | func getBalance3(db *sql.DB, clientID string) (balance float32, err erro...

FILE: src/08-concurrency-foundations/56-faster/main.go
  function sequentialMergesort (line 7) | func sequentialMergesort(s []int) {
  function parallelMergesortV1 (line 18) | func parallelMergesortV1(s []int) {
  constant max (line 42) | max = 2048
  function parallelMergesortV2 (line 44) | func parallelMergesortV2(s []int) {
  function merge (line 72) | func merge(s []int, middle int) {

FILE: src/08-concurrency-foundations/56-faster/main_test.go
  function Benchmark_sequentialMergesort (line 11) | func Benchmark_sequentialMergesort(b *testing.B) {
  function Benchmark_parallelMergesortV1 (line 25) | func Benchmark_parallelMergesortV1(b *testing.B) {
  function Benchmark_parallelMergesortV2 (line 39) | func Benchmark_parallelMergesortV2(b *testing.B) {
  function getRandomElements (line 53) | func getRandomElements() []int {

FILE: src/08-concurrency-foundations/58-races/memory-model/main.go
  function listing1 (line 5) | func listing1() {
  function listing2 (line 12) | func listing2() {
  function listing3 (line 20) | func listing3() {
  function listing4 (line 31) | func listing4() {
  function listing5 (line 42) | func listing5() {
  function listing6 (line 53) | func listing6() {

FILE: src/08-concurrency-foundations/58-races/races/main.go
  function listing1 (line 8) | func listing1() {
  function listing2 (line 20) | func listing2() {
  function listing3 (line 32) | func listing3() {
  function listing4 (line 49) | func listing4() {
  function listing5 (line 65) | func listing5() {

FILE: src/08-concurrency-foundations/59-workload-type/main.go
  function main (line 10) | func main() {
  function read1 (line 18) | func read1(r io.Reader) (int, error) {
  function read2 (line 34) | func read2(r io.Reader) (int, error) {
  function task (line 68) | func task(b []byte) int {
  type dummyReader (line 72) | type dummyReader struct
    method Read (line 76) | func (c *dummyReader) Read(p []byte) (n int, err error) {

FILE: src/08-concurrency-foundations/60-contexts/flight/flight.go
  type Position (line 3) | type Position struct

FILE: src/08-concurrency-foundations/60-contexts/main.go
  type publisher (line 11) | type publisher interface
  type publishHandler (line 15) | type publishHandler struct
    method publishPosition (line 19) | func (h publishHandler) publishPosition(position flight.Position) error {
  type key (line 25) | type key
  constant isValidHostKey (line 27) | isValidHostKey key = "isValidHost"
  function checkValid (line 29) | func checkValid(next http.Handler) http.Handler {
  function handler (line 38) | func handler(ctx context.Context, ch chan Message) error {
  type Message (line 50) | type Message struct

FILE: src/09-concurrency-practice/61-inappropriate-context/main.go
  function handler1 (line 9) | func handler1(w http.ResponseWriter, r *http.Request) {
  function handler2 (line 25) | func handler2(w http.ResponseWriter, r *http.Request) {
  function handler3 (line 41) | func handler3(w http.ResponseWriter, r *http.Request) {
  type detach (line 57) | type detach struct
    method Deadline (line 61) | func (d detach) Deadline() (time.Time, bool) {
    method Done (line 65) | func (d detach) Done() <-chan struct{} {
    method Err (line 69) | func (d detach) Err() error {
    method Value (line 73) | func (d detach) Value(key any) any {
  function doSomeTask (line 77) | func doSomeTask(context.Context, *http.Request) (string, error) {
  function publish (line 81) | func publish(context.Context, string) error {
  function writeResponse (line 85) | func writeResponse(string) {}

FILE: src/09-concurrency-practice/62-starting-goroutine/listing1/main.go
  function main (line 3) | func main() {
  function newWatcher (line 9) | func newWatcher() {
  type watcher (line 14) | type watcher struct
    method watch (line 17) | func (w watcher) watch() {}

FILE: src/09-concurrency-practice/62-starting-goroutine/listing2/main.go
  function main (line 5) | func main() {
  function newWatcher (line 14) | func newWatcher(ctx context.Context) {
  type watcher (line 19) | type watcher struct
    method watch (line 22) | func (w watcher) watch(context.Context) {}

FILE: src/09-concurrency-practice/62-starting-goroutine/listing3/main.go
  function main (line 3) | func main() {
  function newWatcher (line 10) | func newWatcher() watcher {
  type watcher (line 16) | type watcher struct
    method watch (line 19) | func (w watcher) watch() {}
    method close (line 21) | func (w watcher) close() {

FILE: src/09-concurrency-practice/63-goroutines-loop-variables/main.go
  function listing1 (line 5) | func listing1() {
  function listing2 (line 15) | func listing2() {
  function listing3 (line 26) | func listing3() {

FILE: src/09-concurrency-practice/64-select-behavior/main.go
  function main (line 8) | func main() {
  function listing1 (line 21) | func listing1(messageCh <-chan int, disconnectCh chan struct{}) {
  function listing2 (line 33) | func listing2(messageCh <-chan int, disconnectCh chan struct{}) {

FILE: src/09-concurrency-practice/66-nil-channels/main.go
  function merge1 (line 3) | func merge1(ch1, ch2 <-chan int) <-chan int {
  function merge2 (line 19) | func merge2(ch1, ch2 <-chan int) <-chan int {
  function merge3 (line 37) | func merge3(ch1, ch2 <-chan int) <-chan int {
  function merge4 (line 69) | func merge4(ch1, ch2 <-chan int) <-chan int {

FILE: src/09-concurrency-practice/68-string-formatting/main.go
  function main (line 8) | func main() {
  type Customer (line 15) | type Customer struct
    method UpdateAge1 (line 21) | func (c *Customer) UpdateAge1(age int) error {
    method UpdateAge2 (line 33) | func (c *Customer) UpdateAge2(age int) error {
    method UpdateAge3 (line 45) | func (c *Customer) UpdateAge3(age int) error {
    method String (line 57) | func (c *Customer) String() string {

FILE: src/09-concurrency-practice/69-data-race-append/main.go
  function listing1 (line 5) | func listing1() {
  function listing2 (line 19) | func listing2() {

FILE: src/09-concurrency-practice/70-mutex-slices-maps/main.go
  function main (line 8) | func main() {
  type Cache (line 19) | type Cache struct
    method AddBalance (line 24) | func (c *Cache) AddBalance(id string, balance float64) {
    method AverageBalance1 (line 30) | func (c *Cache) AverageBalance1() float64 {
    method AverageBalance2 (line 42) | func (c *Cache) AverageBalance2() float64 {
    method AverageBalance3 (line 53) | func (c *Cache) AverageBalance3() float64 {

FILE: src/09-concurrency-practice/71-wait-group/main.go
  function listing1 (line 9) | func listing1() {
  function listing2 (line 25) | func listing2() {
  function listing3 (line 41) | func listing3() {

FILE: src/09-concurrency-practice/72-cond/main.go
  function listing1 (line 9) | func listing1() {
  function listing2 (line 40) | func listing2() {
  function listing3 (line 68) | func listing3() {

FILE: src/09-concurrency-practice/73-errgroup/main.go
  function handler1 (line 10) | func handler1(ctx context.Context, circles []Circle) ([]Result, error) {
  function handler2 (line 34) | func handler2(ctx context.Context, circles []Circle) ([]Result, error) {
  function foo (line 57) | func foo(context.Context, Circle) (Result, error) {
  type Circle (line 62) | type Circle struct
  type Result (line 63) | type Result struct

FILE: src/09-concurrency-practice/74-copying-sync/main.go
  function main (line 8) | func main() {
  type Counter (line 21) | type Counter struct
    method Increment1 (line 30) | func (c Counter) Increment1(name string) {
    method Increment2 (line 36) | func (c *Counter) Increment2(name string) {
  function NewCounter (line 26) | func NewCounter() Counter {
  type Counter2 (line 40) | type Counter2 struct
  function NewCounter2 (line 45) | func NewCounter2() Counter2 {

FILE: src/10-standard-lib/75-wrong-time-duration/main.go
  function main (line 8) | func main() {
  function listing1 (line 13) | func listing1() {
  function listing2 (line 23) | func listing2() {

FILE: src/10-standard-lib/76-time-after/main.go
  function consumer1 (line 9) | func consumer1(ch <-chan Event) {
  function consumer2 (line 20) | func consumer2(ch <-chan Event) {
  function consumer3 (line 33) | func consumer3(ch <-chan Event) {
  type Event (line 48) | type Event struct
  function handle (line 50) | func handle(Event) {

FILE: src/10-standard-lib/77-json-handling/map-any/main.go
  function listing1 (line 5) | func listing1() error {
  function getMessage (line 15) | func getMessage() []byte {

FILE: src/10-standard-lib/77-json-handling/monotonic-clock/main.go
  type Event (line 9) | type Event struct
  function listing1 (line 13) | func listing1() error {
  function listing2 (line 34) | func listing2() error {

FILE: src/10-standard-lib/77-json-handling/type-embedding/main.go
  function main (line 9) | func main() {
  type Event1 (line 21) | type Event1 struct
  function listing1 (line 26) | func listing1() error {
  type Event2 (line 41) | type Event2 struct
  function listing2 (line 46) | func listing2() error {
  type Event3 (line 61) | type Event3 struct
    method MarshalJSON (line 66) | func (e Event3) MarshalJSON() ([]byte, error) {
  function listing3 (line 78) | func listing3() error {

FILE: src/10-standard-lib/78-sql/null-values/main.go
  function listing1 (line 5) | func listing1(db *sql.DB, id string) error {
  function listing2 (line 26) | func listing2(db *sql.DB, id string) error {
  function listing3 (line 47) | func listing3(db *sql.DB, id string) error {

FILE: src/10-standard-lib/78-sql/prepared-statements/main.go
  function listing1 (line 5) | func listing1(db *sql.DB, id string) error {

FILE: src/10-standard-lib/78-sql/rows-iterations-errors/main.go
  function get1 (line 9) | func get1(ctx context.Context, db *sql.DB, id string) (string, int, erro...
  function get2 (line 36) | func get2(ctx context.Context, db *sql.DB, id string) (string, int, erro...

FILE: src/10-standard-lib/78-sql/sql-open/main.go
  function listing1 (line 7) | func listing1() error {

FILE: src/10-standard-lib/79-closing-resources/http/main.go
  type handler (line 78) | type handler struct
    method getBody1 (line 9) | func (h handler) getBody1() (string, error) {
    method getBody2 (line 23) | func (h handler) getBody2() (string, error) {
    method getStatusCode1 (line 44) | func (h handler) getStatusCode1(body io.Reader) (int, error) {
    method getStatusCode2 (line 60) | func (h handler) getStatusCode2(body io.Reader) (int, error) {

FILE: src/10-standard-lib/79-closing-resources/os-file/main.go
  function listing1 (line 8) | func listing1(filename string) error {
  function writeToFile1 (line 23) | func writeToFile1(filename string, content []byte) (err error) {
  function writeToFile2 (line 40) | func writeToFile2(filename string, content []byte) (err error) {

FILE: src/10-standard-lib/79-closing-resources/sql-rows/main.go
  function listing1 (line 8) | func listing1() error {
  function listing2 (line 25) | func listing2() error {

FILE: src/10-standard-lib/80-http-return/main.go
  function handler1 (line 5) | func handler1(w http.ResponseWriter, req *http.Request) {
  function handler2 (line 15) | func handler2(w http.ResponseWriter, req *http.Request) {
  function foo (line 26) | func foo(req *http.Request) error {

FILE: src/10-standard-lib/81-default-http-client-server/client/main.go
  function main (line 9) | func main() {

FILE: src/10-standard-lib/81-default-http-client-server/server/main.go
  function main (line 8) | func main() {
  type handler (line 18) | type handler struct
    method ServeHTTP (line 20) | func (h handler) ServeHTTP(http.ResponseWriter, *http.Request) {}

FILE: src/11-testing/82-categorizing-tests/build-tags/db_test.go
  function TestInsert1 (line 11) | func TestInsert1(t *testing.T) {
  function TestInsert2 (line 15) | func TestInsert2(t *testing.T) {

FILE: src/11-testing/82-categorizing-tests/short-mode/main_test.go
  function TestLongRunning (line 5) | func TestLongRunning(t *testing.T) {

FILE: src/11-testing/85-table-driven-tests/main.go
  function removeNewLineSuffixes (line 5) | func removeNewLineSuffixes(s string) string {

FILE: src/11-testing/85-table-driven-tests/main_test.go
  function TestRemoveNewLineSuffix_Empty (line 5) | func TestRemoveNewLineSuffix_Empty(t *testing.T) {
  function TestRemoveNewLineSuffix_EndingWithCarriageReturnNewLine (line 13) | func TestRemoveNewLineSuffix_EndingWithCarriageReturnNewLine(t *testing....
  function TestRemoveNewLineSuffix_EndingWithNewLine (line 21) | func TestRemoveNewLineSuffix_EndingWithNewLine(t *testing.T) {
  function TestRemoveNewLineSuffix_EndingWithMultipleNewLines (line 29) | func TestRemoveNewLineSuffix_EndingWithMultipleNewLines(t *testing.T) {
  function TestRemoveNewLineSuffix_EndingWithoutNewLine (line 37) | func TestRemoveNewLineSuffix_EndingWithoutNewLine(t *testing.T) {
  function TestRemoveNewLineSuffix (line 45) | func TestRemoveNewLineSuffix(t *testing.T) {

FILE: src/11-testing/86-sleeping/main.go
  type Handler (line 3) | type Handler struct
    method getBestFoo (line 12) | func (h Handler) getBestFoo(someInputs int) Foo {
  type publisher (line 8) | type publisher interface
  function getFoos (line 26) | func getFoos(inputs int) []Foo {
  type Foo (line 30) | type Foo struct

FILE: src/11-testing/86-sleeping/main_test.go
  type publisherMock1 (line 9) | type publisherMock1 struct
    method Publish (line 14) | func (p *publisherMock1) Publish(got []Foo) {
    method Get (line 20) | func (p *publisherMock1) Get() []Foo {
  function TestGetBestFoo (line 26) | func TestGetBestFoo(t *testing.T) {
  function assert (line 43) | func assert(t *testing.T, assertion func() bool,
  type publisherMock2 (line 54) | type publisherMock2 struct
    method Publish (line 58) | func (p *publisherMock2) Publish(got []Foo) {
  function TestGetBestFoo2 (line 62) | func TestGetBestFoo2(t *testing.T) {

FILE: src/11-testing/87-time-api/listing1/main.go
  type Cache (line 8) | type Cache struct
    method TrimOlderThan (line 18) | func (c *Cache) TrimOlderThan(since time.Duration) {
    method Add (line 31) | func (c *Cache) Add(events []Event) {
    method GetAll (line 38) | func (c *Cache) GetAll() []Event {
  type Event (line 13) | type Event struct

FILE: src/11-testing/87-time-api/listing1/main_test.go
  function TestCache_TrimOlderThan (line 8) | func TestCache_TrimOlderThan(t *testing.T) {

FILE: src/11-testing/87-time-api/listing2/main.go
  type now (line 8) | type now
  type Cache (line 10) | type Cache struct
    method TrimOlderThan (line 28) | func (c *Cache) TrimOlderThan(since time.Duration) {
    method Add (line 41) | func (c *Cache) Add(events []Event) {
    method GetAll (line 48) | func (c *Cache) GetAll() []Event {
  function NewCache (line 16) | func NewCache() *Cache {
  type Event (line 23) | type Event struct

FILE: src/11-testing/87-time-api/listing2/main_test.go
  function TestCache_TrimOlderThan (line 8) | func TestCache_TrimOlderThan(t *testing.T) {
  function parseTime (line 22) | func parseTime(t *testing.T, timestamp string) time.Time {

FILE: src/11-testing/87-time-api/listing3/main.go
  type Cache (line 8) | type Cache struct
    method TrimOlderThan (line 18) | func (c *Cache) TrimOlderThan(now time.Time, since time.Duration) {
    method Add (line 31) | func (c *Cache) Add(events []Event) {
    method GetAll (line 38) | func (c *Cache) GetAll() []Event {
  type Event (line 13) | type Event struct

FILE: src/11-testing/87-time-api/listing3/main_test.go
  function TestCache_TrimOlderThan (line 8) | func TestCache_TrimOlderThan(t *testing.T) {
  function parseTime (line 24) | func parseTime(t *testing.T, timestamp string) time.Time {

FILE: src/11-testing/87-time-api/listing4/main.go
  type Cache (line 8) | type Cache struct
    method TrimOlderThan (line 18) | func (c *Cache) TrimOlderThan(t time.Time) {
    method Add (line 30) | func (c *Cache) Add(events []Event) {
    method GetAll (line 37) | func (c *Cache) GetAll() []Event {
  type Event (line 13) | type Event struct

FILE: src/11-testing/87-time-api/listing4/main_test.go
  function TestCache_TrimOlderThan (line 8) | func TestCache_TrimOlderThan(t *testing.T) {
  function parseTime (line 25) | func parseTime(t *testing.T, timestamp string) time.Time {

FILE: src/11-testing/88-utility-package/httptest/main.go
  function Handler (line 11) | func Handler(w http.ResponseWriter, r *http.Request) {
  type request (line 30) | type request struct
  function buildRequestBody (line 34) | func buildRequestBody(lat1, lng1, lat2, lng2 float64) io.Reader {
  type DurationClient (line 38) | type DurationClient struct
    method GetDuration (line 18) | func (c DurationClient) GetDuration(url string, lat1, lng1, lat2, lng2...
  function NewDurationClient (line 42) | func NewDurationClient() DurationClient {
  function parseResponseBody (line 48) | func parseResponseBody(r io.ReadCloser) (time.Duration, error) {

FILE: src/11-testing/88-utility-package/httptest/main_test.go
  function TestHandler (line 12) | func TestHandler(t *testing.T) {
  function TestDurationClientGet (line 32) | func TestDurationClientGet(t *testing.T) {

FILE: src/11-testing/88-utility-package/iotest/main.go
  type LowerCaseReader (line 7) | type LowerCaseReader struct
    method Read (line 11) | func (l LowerCaseReader) Read(p []byte) (int, error) {
  function foo1 (line 15) | func foo1(r io.Reader) error {
  function foo2 (line 26) | func foo2(r io.Reader) error {
  function readAll (line 37) | func readAll(r io.Reader, retries int) ([]byte, error) {

FILE: src/11-testing/88-utility-package/iotest/main_test.go
  function TestLowerCaseReader (line 9) | func TestLowerCaseReader(t *testing.T) {
  function TestFoo1 (line 19) | func TestFoo1(t *testing.T) {
  function TestFoo2 (line 28) | func TestFoo2(t *testing.T) {
  function randomString (line 37) | func randomString(i int) string {

FILE: src/11-testing/89-benchmark/compiler-optimizations/main.go
  constant m1 (line 4) | m1  = 0x5555555555555555
  constant m2 (line 5) | m2  = 0x3333333333333333
  constant m4 (line 6) | m4  = 0x0f0f0f0f0f0f0f0f
  constant h01 (line 7) | h01 = 0x0101010101010101
  function popcnt (line 10) | func popcnt(x uint64) uint64 {

FILE: src/11-testing/89-benchmark/compiler-optimizations/main_test.go
  function BenchmarkPopcnt1 (line 5) | func BenchmarkPopcnt1(b *testing.B) {
  function BenchmarkPopcnt2 (line 13) | func BenchmarkPopcnt2(b *testing.B) {

FILE: src/11-testing/89-benchmark/observer-effect/main.go
  function calculateSum512 (line 3) | func calculateSum512(s [][512]int64) int64 {
  function calculateSum513 (line 13) | func calculateSum513(s [][513]int64) int64 {

FILE: src/11-testing/89-benchmark/observer-effect/main_test.go
  constant rows (line 5) | rows = 1000
  function BenchmarkCalculateSum512_1 (line 9) | func BenchmarkCalculateSum512_1(b *testing.B) {
  function BenchmarkCalculateSum513_1 (line 19) | func BenchmarkCalculateSum513_1(b *testing.B) {
  function BenchmarkCalculateSum512_2 (line 29) | func BenchmarkCalculateSum512_2(b *testing.B) {
  function BenchmarkCalculateSum513_2 (line 40) | func BenchmarkCalculateSum513_2(b *testing.B) {
  function createMatrix512 (line 51) | func createMatrix512(r int) [][512]int64 {
  function createMatrix513 (line 55) | func createMatrix513(r int) [][513]int64 {

FILE: src/11-testing/89-benchmark/timer/main_test.go
  function BenchmarkFoo1 (line 5) | func BenchmarkFoo1(b *testing.B) {
  function BenchmarkFoo2 (line 13) | func BenchmarkFoo2(b *testing.B) {
  function functionUnderTest (line 22) | func functionUnderTest() {
  function expensiveSetup (line 25) | func expensiveSetup() {

FILE: src/11-testing/89-benchmark/wrong-assumptions/main_test.go
  function BenchmarkAtomicStoreInt32 (line 8) | func BenchmarkAtomicStoreInt32(b *testing.B) {
  function BenchmarkAtomicStoreInt64 (line 15) | func BenchmarkAtomicStoreInt64(b *testing.B) {

FILE: src/11-testing/90-testing-features/different-package/main.go
  function Inc (line 7) | func Inc() uint64 {

FILE: src/11-testing/90-testing-features/different-package/main_test.go
  function TestCount (line 9) | func TestCount(t *testing.T) {

FILE: src/11-testing/90-testing-features/setup-teardown/main_test.go
  function TestMySQLIntegration (line 9) | func TestMySQLIntegration(t *testing.T) {
  function createConnection (line 16) | func createConnection(t *testing.T, dsn string) *sql.DB {
  function TestMain (line 28) | func TestMain(m *testing.M) {
  function setupMySQL (line 35) | func setupMySQL() {}
  function teardownMySQL (line 37) | func teardownMySQL() {}

FILE: src/11-testing/90-testing-features/utility-function/main.go
  type Customer (line 3) | type Customer struct

FILE: src/11-testing/90-testing-features/utility-function/main_test.go
  function TestCustomer1 (line 8) | func TestCustomer1(t *testing.T) {
  function createCustomer1 (line 17) | func createCustomer1(someArg string) (Customer, error) {
  function TestCustomer2 (line 25) | func TestCustomer2(t *testing.T) {
  function createCustomer2 (line 31) | func createCustomer2(t *testing.T, someArg string) Customer {
  function customerFactory (line 39) | func customerFactory(someArg string) (Customer, error) {

FILE: src/12-optimizations/91-cpu-caches/cache-line/main.go
  function sum2 (line 3) | func sum2(s []int64) int64 {
  function sum8 (line 11) | func sum8(s []int64) int64 {

FILE: src/12-optimizations/91-cpu-caches/cache-line/main_test.go
  function BenchmarkSum2 (line 7) | func BenchmarkSum2(b *testing.B) {
  function BenchmarkSum8 (line 18) | func BenchmarkSum8(b *testing.B) {

FILE: src/12-optimizations/91-cpu-caches/predictability/main.go
  type node (line 3) | type node struct
  function linkedList (line 8) | func linkedList(n *node) int64 {
  function sum2 (line 17) | func sum2(s []int64) int64 {

FILE: src/12-optimizations/91-cpu-caches/predictability/main_test.go
  constant n (line 7) | n = 1_000_000
  function BenchmarkLinkedList (line 9) | func BenchmarkLinkedList(b *testing.B) {
  function BenchmarkSum2 (line 23) | func BenchmarkSum2(b *testing.B) {

FILE: src/12-optimizations/91-cpu-caches/slice-structs/main.go
  type Foo (line 3) | type Foo struct
  function sumFoo (line 8) | func sumFoo(foos []Foo) int64 {
  type Bar (line 16) | type Bar struct
  function sumBar (line 21) | func sumBar(bar Bar) int64 {

FILE: src/12-optimizations/91-cpu-caches/slice-structs/main_test.go
  constant n (line 7) | n = 1_000_000
  function BenchmarkSumFoo (line 9) | func BenchmarkSumFoo(b *testing.B) {
  function BenchmarkSumBar (line 20) | func BenchmarkSumBar(b *testing.B) {

FILE: src/12-optimizations/92-false-sharing/main.go
  type Input (line 5) | type Input struct
  type Result1 (line 10) | type Result1 struct
  function count1 (line 15) | func count1(inputs []Input) Result1 {
  type Result2 (line 39) | type Result2 struct
  function count2 (line 45) | func count2(inputs []Input) Result2 {

FILE: src/12-optimizations/92-false-sharing/main_test.go
  constant n (line 5) | n = 1_000_000
  function BenchmarkCount1 (line 9) | func BenchmarkCount1(b *testing.B) {
  function BenchmarkCount2 (line 22) | func BenchmarkCount2(b *testing.B) {

FILE: src/12-optimizations/93-instruction-level-parallelism/main.go
  constant n (line 3) | n = 1_000_000
  function add (line 5) | func add(s [2]int64) [2]int64 {
  function add2 (line 15) | func add2(s [2]int64) [2]int64 {

FILE: src/12-optimizations/93-instruction-level-parallelism/main_test.go
  function BenchmarkAdd (line 7) | func BenchmarkAdd(b *testing.B) {
  function BenchmarkAdd2 (line 16) | func BenchmarkAdd2(b *testing.B) {

FILE: src/12-optimizations/94-data-alignment/main.go
  type Foo1 (line 3) | type Foo1 struct
  function sum1 (line 9) | func sum1(foos []Foo1) int64 {
  type Foo2 (line 17) | type Foo2 struct
  function sum2 (line 23) | func sum2(foos []Foo2) int64 {

FILE: src/12-optimizations/94-data-alignment/main_test.go
  constant n (line 5) | n = 1_000_000
  function BenchmarkSum1 (line 9) | func BenchmarkSum1(b *testing.B) {
  function BenchmarkSum2 (line 19) | func BenchmarkSum2(b *testing.B) {

FILE: src/12-optimizations/95-stack-heap/main.go
  function listing1 (line 3) | func listing1() {
  function sumValue (line 12) | func sumValue(x, y int) int {
  function listing2 (line 17) | func listing2() {
  function sumPtr (line 26) | func sumPtr(x, y int) *int {
  function listing3 (line 31) | func listing3() {
  function sum (line 39) | func sum(x, y *int) int {

FILE: src/12-optimizations/95-stack-heap/main_test.go
  function BenchmarkSumValue (line 10) | func BenchmarkSumValue(b *testing.B) {
  function BenchmarkSumPtr (line 19) | func BenchmarkSumPtr(b *testing.B) {

FILE: src/12-optimizations/96-reduce-allocations/compiler/main.go
  type cache (line 3) | type cache struct
    method get1 (line 7) | func (c *cache) get1(bytes []byte) (v int, contains bool) {
    method get2 (line 13) | func (c *cache) get2(bytes []byte) (v int, contains bool) {

FILE: src/12-optimizations/96-reduce-allocations/sync-pool/main.go
  function write (line 15) | func write(w io.Writer) {
  function getResponse (line 27) | func getResponse([]byte) {
Condensed preview — 208 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,026K chars).
[
  {
    "path": ".gitattributes",
    "chars": 96,
    "preview": "*.html linguist-detectable=false\n*.js linguist-detectable=false\n*.css linguist-detectable=false\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 732,
    "preview": "# These are supported funding model platforms\n\ngithub: teivah\npatreon: # Replace with a single Patreon username\nopen_col"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/community_mistake.md",
    "chars": 235,
    "preview": "---\nname: Community mistake\nabout: Propose a new community mistake\ntitle: ''\nlabels: community mistake\n---\n\n**Describe t"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/erratum.md",
    "chars": 175,
    "preview": "---\nname: Erratum\nabout: Suggest a book correction\ntitle: ''\nlabels: erratum\n---\n\n**Describe the book error**\n\nDescribe "
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 667,
    "preview": "name: ci \non:\n  push:\n    branches:\n      - master \n      - main\npermissions:\n  contents: write\njobs:\n  deploy:\n    runs"
  },
  {
    "path": ".gitignore",
    "chars": 31,
    "preview": ".idea\n*.out\n100-go-mistakes.iml"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 820,
    "preview": "# Contributing to 100 Go Mistakes\n\nFirst of all, thank you for taking the time to contribute! 🎉\n\nWe aim to create a coll"
  },
  {
    "path": "LICENSE.md",
    "chars": 17384,
    "preview": "# Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International\n\nCreative Commons Corporation (“Creative Co"
  },
  {
    "path": "README.md",
    "chars": 346,
    "preview": "# 100 Go Mistakes and How to Avoid Them\n\nSource code and community space of 📖 _100 Go Mistakes and How to Avoid Them_, p"
  },
  {
    "path": "docs/20-slice.md",
    "chars": 7450,
    "preview": "---\ntitle: Not understanding slice length and capacity (#20)\ncomments: true\nhide:\n- toc\n---\n\n# Not understanding slice l"
  },
  {
    "path": "docs/28-maps-memory-leaks.md",
    "chars": 6694,
    "preview": "---\ntitle: Maps and memory leaks (#28)\ncomments: true\nhide:\n- toc\n---\n\n# Maps and memory leaks\n\n![](img/28-maps-memory-l"
  },
  {
    "path": "docs/5-interface-pollution.md",
    "chars": 13902,
    "preview": "---\ntitle: Interface pollution (#5)\ncomments: true\nhide:\n- toc\nstatus: new\n---\n\n# Interface pollution\n\n![](img/interface"
  },
  {
    "path": "docs/56-concurrency-faster.md",
    "chars": 14657,
    "preview": "---\ntitle: Thinking concurrency is always faster (#56)\ncomments: true\nhide:\n- toc\n---\n\n# Thinking concurrency is always "
  },
  {
    "path": "docs/89-benchmarks.md",
    "chars": 18130,
    "preview": "---\ntitle: Writing inaccurate benchmarks (#89)\ncomments: true\nhide:\n- toc\n---\n\n# Writing inaccurate benchmarks\n\n![](img/"
  },
  {
    "path": "docs/9-generics.md",
    "chars": 9611,
    "preview": "---\ntitle: Being confused about when to use generics (#9)\ncomments: true\nhide:\n- toc\n---\n\n# Being confused about when to"
  },
  {
    "path": "docs/92-false-sharing.md",
    "chars": 6273,
    "preview": "---\ntitle: Writing concurrent code that leads to false sharing (#92)\ncomments: true\nhide:\n- toc\nstatus: new\n---\n\n# Writi"
  },
  {
    "path": "docs/98-profiling-execution-tracing.md",
    "chars": 19213,
    "preview": "---\ntitle: Not using Go diagnostics tooling (#98)\ncomments: true\nhide:\n- toc\n---\n\n# Not using Go diagnostics tooling\n\n!["
  },
  {
    "path": "docs/CNAME",
    "chars": 9,
    "preview": "100go.co\n"
  },
  {
    "path": "docs/book.md",
    "chars": 3584,
    "preview": "---\nhide:\n- toc\n---\n\n# 100 Go Mistakes and How to Avoid Them\n\n![](img/cover.png)\n\n## Description\n\nIf you're a Go develop"
  },
  {
    "path": "docs/chapter-1.md",
    "chars": 14387,
    "preview": "---\ntitle: Read the First Chapter\nhide:\n- toc\n---\n\n# Go: Simple to learn but hard to master\n\nThis chapter covers\n\n* What"
  },
  {
    "path": "docs/external.md",
    "chars": 3421,
    "preview": "# External Resources\n\n## English\n\n### The Best Golang Book | Prime Reacts\n\n<iframe width=\"560\" height=\"315\" src=\"https:/"
  },
  {
    "path": "docs/index.md",
    "chars": 123440,
    "preview": "---\ncomments: true\ndescription: Summary of the mistakes in the 100 Go Mistakes book.\nstatus: new\n---\n\n# Common Go Mistak"
  },
  {
    "path": "docs/ja.md",
    "chars": 72069,
    "preview": "---\r\ntitle: Japanese Version\r\ncomments: true\r\n---\r\n\r\n# Go言語でありがちな間違い\r\n\r\n???+ tip \"The Coder Cafe\"\r\n\r\n    もし私の本を楽しんでいただけた"
  },
  {
    "path": "docs/pt-br.md",
    "chars": 129156,
    "preview": "---\ntitle: Brazilian Portuguese Version\ncomments: true\n---\n\n# Erros comuns de Go\n\n???+ tip \"The Coder Cafe\"\n\n    Se você"
  },
  {
    "path": "docs/stylesheets/extra.css",
    "chars": 114,
    "preview": ".md-typeset figure img {\n  display: inline;\n}\n\n.md-typeset .admonition,\n.md-typeset details {\n  font-size: 15px\n}\n"
  },
  {
    "path": "docs/zh.md",
    "chars": 15268,
    "preview": "---\ntitle: Chinese (Simplified) Version\ncomments: true\n---\n\n# 100 个 Go 常见错误及如何避免\n\n???+ tip \"The Coder Cafe\"\n\n    如果您喜欢我的"
  },
  {
    "path": "go.mod",
    "chars": 112,
    "preview": "module github.com/teivah/100-go-mistakes\n\ngo 1.18\n\nrequire golang.org/x/sync v0.0.0-20210220032951-036812b2e83c\n"
  },
  {
    "path": "go.sum",
    "chars": 209,
    "preview": "golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=\ngolang.org/x/sync v"
  },
  {
    "path": "includes/abbreviations.md",
    "chars": 211,
    "preview": "*[GC]: Garbage Collector\n*[ILP]: Instruction-Level Parallelism\n*[CFS]: Completely Fair Scheduler\n*[GOMAXPROCS]: The vari"
  },
  {
    "path": "justfile",
    "chars": 81,
    "preview": "build:\n  mkdocs build\nserve:\n  mkdocs serve\ntest:\n  mkdocs build && mkdocs serve\n"
  },
  {
    "path": "mkdocs.yml",
    "chars": 2851,
    "preview": "site_name: 100 Go Mistakes and How to Avoid Them\nsite_url: https://100go.co\nrepo_name: teivah/100-go-mistakes\ntheme:\n  n"
  },
  {
    "path": "overrides/main.html",
    "chars": 26,
    "preview": "{% extends \"base.html\" %}\n"
  },
  {
    "path": "overrides/partials/comments.html",
    "chars": 1775,
    "preview": "{% if page.meta.comments %}\n<h2 id=\"__comments\">{{ lang.t(\"meta.comments\") }}</h2>\n<!-- Insert generated snippet here --"
  },
  {
    "path": "site/20-slice/index.html",
    "chars": 47977,
    "preview": "<!DOCTYPE html><html lang=\"en\" class=\"no-js\"><head>\n    \n      <meta charset=\"utf-8\">\n      <meta name=\"viewport\" conten"
  },
  {
    "path": "site/28-maps-memory-leaks/index.html",
    "chars": 49512,
    "preview": "<!DOCTYPE html><html lang=\"en\" class=\"no-js\"><head>\n    \n      <meta charset=\"utf-8\">\n      <meta name=\"viewport\" conten"
  },
  {
    "path": "site/404.html",
    "chars": 28559,
    "preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n  <head>\n    \n      <meta charset=\"utf-8\">\n      <meta name=\"viewport\" c"
  },
  {
    "path": "site/5-interface-pollution/index.html",
    "chars": 75906,
    "preview": "<!DOCTYPE html><html lang=\"en\" class=\"no-js\"><head>\n    \n      <meta charset=\"utf-8\">\n      <meta name=\"viewport\" conten"
  },
  {
    "path": "site/56-concurrency-faster/index.html",
    "chars": 69132,
    "preview": "<!DOCTYPE html><html lang=\"en\" class=\"no-js\"><head>\n    \n      <meta charset=\"utf-8\">\n      <meta name=\"viewport\" conten"
  },
  {
    "path": "site/89-benchmarks/index.html",
    "chars": 98492,
    "preview": "<!DOCTYPE html><html lang=\"en\" class=\"no-js\"><head>\n    \n      <meta charset=\"utf-8\">\n      <meta name=\"viewport\" conten"
  },
  {
    "path": "site/9-generics/index.html",
    "chars": 72382,
    "preview": "<!DOCTYPE html><html lang=\"en\" class=\"no-js\"><head>\n    \n      <meta charset=\"utf-8\">\n      <meta name=\"viewport\" conten"
  },
  {
    "path": "site/92-false-sharing/index.html",
    "chars": 50007,
    "preview": "<!DOCTYPE html><html lang=\"en\" class=\"no-js\"><head>\n    \n      <meta charset=\"utf-8\">\n      <meta name=\"viewport\" conten"
  },
  {
    "path": "site/98-profiling-execution-tracing/index.html",
    "chars": 69657,
    "preview": "<!DOCTYPE html><html lang=\"en\" class=\"no-js\"><head>\n    \n      <meta charset=\"utf-8\">\n      <meta name=\"viewport\" conten"
  },
  {
    "path": "site/CNAME",
    "chars": 9,
    "preview": "100go.co\n"
  },
  {
    "path": "site/assets/javascripts/lunr/tinyseg.js",
    "chars": 19942,
    "preview": "/**\n * export the module via AMD, CommonJS or as a browser global\n * Export code from https://github.com/umdjs/umd/blob/"
  },
  {
    "path": "site/assets/javascripts/lunr/wordcut.js",
    "chars": 386233,
    "preview": "(function(f){if(typeof exports===\"object\"&&typeof module!==\"undefined\"){module.exports=f()}else if(typeof define===\"func"
  },
  {
    "path": "site/book/index.html",
    "chars": 39324,
    "preview": "<!DOCTYPE html><html lang=\"en\" class=\"no-js\"><head>\n    \n      <meta charset=\"utf-8\">\n      <meta name=\"viewport\" conten"
  },
  {
    "path": "site/chapter-1/index.html",
    "chars": 52408,
    "preview": "<!DOCTYPE html><html lang=\"en\" class=\"no-js\"><head>\n    \n      <meta charset=\"utf-8\">\n      <meta name=\"viewport\" conten"
  },
  {
    "path": "site/external/index.html",
    "chars": 45143,
    "preview": "<!DOCTYPE html><html lang=\"en\" class=\"no-js\"><head>\n    \n      <meta charset=\"utf-8\">\n      <meta name=\"viewport\" conten"
  },
  {
    "path": "site/index.html",
    "chars": 465399,
    "preview": "<!DOCTYPE html><html lang=\"en\" class=\"no-js\"><head>\n    \n      <meta charset=\"utf-8\">\n      <meta name=\"viewport\" conten"
  },
  {
    "path": "site/ja/index.html",
    "chars": 338436,
    "preview": "<!DOCTYPE html><html lang=\"en\" class=\"no-js\"><head>\n    \n      <meta charset=\"utf-8\">\n      <meta name=\"viewport\" conten"
  },
  {
    "path": "site/pt-br/index.html",
    "chars": 477102,
    "preview": "<!DOCTYPE html><html lang=\"en\" class=\"no-js\"><head>\n    \n      <meta charset=\"utf-8\">\n      <meta name=\"viewport\" conten"
  },
  {
    "path": "site/search/search_index.json",
    "chars": 721169,
    "preview": "{\"config\":{\"lang\":[\"en\"],\"separator\":\"[\\\\s\\\\-]+\",\"pipeline\":[\"stopWordFilter\"]},\"docs\":[{\"location\":\"\",\"title\":\"Common G"
  },
  {
    "path": "site/sitemap.xml",
    "chars": 1762,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n    <url>\n         <"
  },
  {
    "path": "site/stylesheets/extra.css",
    "chars": 114,
    "preview": ".md-typeset figure img {\n  display: inline;\n}\n\n.md-typeset .admonition,\n.md-typeset details {\n  font-size: 15px\n}\n"
  },
  {
    "path": "site/zh/index.html",
    "chars": 95848,
    "preview": "<!DOCTYPE html><html lang=\"en\" class=\"no-js\"><head>\n    \n      <meta charset=\"utf-8\">\n      <meta name=\"viewport\" conten"
  },
  {
    "path": "src/02-code-project-organization/1-variable-shadowing/main.go",
    "chars": 1368,
    "preview": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\t_ = listing1()\n\t_ = listing2()\n\t_ = listing3()\n\t_ = listing4"
  },
  {
    "path": "src/02-code-project-organization/10-type-embedding/main.go",
    "chars": 678,
    "preview": "package main\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"sync\"\n)\n\ntype Foo struct {\n\tBar\n}\n\ntype Bar struct {\n\tBaz int\n}\n\nfunc fooBar() {\n\tf"
  },
  {
    "path": "src/02-code-project-organization/11-functional-options/builder/main.go",
    "chars": 973,
    "preview": "package main\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n)\n\nconst defaultHTTPPort = 8080\n\ntype Config struct {\n\tPort int\n}\n\ntype Con"
  },
  {
    "path": "src/02-code-project-organization/11-functional-options/config-struct/main.go",
    "chars": 143,
    "preview": "package main\n\ntype Config struct {\n\tPort int\n}\n\nfunc NewServer(addr string, cfg Config) {\n}\n\nfunc main() {\n\tNewServer(\"l"
  },
  {
    "path": "src/02-code-project-organization/11-functional-options/functional-options/main.go",
    "chars": 1019,
    "preview": "package main\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n)\n\nconst defaultHTTPPort = 8080\n\ntype options struct {\n\tport *int\n}\n\ntype O"
  },
  {
    "path": "src/02-code-project-organization/13-utility-packages/stringset.go",
    "chars": 133,
    "preview": "package stringset\n\ntype Set map[string]struct{}\n\nfunc New(...string) Set { return nil }\n\nfunc (s Set) Sort() []string { "
  },
  {
    "path": "src/02-code-project-organization/2-nested-code/main.go",
    "chars": 803,
    "preview": "package main\n\nimport \"errors\"\n\nfunc join1(s1, s2 string, max int) (string, error) {\n\tif s1 == \"\" {\n\t\treturn \"\", errors.N"
  },
  {
    "path": "src/02-code-project-organization/3-init-functions/db/main.go",
    "chars": 503,
    "preview": "package main\n\nimport (\n\t\"database/sql\"\n\t\"log\"\n\t\"os\"\n)\n\nvar db *sql.DB\n\nfunc init() {\n\tdataSourceName := os.Getenv(\"MYSQL"
  },
  {
    "path": "src/02-code-project-organization/3-init-functions/main/main.go",
    "chars": 266,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/teivah/100-go-mistakes/src/02-code-project-organization/3-init-functions/red"
  },
  {
    "path": "src/02-code-project-organization/3-init-functions/redis/redis.go",
    "chars": 120,
    "preview": "package redis\n\nimport \"fmt\"\n\nfunc init() {\n\tfmt.Println(\"redis\")\n}\n\nfunc Store(key, value string) error {\n\treturn nil\n}\n"
  },
  {
    "path": "src/02-code-project-organization/5-interface-pollution/copy/main.go",
    "chars": 192,
    "preview": "package main\n\nimport \"io\"\n\nfunc copySourceToDest(source io.Reader, dest io.Writer) error {\n\tb, err := io.ReadAll(source)"
  },
  {
    "path": "src/02-code-project-organization/5-interface-pollution/copy/main_test.go",
    "chars": 367,
    "preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestCopySourceToDest(t *testing.T) {\n\tconst input = \"foo\"\n"
  },
  {
    "path": "src/02-code-project-organization/5-interface-pollution/decoupling/with.go",
    "chars": 275,
    "preview": "package main\n\ntype customerStorer interface {\n\tStoreCustomer(Customer) error\n}\n\ntype CustomerService2 struct {\n\tstorer c"
  },
  {
    "path": "src/02-code-project-organization/5-interface-pollution/decoupling/without.go",
    "chars": 325,
    "preview": "package main\n\ntype CustomerService struct {\n\tstore Store\n}\n\nfunc (cs CustomerService) CreateNewCustomer(id string) error"
  },
  {
    "path": "src/02-code-project-organization/5-interface-pollution/restricting-behavior/main.go",
    "chars": 473,
    "preview": "package main\n\ntype IntConfig struct {\n\tvalue int\n}\n\nfunc (c *IntConfig) Get() int {\n\treturn c.value\n}\n\nfunc (c *IntConfi"
  },
  {
    "path": "src/02-code-project-organization/6-interface-producer/client/client.go",
    "chars": 200,
    "preview": "package client\n\nimport \"github.com/teivah/100-go-mistakes/src/02-code-project-organization/6-interface-producer/store\"\n\n"
  },
  {
    "path": "src/02-code-project-organization/6-interface-producer/store/store.go",
    "chars": 342,
    "preview": "package store\n\ntype CustomerStorage interface {\n\tStoreCustomer(customer Customer) error\n\tGetCustomer(id string) (Custome"
  },
  {
    "path": "src/02-code-project-organization/8-any/main.go",
    "chars": 133,
    "preview": "package main\n\nfunc main() {\n\tvar i any\n\n\ti = 42\n\ti = \"foo\"\n\ti = struct {\n\t\ts string\n\t}{\n\t\ts: \"bar\",\n\t}\n\ti = f\n\n\t_ = i\n}\n"
  },
  {
    "path": "src/02-code-project-organization/8-any/store/after.go",
    "chars": 348,
    "preview": "package store\n\nfunc (s *Store) GetContract(id string) (Contract, error) {\n\treturn Contract{}, nil\n}\n\nfunc (s *Store) Set"
  },
  {
    "path": "src/02-code-project-organization/8-any/store/before.go",
    "chars": 246,
    "preview": "package store\n\ntype Customer struct {\n\t// Some fields\n}\n\ntype Contract struct {\n\t// Some fields\n}\n\ntype Store struct{}\n\n"
  },
  {
    "path": "src/02-code-project-organization/9-generics/main.go",
    "chars": 1139,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n)\n\nfunc getKeys(m any) ([]any, error) {\n\tswitch t := m.(type) {\n\tdefault:\n\t\treturn"
  },
  {
    "path": "src/03-data-types/17-octal-literals/main.go",
    "chars": 81,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tsum := 100 + 0o10\n\tfmt.Println(sum)\n}\n"
  },
  {
    "path": "src/03-data-types/18-integer-overflows/main.go",
    "chars": 740,
    "preview": "package main\n\nimport \"math\"\n\nfunc Inc32(counter int32) int32 {\n\tif counter == math.MaxInt32 {\n\t\tpanic(\"int32 overflow\")\n"
  },
  {
    "path": "src/03-data-types/19-floating-points/main.go",
    "chars": 432,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar n float32 = 1.0001\n\tfmt.Println(n * n)\n\n\tvar a float64\n\tpositiveInf := 1 "
  },
  {
    "path": "src/03-data-types/20-slice-length-cap/main.go",
    "chars": 432,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\ts := make([]int, 3, 6)\n\tprint(s)\n\n\ts[1] = 1\n\tprint(s)\n\n\ts = append(s, 2)\n\tpri"
  },
  {
    "path": "src/03-data-types/21-slice-init/main.go",
    "chars": 581,
    "preview": "package main\n\nfunc convertEmptySlice(foos []Foo) []Bar {\n\tbars := make([]Bar, 0)\n\n\tfor _, foo := range foos {\n\t\tbars = a"
  },
  {
    "path": "src/03-data-types/21-slice-init/main_test.go",
    "chars": 648,
    "preview": "package main\n\nimport \"testing\"\n\nconst n = 1_000_000\n\nvar global []Bar\n\nfunc BenchmarkConvert_EmptySlice(b *testing.B) {\n"
  },
  {
    "path": "src/03-data-types/22-nil-empty-slice/json/main.go",
    "chars": 423,
    "preview": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\nfunc main() {\n\tvar s1 []float32\n\tcustomer1 := customer{\n\t\tID:         "
  },
  {
    "path": "src/03-data-types/22-nil-empty-slice/slice-init/main.go",
    "chars": 267,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tvar s []string\n\tlog(1, s)\n\n\ts = []string(nil)\n\tlog(2, s)\n\n\ts = []string{"
  },
  {
    "path": "src/03-data-types/23-checking-slice-empty/main.go",
    "chars": 464,
    "preview": "package main\n\nfunc handleOperations1(id string) {\n\toperations := getOperations(id)\n\tif operations != nil {\n\t\thandle(oper"
  },
  {
    "path": "src/03-data-types/24-slice-copy/main.go",
    "chars": 295,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc bad() {\n\tsrc := []int{0, 1, 2}\n\tvar dst []int\n\tcopy(dst, src)\n\tfmt.Println(dst)\n\n\t_ = s"
  },
  {
    "path": "src/03-data-types/25-slice-append/main.go",
    "chars": 405,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tlisting1()\n\tlisting2()\n\tlisting3()\n}\n\nfunc listing1() {\n\ts := []int{1, 2, 3}\n"
  },
  {
    "path": "src/03-data-types/26-slice-memory-leak/capacity-leak/main.go",
    "chars": 560,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n)\n\nfunc consumeMessages() {\n\tfor {\n\t\tmsg := receiveMessage()\n\t\t// Do something "
  },
  {
    "path": "src/03-data-types/26-slice-memory-leak/slice-pointers/main.go",
    "chars": 750,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n)\n\ntype Foo struct {\n\tv []byte\n}\n\nfunc main() {\n\tfoos := make([]Foo, 1_000)\n\tpr"
  },
  {
    "path": "src/03-data-types/27-map-init/main_test.go",
    "chars": 491,
    "preview": "package main\n\nimport \"testing\"\n\nconst n = 1_000_000\n\nvar global map[int]struct{}\n\nfunc BenchmarkMapWithoutSize(b *testin"
  },
  {
    "path": "src/03-data-types/28-map-memory-leak/main.go",
    "chars": 502,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n)\n\nfunc main() {\n\t// Init\n\tn := 1_000_000\n\tm := make(map[int][128]byte)\n\tprintA"
  },
  {
    "path": "src/03-data-types/29-comparing-values/main.go",
    "chars": 1093,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n)\n\ntype customer1 struct {\n\tid string\n}\n\ntype customer2 struct {\n\tid         st"
  },
  {
    "path": "src/04-control-structures/30-range-loop-element-copied/concepts/main.go",
    "chars": 200,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\ts := []string{\"a\", \"b\", \"c\"}\n\tfor i, v := range s {\n\t\tfmt.Printf(\"index=%d, v"
  },
  {
    "path": "src/04-control-structures/30-range-loop-element-copied/value-copy/main.go",
    "chars": 1108,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\ntype account struct {\n\tbalance float32\n}\n\nfunc main() {\n\taccounts := createA"
  },
  {
    "path": "src/04-control-structures/31-range-loop-arg-evaluation/arrays/main.go",
    "chars": 379,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc listing1() {\n\ta := [3]int{0, 1, 2}\n\tfor i, v := range a {\n\t\ta[2] = 10\n\t\tif i == 2 {\n\t\t\t"
  },
  {
    "path": "src/04-control-structures/31-range-loop-arg-evaluation/channels/main.go",
    "chars": 292,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tch1 := make(chan int, 3)\n\tgo func() {\n\t\tch1 <- 0\n\t\tch1 <- 1\n\t\tch1 <- 2\n\t\tclos"
  },
  {
    "path": "src/04-control-structures/31-range-loop-arg-evaluation/concepts/main.go",
    "chars": 173,
    "preview": "package main\n\nfunc main() {\n\ts1 := []int{0, 1, 2}\n\tfor range s1 {\n\t\ts1 = append(s1, 10)\n\t}\n\n\ts2 := []int{0, 1, 2}\n\tfor i"
  },
  {
    "path": "src/04-control-structures/32-range-loop-pointers/concepts/main.go",
    "chars": 439,
    "preview": "package concepts\n\ntype Store struct {\n\tm map[string]*Foo\n}\n\nfunc (s Store) Put(id string, foo *Foo) {\n\ts.m[id] = foo\n\t//"
  },
  {
    "path": "src/04-control-structures/32-range-loop-pointers/customer-store/main.go",
    "chars": 892,
    "preview": "package main\n\nimport \"fmt\"\n\ntype Customer struct {\n\tID      string\n\tBalance float64\n}\n\ntype Store struct {\n\tm map[string"
  },
  {
    "path": "src/04-control-structures/33-map-iteration/main.go",
    "chars": 498,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc listing1() {\n\tm := map[int]bool{\n\t\t0: true,\n\t\t1: false,\n\t\t2: true,\n\t}\n\n\tfor k, v := ran"
  },
  {
    "path": "src/04-control-structures/34-break/main.go",
    "chars": 591,
    "preview": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n)\n\nfunc listing1() {\n\tfor i := 0; i < 5; i++ {\n\t\tfmt.Printf(\"%d \", i)\n\n\t\tswitch"
  },
  {
    "path": "src/04-control-structures/35-defer-loop/main.go",
    "chars": 815,
    "preview": "package main\n\nimport \"os\"\n\nfunc readFiles1(ch <-chan string) error {\n\tfor path := range ch {\n\t\tfile, err := os.Open(path"
  },
  {
    "path": "src/05-strings/36-rune/main.go",
    "chars": 172,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\ts := \"hello\"\n\tfmt.Println(len(s))\n\n\ts = \"汉\"\n\tfmt.Println(len(s))\n\n\ts = string"
  },
  {
    "path": "src/05-strings/37-string-iteration/main.go",
    "chars": 497,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\ts := \"hêllo\"\n\tfor i := range s {\n\t\tfmt.Printf(\"position %d: %c\\n\", i, s[i])\n\t"
  },
  {
    "path": "src/05-strings/38-trim/main.go",
    "chars": 302,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc main() {\n\tfmt.Println(strings.TrimRight(\"123oxo\", \"xo\"))\n\n\tfmt.Println("
  },
  {
    "path": "src/05-strings/39-string-concat/main.go",
    "chars": 540,
    "preview": "package main\n\nimport \"strings\"\n\nfunc concat1(values []string) string {\n\ts := \"\"\n\tfor _, value := range values {\n\t\ts += v"
  },
  {
    "path": "src/05-strings/39-string-concat/main_test.go",
    "chars": 675,
    "preview": "package main\n\nimport \"testing\"\n\nvar global string\n\nfunc BenchmarkConcatV1(b *testing.B) {\n\tvar local string\n\ts := getInp"
  },
  {
    "path": "src/05-strings/40-string-conversion/main.go",
    "chars": 493,
    "preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"strings\"\n)\n\nfunc getBytes1(reader io.Reader) ([]byte, error) {\n\tb, err := io.Rea"
  },
  {
    "path": "src/05-strings/41-substring-memory-leak/main.go",
    "chars": 864,
    "preview": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc main() {\n\ts1 := \"Hello, World!\"\n\ts2 := s1[:5]\n\tfmt.Println(s2"
  },
  {
    "path": "src/06-functions-methods/42-receiver/pointer/main.go",
    "chars": 244,
    "preview": "package main\n\nimport \"fmt\"\n\ntype customer struct {\n\tbalance float64\n}\n\nfunc (c *customer) add(operation float64) {\n\tc.ba"
  },
  {
    "path": "src/06-functions-methods/42-receiver/struct-with-pointer/main.go",
    "chars": 303,
    "preview": "package main\n\nimport \"fmt\"\n\ntype customer struct {\n\tdata *data\n}\n\ntype data struct {\n\tbalance float64\n}\n\nfunc (c custome"
  },
  {
    "path": "src/06-functions-methods/42-receiver/value/main.go",
    "chars": 225,
    "preview": "package main\n\nimport \"fmt\"\n\ntype customer struct {\n\tbalance float64\n}\n\nfunc (c customer) add(v float64) {\n\tc.balance += "
  },
  {
    "path": "src/06-functions-methods/43-named-result-parameters/main.go",
    "chars": 507,
    "preview": "package main\n\nimport \"io\"\n\nfunc f(a int) (b int) {\n\tb = a\n\treturn\n}\n\ntype locator interface {\n\tgetCoordinates(address st"
  },
  {
    "path": "src/06-functions-methods/44-side-effects-named-result-parameters/main.go",
    "chars": 1049,
    "preview": "package main\n\nimport (\n\t\"context\"\n\t\"errors\"\n)\n\ntype loc struct{}\n\nfunc (l loc) getCoordinates1(ctx context.Context, addr"
  },
  {
    "path": "src/06-functions-methods/45-nil-receiver/main.go",
    "chars": 982,
    "preview": "package main\n\nimport (\n\t\"errors\"\n\t\"log\"\n\t\"strings\"\n)\n\ntype MultiError struct {\n\terrs []string\n}\n\nfunc (m *MultiError) Ad"
  },
  {
    "path": "src/06-functions-methods/46-function-input/main.go",
    "chars": 542,
    "preview": "package main\n\nimport (\n\t\"bufio\"\n\t\"io\"\n\t\"os\"\n)\n\nfunc countEmptyLinesInFile(filename string) (int, error) {\n\tfile, err := "
  },
  {
    "path": "src/06-functions-methods/46-function-input/main_test.go",
    "chars": 215,
    "preview": "package main\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestCountEmptyLines(t *testing.T) {\n\temptyLines, err := countEmptyL"
  },
  {
    "path": "src/06-functions-methods/47-defer-evaluation/args/main.go",
    "chars": 1389,
    "preview": "package main\n\nimport \"fmt\"\n\nconst (\n\tStatusSuccess  = \"success\"\n\tStatusErrorFoo = \"error_foo\"\n\tStatusErrorBar = \"error_b"
  },
  {
    "path": "src/06-functions-methods/47-defer-evaluation/receiver/pointer/main.go",
    "chars": 184,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\ts := &Struct{id: \"foo\"}\n\tdefer s.print()\n\ts.id = \"bar\"\n}\n\ntype Struct struct "
  },
  {
    "path": "src/06-functions-methods/47-defer-evaluation/receiver/value/main.go",
    "chars": 182,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\ts := Struct{id: \"foo\"}\n\tdefer s.print()\n\ts.id = \"bar\"\n}\n\ntype Struct struct {"
  },
  {
    "path": "src/07-error-management/48-panic/main.go",
    "chars": 333,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tfmt.Println(\"recover\", r)\n\t"
  },
  {
    "path": "src/07-error-management/49-error-wrapping/main.go",
    "chars": 714,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc bar() error {\n\treturn barError{}\n}\n\ntype barError struct{}\n\nfunc (b barError) Error() s"
  },
  {
    "path": "src/07-error-management/50-compare-error-type/main.go",
    "chars": 1839,
    "preview": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n)\n\ntype transientError struct {\n\terr error\n}\n\nfunc (t transientError"
  },
  {
    "path": "src/07-error-management/51-comparing-error-value/main.go",
    "chars": 336,
    "preview": "package main\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n)\n\nfunc listing1() {\n\terr := query()\n\tif err != nil {\n\t\tif err == sql.E"
  },
  {
    "path": "src/07-error-management/52-handling-error-twice/main.go",
    "chars": 1833,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n)\n\ntype Route struct{}\n\nfunc GetRoute1(srcLat, srcLng, dstLat, dstLng float32) (Rou"
  },
  {
    "path": "src/07-error-management/53-not-handling-error/main.go",
    "chars": 286,
    "preview": "package main\n\nimport \"errors\"\n\nfunc listing1() {\n\t// ...\n\n\tnotify()\n}\n\nfunc listing2() {\n\t// ...\n\n\t// Notifications are "
  },
  {
    "path": "src/07-error-management/54-defer-errors/main.go",
    "chars": 836,
    "preview": "package main\n\nimport (\n\t\"database/sql\"\n\t\"log\"\n)\n\nconst query = \"...\"\n\nfunc getBalance1(db *sql.DB, clientID string) (flo"
  },
  {
    "path": "src/08-concurrency-foundations/56-faster/main.go",
    "chars": 1388,
    "preview": "package main\n\nimport (\n\t\"sync\"\n)\n\nfunc sequentialMergesort(s []int) {\n\tif len(s) <= 1 {\n\t\treturn\n\t}\n\n\tmiddle := len(s) /"
  },
  {
    "path": "src/08-concurrency-foundations/56-faster/main_test.go",
    "chars": 1009,
    "preview": "package main\n\nimport (\n\t\"math/rand\"\n\t\"testing\"\n\t\"time\"\n)\n\nvar global []int\n\nfunc Benchmark_sequentialMergesort(b *testin"
  },
  {
    "path": "src/08-concurrency-foundations/58-races/memory-model/main.go",
    "chars": 633,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc listing1() {\n\ti := 0\n\tgo func() {\n\t\ti++\n\t}()\n}\n\nfunc listing2() {\n\ti := 0\n\tgo func() {\n"
  },
  {
    "path": "src/08-concurrency-foundations/58-races/races/main.go",
    "chars": 738,
    "preview": "package races\n\nimport (\n\t\"sync\"\n\t\"sync/atomic\"\n)\n\nfunc listing1() {\n\ti := 0\n\n\tgo func() {\n\t\ti++\n\t}()\n\n\tgo func() {\n\t\ti++"
  },
  {
    "path": "src/08-concurrency-foundations/59-workload-type/main.go",
    "chars": 1112,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"sync\"\n\t\"sync/atomic\"\n)\n\nfunc main() {\n\tres1, _ := read1(&dummyReader{})\n\tfmt.Print"
  },
  {
    "path": "src/08-concurrency-foundations/60-contexts/flight/flight.go",
    "chars": 67,
    "preview": "package flight\n\ntype Position struct {\n\tLat float32\n\tLng float32\n}\n"
  },
  {
    "path": "src/08-concurrency-foundations/60-contexts/main.go",
    "chars": 1024,
    "preview": "package main\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/teivah/100-go-mistakes/src/08-concurrency-foundation"
  },
  {
    "path": "src/09-concurrency-practice/61-inappropriate-context/main.go",
    "chars": 1491,
    "preview": "package main\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"time\"\n)\n\nfunc handler1(w http.ResponseWriter, r *http.Request) {\n\trespon"
  },
  {
    "path": "src/09-concurrency-practice/62-starting-goroutine/listing1/main.go",
    "chars": 197,
    "preview": "package main\n\nfunc main() {\n\tnewWatcher()\n\n\t// Run the application\n}\n\nfunc newWatcher() {\n\tw := watcher{}\n\tgo w.watch()\n"
  },
  {
    "path": "src/09-concurrency-practice/62-starting-goroutine/listing2/main.go",
    "chars": 329,
    "preview": "package main\n\nimport \"context\"\n\nfunc main() {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n"
  },
  {
    "path": "src/09-concurrency-practice/62-starting-goroutine/listing3/main.go",
    "chars": 291,
    "preview": "package main\n\nfunc main() {\n\tw := newWatcher()\n\tdefer w.close()\n\n\t// Run the application\n}\n\nfunc newWatcher() watcher {\n"
  },
  {
    "path": "src/09-concurrency-practice/63-goroutines-loop-variables/main.go",
    "chars": 365,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc listing1() {\n\ts := []int{1, 2, 3}\n\n\tfor _, i := range s {\n\t\tgo func() {\n\t\t\tfmt.Print(i)"
  },
  {
    "path": "src/09-concurrency-practice/64-select-behavior/main.go",
    "chars": 794,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tmessageCh := make(chan int, 10)\n\tdisconnectCh := make(chan struc"
  },
  {
    "path": "src/09-concurrency-practice/66-nil-channels/main.go",
    "chars": 1177,
    "preview": "package main\n\nfunc merge1(ch1, ch2 <-chan int) <-chan int {\n\tch := make(chan int, 1)\n\n\tgo func() {\n\t\tfor v := range ch1 "
  },
  {
    "path": "src/09-concurrency-practice/68-string-formatting/main.go",
    "chars": 974,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n)\n\nfunc main() {\n\tcustomer := Customer{}\n\t_ = customer.UpdateAge1(-1)\n\t_ = custome"
  },
  {
    "path": "src/09-concurrency-practice/69-data-race-append/main.go",
    "chars": 468,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc listing1() {\n\ts := make([]int, 1)\n\n\tgo func() {\n\t\ts1 := append(s, 1)\n\t\tfmt.Println(s1)\n"
  },
  {
    "path": "src/09-concurrency-practice/70-mutex-slices-maps/main.go",
    "chars": 1110,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n)\n\nfunc main() {\n\tc := Cache{\n\t\tbalances: make(map[string]float64),\n\t}\n\tc.AddBalan"
  },
  {
    "path": "src/09-concurrency-practice/71-wait-group/main.go",
    "chars": 622,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\t\"sync/atomic\"\n)\n\nfunc listing1() {\n\twg := sync.WaitGroup{}\n\tvar v uint64\n\n\tfor i "
  },
  {
    "path": "src/09-concurrency-practice/72-cond/main.go",
    "chars": 1541,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\t\"time\"\n)\n\nfunc listing1() {\n\ttype Donation struct {\n\t\tmu      sync.RWMutex\n\t\tbala"
  },
  {
    "path": "src/09-concurrency-practice/73-errgroup/main.go",
    "chars": 1031,
    "preview": "package main\n\nimport (\n\t\"context\"\n\t\"sync\"\n\n\t\"golang.org/x/sync/errgroup\"\n)\n\nfunc handler1(ctx context.Context, circles ["
  },
  {
    "path": "src/09-concurrency-practice/74-copying-sync/main.go",
    "chars": 718,
    "preview": "package main\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\nfunc main() {\n\tcounter := NewCounter()\n\n\tgo func() {\n\t\tcounter.Increment1(\"foo"
  },
  {
    "path": "src/10-standard-lib/75-wrong-time-duration/main.go",
    "chars": 335,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tlisting1()\n\t//listing2()\n}\n\nfunc listing1() {\n\tticker := time.Ne"
  },
  {
    "path": "src/10-standard-lib/76-time-after/main.go",
    "chars": 797,
    "preview": "package main\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"time\"\n)\n\nfunc consumer1(ch <-chan Event) {\n\tfor {\n\t\tselect {\n\t\tcase event := "
  },
  {
    "path": "src/10-standard-lib/77-json-handling/map-any/main.go",
    "chars": 222,
    "preview": "package main\n\nimport \"encoding/json\"\n\nfunc listing1() error {\n\tb := getMessage()\n\tvar m map[string]any\n\terr := json.Unma"
  },
  {
    "path": "src/10-standard-lib/77-json-handling/monotonic-clock/main.go",
    "chars": 652,
    "preview": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\ntype Event struct {\n\tTime time.Time\n}\n\nfunc listing1() error {"
  },
  {
    "path": "src/10-standard-lib/77-json-handling/type-embedding/main.go",
    "chars": 1077,
    "preview": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tif err := listing1(); err != nil {\n\t\tpanic(err)"
  },
  {
    "path": "src/10-standard-lib/78-sql/null-values/main.go",
    "chars": 1056,
    "preview": "package main\n\nimport \"database/sql\"\n\nfunc listing1(db *sql.DB, id string) error {\n\trows, err := db.Query(\"SELECT DEP, AG"
  },
  {
    "path": "src/10-standard-lib/78-sql/prepared-statements/main.go",
    "chars": 262,
    "preview": "package main\n\nimport \"database/sql\"\n\nfunc listing1(db *sql.DB, id string) error {\n\tstmt, err := db.Prepare(\"SELECT * FRO"
  },
  {
    "path": "src/10-standard-lib/78-sql/rows-iterations-errors/main.go",
    "chars": 1112,
    "preview": "package main\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"log\"\n)\n\nfunc get1(ctx context.Context, db *sql.DB, id string) (strin"
  },
  {
    "path": "src/10-standard-lib/78-sql/sql-open/main.go",
    "chars": 218,
    "preview": "package main\n\nimport \"database/sql\"\n\nvar dsn = \"\"\n\nfunc listing1() error {\n\tdb, err := sql.Open(\"mysql\", dsn)\n\tif err !="
  },
  {
    "path": "src/10-standard-lib/79-closing-resources/http/main.go",
    "chars": 1350,
    "preview": "package main\n\nimport (\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc (h handler) getBody1() (string, error) {\n\tresp, err := h.client."
  },
  {
    "path": "src/10-standard-lib/79-closing-resources/os-file/main.go",
    "chars": 897,
    "preview": "package main\n\nimport (\n\t\"log\"\n\t\"os\"\n)\n\nfunc listing1(filename string) error {\n\tf, err := os.OpenFile(filename, os.O_APPE"
  },
  {
    "path": "src/10-standard-lib/79-closing-resources/sql-rows/main.go",
    "chars": 647,
    "preview": "package main\n\nimport (\n\t\"database/sql\"\n\t\"log\"\n)\n\nfunc listing1() error {\n\tdb, err := sql.Open(\"postgres\", dataSourceName"
  },
  {
    "path": "src/10-standard-lib/80-http-return/main.go",
    "chars": 542,
    "preview": "package main\n\nimport \"net/http\"\n\nfunc handler1(w http.ResponseWriter, req *http.Request) {\n\terr := foo(req)\n\tif err != n"
  },
  {
    "path": "src/10-standard-lib/81-default-http-client-server/client/main.go",
    "chars": 325,
    "preview": "package main\n\nimport (\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n)\n\nfunc main() {\n\tclient := &http.Client{\n\t\tTimeout: 5 * time.Second,\n"
  },
  {
    "path": "src/10-standard-lib/81-default-http-client-server/server/main.go",
    "chars": 375,
    "preview": "package main\n\nimport (\n\t\"net/http\"\n\t\"time\"\n)\n\nfunc main() {\n\ts := &http.Server{\n\t\tAddr:              \":8080\",\n\t\tReadHead"
  },
  {
    "path": "src/11-testing/82-categorizing-tests/build-tags/db_test.go",
    "chars": 257,
    "preview": "//go:build integration\n// +build integration\n\npackage db\n\nimport (\n\t\"os\"\n\t\"testing\"\n)\n\nfunc TestInsert1(t *testing.T) {\n"
  },
  {
    "path": "src/11-testing/82-categorizing-tests/short-mode/main_test.go",
    "chars": 143,
    "preview": "package main\n\nimport \"testing\"\n\nfunc TestLongRunning(t *testing.T) {\n\tif testing.Short() {\n\t\tt.Skip(\"skipping long-runni"
  },
  {
    "path": "src/11-testing/85-table-driven-tests/main.go",
    "chars": 282,
    "preview": "package main\n\nimport \"strings\"\n\nfunc removeNewLineSuffixes(s string) string {\n\tif s == \"\" {\n\t\treturn s\n\t}\n\tif strings.Ha"
  },
  {
    "path": "src/11-testing/85-table-driven-tests/main_test.go",
    "chars": 1594,
    "preview": "package main\n\nimport \"testing\"\n\nfunc TestRemoveNewLineSuffix_Empty(t *testing.T) {\n\tgot := removeNewLineSuffixes(\"\")\n\tex"
  },
  {
    "path": "src/11-testing/86-sleeping/main.go",
    "chars": 408,
    "preview": "package main\n\ntype Handler struct {\n\tn         int\n\tpublisher publisher\n}\n\ntype publisher interface {\n\tPublish([]Foo)\n}\n"
  },
  {
    "path": "src/11-testing/86-sleeping/main_test.go",
    "chars": 1160,
    "preview": "package main\n\nimport (\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n)\n\ntype publisherMock1 struct {\n\tmu  sync.RWMutex\n\tgot []Foo\n}\n\nfunc ("
  },
  {
    "path": "src/11-testing/87-time-api/listing1/main.go",
    "chars": 615,
    "preview": "package listing1\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\ntype Cache struct {\n\tmu     sync.RWMutex\n\tevents []Event\n}\n\ntype Event str"
  },
  {
    "path": "src/11-testing/87-time-api/listing1/main_test.go",
    "chars": 487,
    "preview": "package listing1\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestCache_TrimOlderThan(t *testing.T) {\n\tevents := []Event{\n\t\t{Tim"
  },
  {
    "path": "src/11-testing/87-time-api/listing2/main.go",
    "chars": 749,
    "preview": "package listing1\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\ntype now func() time.Time\n\ntype Cache struct {\n\tmu     sync.RWMutex\n\tevent"
  },
  {
    "path": "src/11-testing/87-time-api/listing2/main_test.go",
    "chars": 607,
    "preview": "package listing1\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestCache_TrimOlderThan(t *testing.T) {\n\tevents := []Event{\n\t\t{Tim"
  },
  {
    "path": "src/11-testing/87-time-api/listing3/main.go",
    "chars": 625,
    "preview": "package listing1\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\ntype Cache struct {\n\tmu     sync.RWMutex\n\tevents []Event\n}\n\ntype Event str"
  },
  {
    "path": "src/11-testing/87-time-api/listing3/main_test.go",
    "chars": 685,
    "preview": "package listing1\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestCache_TrimOlderThan(t *testing.T) {\n\tevents := []Event{\n\t\t{Tim"
  },
  {
    "path": "src/11-testing/87-time-api/listing4/main.go",
    "chars": 580,
    "preview": "package listing1\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\ntype Cache struct {\n\tmu     sync.RWMutex\n\tevents []Event\n}\n\ntype Event str"
  },
  {
    "path": "src/11-testing/87-time-api/listing4/main_test.go",
    "chars": 695,
    "preview": "package listing1\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestCache_TrimOlderThan(t *testing.T) {\n\tevents := []Event{\n\t\t{Tim"
  },
  {
    "path": "src/11-testing/88-utility-package/httptest/main.go",
    "chars": 1172,
    "preview": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc Handler(w http.ResponseWriter, r *h"
  },
  {
    "path": "src/11-testing/88-utility-package/httptest/main_test.go",
    "chars": 1066,
    "preview": "package main\n\nimport (\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestHandler(t"
  },
  {
    "path": "src/11-testing/88-utility-package/iotest/main.go",
    "chars": 728,
    "preview": "package main\n\nimport (\n\t\"io\"\n)\n\ntype LowerCaseReader struct {\n\treader io.Reader\n}\n\nfunc (l LowerCaseReader) Read(p []byt"
  },
  {
    "path": "src/11-testing/88-utility-package/iotest/main_test.go",
    "chars": 619,
    "preview": "package main\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\t\"testing/iotest\"\n)\n\nfunc TestLowerCaseReader(t *testing.T) {\n\terr := iotes"
  },
  {
    "path": "src/11-testing/89-benchmark/compiler-optimizations/main.go",
    "chars": 263,
    "preview": "package main\n\nconst (\n\tm1  = 0x5555555555555555\n\tm2  = 0x3333333333333333\n\tm4  = 0x0f0f0f0f0f0f0f0f\n\th01 = 0x01010101010"
  },
  {
    "path": "src/11-testing/89-benchmark/compiler-optimizations/main_test.go",
    "chars": 264,
    "preview": "package main\n\nimport \"testing\"\n\nfunc BenchmarkPopcnt1(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\tpopcnt(uint64(i))\n\t}"
  },
  {
    "path": "src/11-testing/89-benchmark/observer-effect/main.go",
    "chars": 329,
    "preview": "package main\n\nfunc calculateSum512(s [][512]int64) int64 {\n\tvar sum int64\n\tfor i := 0; i < len(s); i++ {\n\t\tfor j := 0; j"
  },
  {
    "path": "src/11-testing/89-benchmark/observer-effect/main_test.go",
    "chars": 969,
    "preview": "package main\n\nimport \"testing\"\n\nconst rows = 1000\n\nvar res int64\n\nfunc BenchmarkCalculateSum512_1(b *testing.B) {\n\tvar s"
  },
  {
    "path": "src/11-testing/89-benchmark/timer/main_test.go",
    "chars": 357,
    "preview": "package timer\n\nimport \"testing\"\n\nfunc BenchmarkFoo1(b *testing.B) {\n\texpensiveSetup()\n\tb.ResetTimer()\n\tfor i := 0; i < b"
  },
  {
    "path": "src/11-testing/89-benchmark/wrong-assumptions/main_test.go",
    "chars": 293,
    "preview": "package main\n\nimport (\n\t\"sync/atomic\"\n\t\"testing\"\n)\n\nfunc BenchmarkAtomicStoreInt32(b *testing.B) {\n\tvar v int32\n\tfor i :"
  },
  {
    "path": "src/11-testing/90-testing-features/different-package/main.go",
    "chars": 122,
    "preview": "package counter\n\nimport \"sync/atomic\"\n\nvar count uint64\n\nfunc Inc() uint64 {\n\tatomic.AddUint64(&count, 1)\n\treturn count\n"
  },
  {
    "path": "src/11-testing/90-testing-features/different-package/main_test.go",
    "chars": 230,
    "preview": "package counter_test\n\nimport (\n\t\"testing\"\n\n\tcounter \"github.com/teivah/100-go-mistakes/src/11-testing/90-testing-feature"
  },
  {
    "path": "src/11-testing/90-testing-features/setup-teardown/main_test.go",
    "chars": 479,
    "preview": "package main\n\nimport (\n\t\"database/sql\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc TestMySQLIntegration(t *testing.T) {\n\tsetupMySQL()\n\tdefe"
  },
  {
    "path": "src/11-testing/90-testing-features/utility-function/main.go",
    "chars": 50,
    "preview": "package main\n\ntype Customer struct {\n\tid string\n}\n"
  },
  {
    "path": "src/11-testing/90-testing-features/utility-function/main_test.go",
    "chars": 770,
    "preview": "package main\n\nimport (\n\t\"errors\"\n\t\"testing\"\n)\n\nfunc TestCustomer1(t *testing.T) {\n\tcustomer, err := createCustomer1(\"foo"
  },
  {
    "path": "src/12-optimizations/91-cpu-caches/cache-line/main.go",
    "chars": 245,
    "preview": "package main\n\nfunc sum2(s []int64) int64 {\n\tvar total int64\n\tfor i := 0; i < len(s); i += 2 {\n\t\ttotal += s[i]\n\t}\n\treturn"
  },
  {
    "path": "src/12-optimizations/91-cpu-caches/cache-line/main_test.go",
    "chars": 419,
    "preview": "package main\n\nimport \"testing\"\n\nvar global int64\n\nfunc BenchmarkSum2(b *testing.B) {\n\tvar local int64\n\tfor i := 0; i < b"
  },
  {
    "path": "src/12-optimizations/91-cpu-caches/predictability/main.go",
    "chars": 295,
    "preview": "package main\n\ntype node struct {\n\tvalue int64\n\tnext  *node\n}\n\nfunc linkedList(n *node) int64 {\n\tvar total int64\n\tfor n !"
  },
  {
    "path": "src/12-optimizations/91-cpu-caches/predictability/main_test.go",
    "chars": 511,
    "preview": "package main\n\nimport \"testing\"\n\nvar global int64\n\nconst n = 1_000_000\n\nfunc BenchmarkLinkedList(b *testing.B) {\n\tvar loc"
  },
  {
    "path": "src/12-optimizations/91-cpu-caches/slice-structs/main.go",
    "chars": 340,
    "preview": "package main\n\ntype Foo struct {\n\ta int64\n\tb int64\n}\n\nfunc sumFoo(foos []Foo) int64 {\n\tvar total int64\n\tfor i := 0; i < l"
  },
  {
    "path": "src/12-optimizations/91-cpu-caches/slice-structs/main_test.go",
    "chars": 474,
    "preview": "package main\n\nimport \"testing\"\n\nvar global int64\n\nconst n = 1_000_000\n\nfunc BenchmarkSumFoo(b *testing.B) {\n\tvar local i"
  },
  {
    "path": "src/12-optimizations/92-false-sharing/main.go",
    "chars": 840,
    "preview": "package main\n\nimport \"sync\"\n\ntype Input struct {\n\ta int64\n\tb int64\n}\n\ntype Result1 struct {\n\tsumA int64\n\tsumB int64\n}\n\nf"
  },
  {
    "path": "src/12-optimizations/92-false-sharing/main_test.go",
    "chars": 506,
    "preview": "package main\n\nimport \"testing\"\n\nconst n = 1_000_000\n\nvar globalResult1 Result1\n\nfunc BenchmarkCount1(b *testing.B) {\n\tva"
  }
]

// ... and 8 more files (download for full content)

About this extraction

This page contains the full source code of the teivah/100-go-mistakes GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 208 files (3.6 MB), approximately 956.5k tokens, and a symbol index with 712 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!