mirror of
https://github.com/teivah/100-go-mistakes.git
synced 2026-06-21 08:57:07 +08:00
Merging desc
This commit is contained in:
parent
b7a7eb62fe
commit
43fb8af037
13 changed files with 2724 additions and 232 deletions
200
docs/mistakes.md
200
docs/mistakes.md
|
|
@ -4,6 +4,8 @@ This section contains a summary of the 100 mistakes in the book. Meanwhile, it's
|
|||
|
||||
**Note:** You're currently viewing a new version that I'm enriching with significantly more content. Yet, this version is still under development; please be gentle if you find an issue, and feel free to create a PR.
|
||||
|
||||
<!-- TODO Include full excerpts -->
|
||||
|
||||

|
||||
|
||||
## Code and Project Organization
|
||||
|
|
@ -14,7 +16,7 @@ This section contains a summary of the 100 mistakes in the book. Meanwhile, it's
|
|||
|
||||
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.
|
||||
|
||||
[Source code](../src/02-code-project-organization/1-variable-shadowing/main.go)
|
||||
[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)
|
||||
|
||||
|
|
@ -64,7 +66,7 @@ In general, the more nested levels a function requires, the more complex it is t
|
|||
|
||||
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.
|
||||
|
||||
[Source code](../src/02-code-project-organization/2-nested-code/main.go)
|
||||
[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)
|
||||
|
||||
|
|
@ -79,7 +81,7 @@ Init functions can lead to some issues:
|
|||
|
||||
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.
|
||||
|
||||
[Source code](../src/02-code-project-organization/3-init-functions/)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/3-init-functions/)
|
||||
|
||||
### Overusing getters and setters (#4)
|
||||
|
||||
|
|
@ -110,7 +112,7 @@ We should be cautious when creating abstractions in our code (abstractions shoul
|
|||
|
||||
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.
|
||||
|
||||
[Source code](../src/02-code-project-organization/5-interface-pollution/)
|
||||
[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)
|
||||
|
||||
|
|
@ -120,7 +122,7 @@ Interfaces are satisfied implicitly in Go, which tends to be a gamechanger compa
|
|||
|
||||
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.
|
||||
|
||||
[Source code](../src/02-code-project-organization/6-interface-producer/)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/6-interface-producer/)
|
||||
|
||||
### Returning interfaces (#7)
|
||||
|
||||
|
|
@ -134,15 +136,13 @@ In most cases, we shouldn’t return interfaces but concrete implementations. Ot
|
|||
|
||||
The `any` type can be helpful if there is a genuine need for accepting or returning any possible type (for instance, when it comes to marshaling or formatting). In general, we should avoid overgeneralizing the code we write at all costs. Perhaps a little bit of duplicated code might occasionally be better if it improves other aspects such as code expressiveness.
|
||||
|
||||
[Source code](../src/02-code-project-organization/8-any/main.go)
|
||||
[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)
|
||||
|
||||
**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 whole section](res/9-generics.md)
|
||||
|
||||
[Source code](../src/02-code-project-organization/9-generics/main.go)
|
||||
[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)
|
||||
|
||||
|
|
@ -175,7 +175,7 @@ If we decide to use type embedding, we need to keep two main constraints in mind
|
|||
|
||||
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.
|
||||
|
||||
[Source code](../src/02-code-project-organization/10-type-embedding/main.go)
|
||||
[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)
|
||||
|
||||
|
|
@ -232,7 +232,7 @@ func NewServer(addr string, opts ...Option) ( *http.Server, error) { <1>
|
|||
|
||||
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.
|
||||
|
||||
[Source code](../src/02-code-project-organization/11-functional-options/)
|
||||
[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)
|
||||
|
||||
|
|
@ -255,7 +255,7 @@ Organizing a project isn’t straightforward, but following these rules should h
|
|||
|
||||
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.
|
||||
|
||||
[Source code](../src/02-code-project-organization/13-utility-packages/stringset.go)
|
||||
[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)
|
||||
|
||||
|
|
@ -314,7 +314,7 @@ We should also note the other integer literal representations:
|
|||
|
||||
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 under- score character with other representations (for example, `0b00_00_01`).
|
||||
|
||||
[Source code](../src/03-data-types/17-octal-literals/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/17-octal-literals/main.go)
|
||||
|
||||
### Neglecting integer overflows (#18)
|
||||
|
||||
|
|
@ -332,7 +332,7 @@ 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).
|
||||
|
||||
[Source code](../src/03-data-types/18-integer-overflows)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/18-integer-overflows)
|
||||
|
||||
### Not understanding floating-points (#19)
|
||||
|
||||
|
|
@ -354,7 +354,7 @@ Because Go’s `float32` and `float64` types are approximations, we have to bear
|
|||
* 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.
|
||||
|
||||
[Source code](../src/03-data-types/19-floating-points/main.go)
|
||||
[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)
|
||||
|
||||
|
|
@ -362,7 +362,7 @@ Because Go’s `float32` and `float64` types are approximations, we have to bear
|
|||
|
||||
<!-- TODO -->
|
||||
|
||||
[Source code](../src/03-data-types/20-slice-length-cap/main.go)
|
||||
[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)
|
||||
|
||||
|
|
@ -372,7 +372,7 @@ While initializing a slice using `make`, we can provide a length and an optional
|
|||
|
||||
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.
|
||||
|
||||
[Source code](../src/03-data-types/21-slice-init/main.go)
|
||||
[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)
|
||||
|
||||
|
|
@ -386,7 +386,7 @@ In Go, there is a distinction between nil and empty slices. A nil slice is equal
|
|||
|
||||
The last option, `[]string{}`, should be avoided if we initialize the slice without ele- ments. Finally, let’s check whether the libraries we use make the distinctions between nil and empty slices to prevent unexpected behaviors.
|
||||
|
||||
[Source code](../src/03-data-types/22-nil-empty-slice/)
|
||||
[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)
|
||||
|
||||
|
|
@ -396,7 +396,7 @@ To determine whether a slice has elements, we can either do it by checking if th
|
|||
|
||||
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 seman- tic 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.
|
||||
|
||||
[Source code](../src/03-data-types/23-checking-slice-empty/main.go)
|
||||
[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)
|
||||
|
||||
|
|
@ -404,7 +404,7 @@ Meanwhile, when designing interfaces, we should avoid distinguishing nil and emp
|
|||
|
||||
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 destina- tion 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.
|
||||
|
||||
[Source code](../src/03-data-types/24-slice-copy/main.go)
|
||||
[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)
|
||||
|
||||
|
|
@ -415,7 +415,7 @@ When using slicing, we must remember that we can face a situation leading to uni
|
|||
> [!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`.
|
||||
|
||||
[Source code](../src/03-data-types/25-slice-append/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/25-slice-append/main.go)
|
||||
|
||||
### Slice and memory leaks (#26)
|
||||
|
||||
|
|
@ -425,13 +425,13 @@ When using slicing, we must remember that we can face a situation leading to uni
|
|||
|
||||
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.
|
||||
|
||||
[Source code](../src/03-data-types/26-slice-memory-leak/capacity-leak)
|
||||
[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`.
|
||||
|
||||
[Source code](../src/03-data-types/26-slice-memory-leak/slice-pointers)
|
||||
[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)
|
||||
|
||||
|
|
@ -441,7 +441,7 @@ A map provides an unordered collection of key-value pairs in which all the keys
|
|||
|
||||
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.
|
||||
|
||||
[Source code](../src/03-data-types/27-map-init/main_test.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/27-map-init/main_test.go)
|
||||
|
||||
### [Map and memory leaks](https://teivah.medium.com/maps-and-memory-leaks-in-go-a85ebe6e7e69) (#28)
|
||||
|
||||
|
|
@ -449,7 +449,7 @@ If we know up front the number of elements a map will contain, we should create
|
|||
|
||||
<!-- TODO -->
|
||||
|
||||
[Source code](../src/03-data-types/28-map-memory-leak/main.go)
|
||||
[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)
|
||||
|
||||
|
|
@ -473,7 +473,7 @@ If performance is crucial at run time, implementing our custom method might be t
|
|||
One additional note: we must remember that the standard library has some exist- ing 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.
|
||||
|
||||
|
||||
[Source code](../src/03-data-types/29-comparing-values/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/29-comparing-values/main.go)
|
||||
|
||||
## Control Structures
|
||||
|
||||
|
|
@ -493,7 +493,7 @@ Compared to a classic for `loop`, a `range` loop is a convenient way to iterate
|
|||
|
||||
Yet, we should remember that the value element in a range loop is a copy. Therefore, if the value is a struct we need to mutate, we will only update the copy, not the element itself, unless the value or field we modify is a pointer. The favored options are to access the element via the index using a range loop or a classic for loop.
|
||||
|
||||
[Source code](../src/04-control-structures/30-range-loop-element-copied/)
|
||||
[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)
|
||||
|
||||
|
|
@ -513,7 +513,7 @@ for i, v := range a {
|
|||
|
||||
This code updates the last index to 10. However, if we run this code, it does not print 10; it prints 2.
|
||||
|
||||
[Source code](../src/04-control-structures/31-range-loop-arg-evaluation/)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/31-range-loop-arg-evaluation/)
|
||||
|
||||
### Ignoring the impacts of using pointer elements in `range` loops (#32)
|
||||
|
||||
|
|
@ -521,7 +521,7 @@ This code updates the last index to 10. However, if we run this code, it does no
|
|||
|
||||
When iterating over a data structure using a `range` loop, we must recall that all the values are assigned to a unique variable with a single unique address. Therefore, if we store a pointer referencing this variable during each iteration, we will end up in a situ- ation where we store the same pointer referencing the same element: the latest one. We can overcome this issue by forcing the creation of a local variable in the loop’s scope or creating a pointer referencing a slice element via its index. Both solutions are fine.
|
||||
|
||||
[Source code](../src/04-control-structures/32-range-loop-pointers/)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/32-range-loop-pointers/)
|
||||
|
||||
### Making wrong assumptions during map iterations (ordering and map insert during iteration) (#33)
|
||||
|
||||
|
|
@ -533,7 +533,7 @@ When iterating over a data structure using a `range` loop, we must recall that a
|
|||
|
||||
<!-- TODO -->
|
||||
|
||||
[Source code](../src/04-control-structures/33-map-iteration/main.go)
|
||||
[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)
|
||||
|
||||
|
|
@ -574,7 +574,7 @@ loop:
|
|||
|
||||
Here, we associate the `loop` label with the `for` loop. Then, because we provide the `loop` label to the `break` statement, it breaks the loop, not the switch. Therefore, this new version will print `0 1 2`, as we expected.
|
||||
|
||||
[Source code](../src/04-control-structures/34-break/main.go)
|
||||
[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)
|
||||
|
||||
|
|
@ -629,7 +629,7 @@ func readFile(path string) error {
|
|||
|
||||
Another solution is to make the `readFile` function a closure but intrinsically, this remains the same solution: adding another surrounding function to execute the `defer` calls during each iteration.
|
||||
|
||||
[Source code](../src/04-control-structures/35-defer-loop/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/35-defer-loop/main.go)
|
||||
|
||||
## Strings
|
||||
|
||||
|
|
@ -646,7 +646,7 @@ As runes are everywhere in Go, it's important to understand the following:
|
|||
* 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.
|
||||
|
||||
[Source code](../src/05-strings/36-rune/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/36-rune/main.go)
|
||||
|
||||
### Inaccurate string iteration (#37)
|
||||
|
||||
|
|
@ -716,7 +716,7 @@ r := []rune(s)[4]
|
|||
fmt.Printf("%c\n", r) // o
|
||||
```
|
||||
|
||||
[Source code](../src/05-strings/37-string-iteration/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/37-string-iteration/main.go)
|
||||
|
||||
### Misusing trim functions (#38)
|
||||
|
||||
|
|
@ -736,7 +736,7 @@ 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.
|
||||
|
||||
[Source code](../src/05-strings/38-trim/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/38-trim/main.go)
|
||||
|
||||
### Under-optimized strings concatenation (#39)
|
||||
|
||||
|
|
@ -803,7 +803,7 @@ As we can see, the latest version is by far the most efficient: 99% faster than
|
|||
|
||||
`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 concate- nate a few strings (such as a name and a surname), using `strings.Builder` is not rec- ommended as doing so will make the code a bit less readable than using the `+=` operator or `fmt.Sprintf`.
|
||||
|
||||
[Source code](../src/05-strings/39-string-concat/)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/39-string-concat/)
|
||||
|
||||
### Useless string conversions (#40)
|
||||
|
||||
|
|
@ -813,7 +813,7 @@ When choosing to work with a string or a `[]byte`, most programmers tend to favo
|
|||
|
||||
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.
|
||||
|
||||
[Source code](../src/05-strings/40-string-conversion/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/40-string-conversion/main.go)
|
||||
|
||||
### Substring and memory leaks (#41)
|
||||
|
||||
|
|
@ -823,7 +823,7 @@ In mistake [#26, “Slices and memory leaks,”](#slice-and-memory-leaks--26-) w
|
|||
|
||||
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.
|
||||
|
||||
[Source code](../src/05-strings/41-substring-memory-leak/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/41-substring-memory-leak/main.go)
|
||||
|
||||
## Functions and Methods
|
||||
|
||||
|
|
@ -865,7 +865,7 @@ A receiver _should_ be a value
|
|||
|
||||
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.
|
||||
|
||||
[Source code](../src/06-functions-methods/42-receiver/)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/42-receiver/)
|
||||
|
||||
### Never using named result parameters (#43)
|
||||
|
||||
|
|
@ -886,7 +886,7 @@ In this example, we attach a name to the result parameter: `b`. When we call ret
|
|||
|
||||
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.
|
||||
|
||||
[Source code](../src/06-functions-methods/43-named-result-parameters/main.go)
|
||||
[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)
|
||||
|
||||
|
|
@ -915,7 +915,7 @@ The error might not be obvious at first glance. Here, the error returned in the
|
|||
|
||||
When using named result parameters, we must recall that each parameter is initial- ized 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 cau- tious when using named result parameters, to avoid potential side effects.
|
||||
|
||||
[Source code](../src/06-functions-methods/44-side-effects-named-result-parameters/main.go)
|
||||
[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)
|
||||
|
||||
|
|
@ -923,7 +923,7 @@ When using named result parameters, we must recall that each parameter is initia
|
|||
|
||||
<!-- TODO -->
|
||||
|
||||
[Source code](../src/06-functions-methods/45-nil-receiver/main.go)
|
||||
[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)
|
||||
|
||||
|
|
@ -931,7 +931,7 @@ When using named result parameters, we must recall that each parameter is initia
|
|||
|
||||
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 imple- mentation can be reused and easily tested.
|
||||
|
||||
[Source code](../src/06-functions-methods/46-function-input/)
|
||||
[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)
|
||||
|
||||
|
|
@ -1014,7 +1014,7 @@ Here, we wrap the calls to both `notify` and `incrementCounter` within a closure
|
|||
|
||||
Let's also note this behavior applies with method receiver: the receiver is evaluated immediately.
|
||||
|
||||
[Source code](../src/06-functions-methods/47-defer-evaluation/)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/47-defer-evaluation/)
|
||||
|
||||
## Error Management
|
||||
|
||||
|
|
@ -1045,7 +1045,7 @@ main.main()
|
|||
|
||||
Panicking in Go should be used sparingly. There are two prominent cases, one to signal a programmer error (e.g., [`sql.Register`](https://cs.opensource.google/go/go/+/refs/tags/go1.20.7:src/database/sql/sql.go;l=44) that panics if the driver is `nil` or has already been register) and another where our application fails to create a man- datory dependency. Hence, exceptional conditions that lead us to stop the application. In most other cases, error management should be done with a function that returns a proper error type as the last return argument.
|
||||
|
||||
[Source code](../src/07-error-management/48-panic/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/48-panic/main.go)
|
||||
|
||||
### Ignoring when to wrap an error (#49)
|
||||
|
||||
|
|
@ -1058,7 +1058,7 @@ Since Go 1.13, the %w directive allows us to wrap errors conveniently. Error wra
|
|||
|
||||
When handling an error, we can decide to wrap it. Wrapping is about adding additional context to an error and/or marking an error as a specific type. If we need to mark an error, we should create a custom error type. However, if we just want to add extra context, we should use fmt.Errorf with the %w directive as it doesn’t require creating a new error type. Yet, error wrapping creates potential coupling as it makes the source error available for the caller. If we want to prevent it, we shouldn’t use error wrapping but error transformation, for example, using fmt.Errorf with the %v directive.
|
||||
|
||||
[Source code](../src/07-error-management/49-error-wrapping/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/49-error-wrapping/main.go)
|
||||
|
||||
### Comparing an error type inaccurately (#50)
|
||||
|
||||
|
|
@ -1066,7 +1066,7 @@ When handling an error, we can decide to wrap it. Wrapping is about adding addit
|
|||
|
||||
<!-- TODO -->
|
||||
|
||||
[Source code](../src/07-error-management/50-compare-error-type/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/50-compare-error-type/main.go)
|
||||
|
||||
### Comparing an error value inaccurately (#51)
|
||||
|
||||
|
|
@ -1087,7 +1087,7 @@ In general, the convention is to start with `Err` followed by the error type: he
|
|||
|
||||
If we use error wrapping in our application with the `%w` directive and `fmt.Errorf`, checking an error against a specific value should be done using `errors.Is` instead of `==`. Thus, even if the sentinel error is wrapped, `errors.Is` can recursively unwrap it and compare each error in the chain against the provided value.
|
||||
|
||||
[Source code](../src/07-error-management/51-comparing-error-value/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/51-comparing-error-value/main.go)
|
||||
|
||||
### Handling an error twice (#52)
|
||||
|
||||
|
|
@ -1097,13 +1097,13 @@ Handling an error multiple times is a mistake made frequently by developers, not
|
|||
|
||||
Let's reming us that handling an error should be done only once. Logging an error is handling an error. Hence, we should either log or return an error. By doing this, we simplify our code and gain better insights into the error situation. Using error wrap- ping is the most convenient approach as it allows us to propagate the source error and add context to an error.
|
||||
|
||||
[Source code](../src/07-error-management/52-handling-error-twice/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/52-handling-error-twice/main.go)
|
||||
|
||||
### Not handling an error (#53)
|
||||
|
||||
**TL;DR**: Ignoring an error, whether during a function call or in a `defer` function, should be done explicitly using the blank identifier. Otherwise, future readers may be confused about whether it was intentional or a miss.
|
||||
|
||||
[Source code](../src/07-error-management/53-not-handling-error/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/53-not-handling-error/main.go)
|
||||
|
||||
### Not handling `defer` errors (#54)
|
||||
|
||||
|
|
@ -1138,7 +1138,7 @@ In terms of compilation and run time, this approach doesn’t change anything co
|
|||
_ = notify()
|
||||
```
|
||||
|
||||
[Source code](../src/07-error-management/54-defer-errors/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/54-defer-errors/main.go)
|
||||
|
||||
## Concurrency: Foundations
|
||||
|
||||
|
|
@ -1150,7 +1150,7 @@ _ = notify()
|
|||
|
||||
**TL;DR**: To be a proficient developer, you must acknowledge that concurrency isn’t always faster. Solutions involving parallelization of minimal workloads may not necessarily be faster than a sequential implementation. Benchmarking sequential versus concurrent solutions should be the way to validate assumptions.
|
||||
|
||||
[Source code](../src/08-concurrency-foundations/56-faster/)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/56-faster/)
|
||||
|
||||
### Being puzzled about when to use channels or mutexes (#57)
|
||||
|
||||
|
|
@ -1162,19 +1162,19 @@ _ = notify()
|
|||
|
||||
Understanding the Go memory model and the underlying guarantees in terms of ordering and synchronization is essential to prevent possible data races and/or race conditions.
|
||||
|
||||
[Source code](../src/08-concurrency-foundations/58-races/)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/58-races/)
|
||||
|
||||
### Not understanding the concurrency impacts of a workload type (#59)
|
||||
|
||||
**TL;DR**: When creating a certain number of goroutines, consider the workload type. Creating CPU-bound goroutines means bounding this number close to the `GOMAXPROCS` variable (based by default on the number of CPU cores on the host). Creating I/O-bound goroutines depends on other factors, such as the external system.
|
||||
|
||||
[Source code](../src/08-concurrency-foundations/59-workload-type/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/59-workload-type/main.go)
|
||||
|
||||
### Misunderstanding Go contexts (#60)
|
||||
|
||||
**TL;DR**: Go contexts are also one of the cornerstones of concurrency in Go. A context allows you to carry a deadline, a cancellation signal, and/or a list of keys-values.
|
||||
|
||||
[Source code](../src/08-concurrency-foundations/60-contexts/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/60-contexts/main.go)
|
||||
|
||||
## Concurrency: Practice
|
||||
|
||||
|
|
@ -1182,25 +1182,25 @@ Understanding the Go memory model and the underlying guarantees in terms of orde
|
|||
|
||||
**TL;DR**: Understanding the conditions when a context can be canceled should matter when propagating it: for example, an HTTP handler canceling the context when the response has been sent.
|
||||
|
||||
[Source code](../src/09-concurrency-practice/61-inappropriate-context/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/61-inappropriate-context/main.go)
|
||||
|
||||
### Starting a goroutine without knowing when to stop it (#62)
|
||||
|
||||
**TL;DR**: Avoiding leaks means being mindful that whenever a goroutine is started, you should have a plan to stop it eventually.
|
||||
|
||||
[Source code](../src/09-concurrency-practice/62-starting-goroutine/)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/62-starting-goroutine/)
|
||||
|
||||
### Not being careful with goroutines and loop variables (#63)
|
||||
|
||||
**TL;DR**: To avoid bugs with goroutines and loop variables, create local variables or call functions instead of closures.
|
||||
|
||||
[Source code](../src/09-concurrency-practice/63-goroutines-loop-variables/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/63-goroutines-loop-variables/main.go)
|
||||
|
||||
### Expecting a deterministic behavior using select and channels (#64)
|
||||
|
||||
**TL;DR**: Understanding that `select` with multiple channels chooses the case randomly if multiple options are possible prevents making wrong assumptions that can lead to subtle concurrency bugs.
|
||||
|
||||
[Source code](../src/09-concurrency-practice/64-select-behavior/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/64-select-behavior/main.go)
|
||||
|
||||
### Not using notification channels (#65)
|
||||
|
||||
|
|
@ -1210,7 +1210,7 @@ Understanding the Go memory model and the underlying guarantees in terms of orde
|
|||
|
||||
**TL;DR**: Using nil channels should be part of your concurrency toolset because it allows you to _remove_ cases from `select` statements, for example.
|
||||
|
||||
[Source code](../src/09-concurrency-practice/66-nil-channels/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/66-nil-channels/main.go)
|
||||
|
||||
### Being puzzled about channel size (#67)
|
||||
|
||||
|
|
@ -1222,43 +1222,43 @@ You should have a good reason to specify a channel size other than one for buffe
|
|||
|
||||
**TL;DR**: Being aware that string formatting may lead to calling existing functions means watching out for possible deadlocks and other data races.
|
||||
|
||||
[Source code](../src/09-concurrency-practice/68-string-formatting/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/68-string-formatting/main.go)
|
||||
|
||||
### Creating data races with append (#69)
|
||||
|
||||
**TL;DR**: Calling `append` isn’t always data-race-free; hence, it shouldn’t be used concurrently on a shared slice.
|
||||
|
||||
[Source code](../src/09-concurrency-practice/69-data-race-append/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/69-data-race-append/main.go)
|
||||
|
||||
### Using mutexes inaccurately with slices and maps (#70)
|
||||
|
||||
**TL;DR**: Remembering that slices and maps are pointers can prevent common data races.
|
||||
|
||||
[Source code](../src/09-concurrency-practice/70-mutex-slices-maps/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/70-mutex-slices-maps/main.go)
|
||||
|
||||
### Misusing `sync.WaitGroup` (#71)
|
||||
|
||||
**TL;DR**: To accurately use `sync.WaitGroup`, call the `Add` method before spinning up goroutines.
|
||||
|
||||
[Source code](../src/09-concurrency-practice/71-wait-group/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/71-wait-group/main.go)
|
||||
|
||||
### Forgetting about `sync.Cond` (#72)
|
||||
|
||||
**TL;DR**: You can send repeated notifications to multiple goroutines with `sync.Cond`.
|
||||
|
||||
[Source code](../src/09-concurrency-practice/72-cond/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/72-cond/main.go)
|
||||
|
||||
### Not using `errgroup` (#73)
|
||||
|
||||
**TL;DR**: You can synchronize a group of goroutines and handle errors and contexts with the `errgroup` package.
|
||||
|
||||
[Source code](../src/09-concurrency-practice/73-errgroup/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/73-errgroup/main.go)
|
||||
|
||||
### Copying a `sync` type (#74)
|
||||
|
||||
**TL;DR**: `sync` types shouldn’t be copied.
|
||||
|
||||
[Source code](../src/09-concurrency-practice/74-copying-sync/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/74-copying-sync/main.go)
|
||||
|
||||
## Standard Library
|
||||
|
||||
|
|
@ -1266,13 +1266,13 @@ You should have a good reason to specify a channel size other than one for buffe
|
|||
|
||||
**TL;DR**: Remain cautious with functions accepting a `time.Duration`. Even though passing an integer is allowed, strive to use the time API to prevent any possible confusion.
|
||||
|
||||
[Source code](../src/10-standard-lib/75-wrong-time-duration/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/75-wrong-time-duration/main.go)
|
||||
|
||||
### `time.After` and memory leaks (#76)
|
||||
|
||||
**TL;DR**: Avoiding calls to `time.After` in repeated functions (such as loops or HTTP handlers) can avoid peak memory consumption. The resources created by `time.After` are released only when the timer expires.
|
||||
|
||||
[Source code](../src/10-standard-lib/76-time-after/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/76-time-after/main.go)
|
||||
|
||||
### JSON handling common mistakes (#77)
|
||||
|
||||
|
|
@ -1280,19 +1280,19 @@ You should have a good reason to specify a channel size other than one for buffe
|
|||
|
||||
Be careful about using embedded fields in Go structs. Doing so may lead to sneaky bugs like an embedded time.Time field implementing the `json.Marshaler` interface, hence overriding the default marshaling behavior.
|
||||
|
||||
[Source code](../src/10-standard-lib/77-json-handling/type-embedding/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/77-json-handling/type-embedding/main.go)
|
||||
|
||||
* JSON and the monotonic clock
|
||||
|
||||
When comparing two `time.Time` structs, recall that `time.Time` contains both a wall clock and a monotonic clock, and the comparison using the == operator is done on both clocks.
|
||||
|
||||
[Source code](../src/10-standard-lib/77-json-handling/monotonic-clock/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/77-json-handling/monotonic-clock/main.go)
|
||||
|
||||
* Map of `any`
|
||||
|
||||
To avoid wrong assumptions when you provide a map while unmarshaling JSON data, remember that numerics are converted to `float64` by default.
|
||||
|
||||
[Source code](../src/10-standard-lib/77-json-handling/map-any/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/77-json-handling/map-any/main.go)
|
||||
|
||||
### Common SQL mistakes (#78)
|
||||
|
||||
|
|
@ -1300,7 +1300,7 @@ You should have a good reason to specify a channel size other than one for buffe
|
|||
|
||||
Call the `Ping` or `PingContext` method if you need to test your configuration and make sure a database is reachable.
|
||||
|
||||
[Source code](../src/10-standard-lib/78-sql/sql-open)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/78-sql/sql-open)
|
||||
|
||||
* Forgetting about connections pooling
|
||||
|
||||
|
|
@ -1310,37 +1310,37 @@ You should have a good reason to specify a channel size other than one for buffe
|
|||
|
||||
Using SQL prepared statements makes queries more efficient and more secure.
|
||||
|
||||
[Source code](../src/10-standard-lib/78-sql/prepared-statements)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/78-sql/prepared-statements)
|
||||
|
||||
* Mishandling null values
|
||||
|
||||
Deal with nullable columns in tables using pointers or `sql.NullXXX` types.
|
||||
|
||||
[Source code](../src/10-standard-lib/78-sql/null-values/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/78-sql/null-values/main.go)
|
||||
|
||||
* Not handling rows iteration errors
|
||||
|
||||
Call the `Err` method of `sql.Rows` after row iterations to ensure that you haven’t missed an error while preparing the next row.
|
||||
|
||||
[Source code](../src/10-standard-lib/78-sql/rows-iterations-errors)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/78-sql/rows-iterations-errors)
|
||||
|
||||
### Not closing transient resources (HTTP body, `sql.Rows`, and `os.File`) (#79)
|
||||
|
||||
**TL;DR**: Eventually close all structs implementing `io.Closer` to avoid possible leaks.
|
||||
|
||||
[Source code](../src/10-standard-lib/79-closing-resources/)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/79-closing-resources/)
|
||||
|
||||
### Forgetting the return statement after replying to an HTTP request (#80)
|
||||
|
||||
**TL;DR**: To avoid unexpected behaviors in HTTP handler implementations, make sure you don’t miss the `return` statement if you want a handler to stop after `http.Error`.
|
||||
|
||||
[Source code](../src/10-standard-lib/80-http-return/main.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/80-http-return/main.go)
|
||||
|
||||
### Using the default HTTP client and server (#81)
|
||||
|
||||
**TL;DR**: For production-grade applications, don’t use the default HTTP client and server implementations. These implementations are missing timeouts and behaviors that should be mandatory in production.
|
||||
|
||||
[Source code](../src/10-standard-lib/81-default-http-client-server/)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/81-default-http-client-server/)
|
||||
|
||||
## Testing
|
||||
|
||||
|
|
@ -1348,7 +1348,7 @@ You should have a good reason to specify a channel size other than one for buffe
|
|||
|
||||
**TL;DR**: Categorizing tests using build flags, environment variables, or short mode makes the testing process more efficient. You can create test categories using build flags or environment variables (for example, unit versus integration tests) and differentiate short from long-running tests to decide which kinds of tests to execute.
|
||||
|
||||
[Source code](../src/11-testing/82-categorizing-tests/)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/82-categorizing-tests/)
|
||||
|
||||
### Not enabling the race flag (#83)
|
||||
|
||||
|
|
@ -1362,29 +1362,29 @@ You should have a good reason to specify a channel size other than one for buffe
|
|||
|
||||
**TL;DR**: Table-driven tests are an efficient way to group a set of similar tests to prevent code duplication and make future updates easier to handle.
|
||||
|
||||
[Source code](../src/11-testing/85-table-driven-tests/main_test.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/85-table-driven-tests/main_test.go)
|
||||
|
||||
### Sleeping in unit tests (#86)
|
||||
|
||||
**TL;DR**: Avoid sleeps using synchronization to make a test less flaky and more robust. If synchronization isn’t possible, consider a retry approach.
|
||||
|
||||
[Source code](../src/11-testing/86-sleeping/main_test.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/86-sleeping/main_test.go)
|
||||
|
||||
### Not dealing with the time API efficiently (#87)
|
||||
|
||||
**TL;DR**: Understanding how to deal with functions using the time API is another way to make a test less flaky. You can use standard techniques such as handling the time as part of a hidden dependency or asking clients to provide it.
|
||||
|
||||
[Source code](../src/11-testing/87-time-api/)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/87-time-api/)
|
||||
|
||||
### Not using testing utility packages (`httptest` and `iotest`) (#88)
|
||||
|
||||
* The `httptest` package is helpful for dealing with HTTP applications. It provides a set of utilities to test both clients and servers.
|
||||
|
||||
[Source code](../src/11-testing/88-utility-package/httptest/main_test.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/88-utility-package/httptest/main_test.go)
|
||||
|
||||
* The `iotest` package helps write io.Reader and test that an application is tolerant to errors.
|
||||
|
||||
[Source code](../src/11-testing/88-utility-package/iotest/main_test.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/88-utility-package/iotest/main_test.go)
|
||||
|
||||
### [Writing inaccurate benchmarks](https://teivah.medium.com/how-to-write-accurate-benchmarks-in-go-4266d7dd1a95) (#89)
|
||||
|
||||
|
|
@ -1392,7 +1392,7 @@ You should have a good reason to specify a channel size other than one for buffe
|
|||
|
||||
Use time methods to preserve the accuracy of a benchmark.
|
||||
|
||||
[Source code](../src/11-testing/89-benchmark/timer/main_test.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/89-benchmark/timer/main_test.go)
|
||||
|
||||
* Making wrong assumptions about micro-benchmarks
|
||||
|
||||
|
|
@ -1400,19 +1400,19 @@ You should have a good reason to specify a channel size other than one for buffe
|
|||
|
||||
Be careful with the results of a micro-benchmark if the system that ends up running the application is different from the one running the micro-benchmark.
|
||||
|
||||
[Source code](../src/11-testing/89-benchmark/wrong-assumptions/main_test.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/89-benchmark/wrong-assumptions/main_test.go)
|
||||
|
||||
* Not being careful about compiler optimizations
|
||||
|
||||
Make sure the function under test leads to a side effect, to prevent compiler optimizations from fooling you about the benchmark results.
|
||||
|
||||
[Source code](../src/11-testing/89-benchmark/compiler-optimizations/main_test.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/89-benchmark/compiler-optimizations/main_test.go)
|
||||
|
||||
* Being fooled by the observer effect
|
||||
|
||||
To prevent the observer effect, force a benchmark to re-create the data used by a CPU-bound function.
|
||||
|
||||
[Source code](../src/11-testing/89-benchmark/observer-effect/main_test.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/89-benchmark/observer-effect/main_test.go)
|
||||
|
||||
### Not exploring all the Go testing features (#90)
|
||||
|
||||
|
|
@ -1424,19 +1424,19 @@ You should have a good reason to specify a channel size other than one for buffe
|
|||
|
||||
Place unit tests in a different package to enforce writing tests that focus on an exposed behavior, not internals.
|
||||
|
||||
[Source code](../src/11-testing/90-testing-features/different-package/main_test.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/90-testing-features/different-package/main_test.go)
|
||||
|
||||
* Utility functions
|
||||
|
||||
Handling errors using the `*testing.T` variable instead of the classic `if err != nil` makes code shorter and easier to read.
|
||||
|
||||
[Source code](../src/11-testing/90-testing-features/utility-function/main_test.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/90-testing-features/utility-function/main_test.go)
|
||||
|
||||
* Setup and teardown
|
||||
|
||||
You can use setup and teardown functions to configure a complex environment, such as in the case of integration tests.
|
||||
|
||||
[Source code](../src/11-testing/90-testing-features/setup-teardown/main_test.go)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/90-testing-features/setup-teardown/main_test.go)
|
||||
|
||||
### Not using fuzzing (community mistake)
|
||||
|
||||
|
|
@ -1456,19 +1456,19 @@ Credits: [@jeromedoucet](https://github.com/jeromedoucet)
|
|||
|
||||
Being conscious of the cache line concept is critical to understanding how to organize data in data-intensive applications. A CPU doesn’t fetch memory word by word; instead, it usually copies a memory block to a 64-byte cache line. To get the most out of each individual cache line, enforce spatial locality.
|
||||
|
||||
[Source code](../src/12-optimizations/91-cpu-caches/cache-line/)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/91-cpu-caches/cache-line/)
|
||||
|
||||
* Slice of structs vs. struct of slices
|
||||
|
||||
<!-- TODO -->
|
||||
|
||||
[Source code](../src/12-optimizations/91-cpu-caches/slice-structs/)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/91-cpu-caches/slice-structs/)
|
||||
|
||||
* Predictability
|
||||
|
||||
Making code predictable for the CPU can also be an efficient way to optimize certain functions. For example, a unit or constant stride is predictable for the CPU, but a non-unit stride (for example, a linked list) isn’t predictable.
|
||||
|
||||
[Source code](../src/12-optimizations/91-cpu-caches/predictability/)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/91-cpu-caches/predictability/)
|
||||
|
||||
* Cache placement policy
|
||||
|
||||
|
|
@ -1478,31 +1478,31 @@ Credits: [@jeromedoucet](https://github.com/jeromedoucet)
|
|||
|
||||
**TL;DR**: Knowing that lower levels of CPU caches aren’t shared across all the cores helps avoid performance-degrading patterns such as false sharing while writing concurrency code. Sharing memory is an illusion.
|
||||
|
||||
[Source code](../src/12-optimizations/92-false-sharing/)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/92-false-sharing/)
|
||||
|
||||
### Not taking into account instruction-level parallelism (#93)
|
||||
|
||||
**TL;DR**: Use instruction-level parallelism (ILP) to optimize specific parts of your code to allow a CPU to execute as many parallel instructions as possible. Identifying data hazards is one of the main steps.
|
||||
|
||||
[Source code](../src/12-optimizations/93-instruction-level-parallelism/)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/93-instruction-level-parallelism/)
|
||||
|
||||
### Not being aware of data alignment (#94)
|
||||
|
||||
**TL;DR**: You can avoid common mistakes by remembering that in Go, basic types are aligned with their own size. For example, keep in mind that reorganizing the fields of a struct by size in descending order can lead to more compact structs (less memory allocation and potentially a better spatial locality).
|
||||
|
||||
[Source code](../src/12-optimizations/94-data-alignment/)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/94-data-alignment/)
|
||||
|
||||
### Not understanding stack vs. heap (#95)
|
||||
|
||||
**TL;DR**: Understanding the fundamental differences between heap and stack should also be part of your core knowledge when optimizing a Go application. Stack allocations are almost free, whereas heap allocations are slower and rely on the GC to clean the memory.
|
||||
|
||||
[Source code](../src/12-optimizations/95-stack-heap/)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/95-stack-heap/)
|
||||
|
||||
### Not knowing how to reduce allocations (API change, compiler optimizations, and `sync.Pool`) (#96)
|
||||
|
||||
**TL;DR**: Reducing allocations is also an essential aspect of optimizing a Go application. This can be done in different ways, such as designing the API carefully to prevent sharing up, understanding the common Go compiler optimizations, and using `sync.Pool`.
|
||||
|
||||
[Source code](../src/12-optimizations/96-reduce-allocations/)
|
||||
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/96-reduce-allocations/)
|
||||
|
||||
### Not relying on inlining (#97)
|
||||
|
||||
|
|
|
|||
|
|
@ -281,6 +281,44 @@
|
|||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="/9-generics/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Being confused about when to use generics (#9)
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="/chapter-1/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Go: Simple to learn but hard to master
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="/jobs/" class="md-nav__link">
|
||||
|
||||
|
|
@ -328,9 +366,9 @@
|
|||
|
||||
|
||||
|
||||
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4" >
|
||||
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_6" >
|
||||
|
||||
<label class="md-nav__link" for="__nav_4" id="__nav_4_label" tabindex="0">
|
||||
<label class="md-nav__link" for="__nav_6" id="__nav_6_label" tabindex="0">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
|
|
@ -341,8 +379,8 @@
|
|||
<span class="md-nav__icon md-icon"></span>
|
||||
</label>
|
||||
|
||||
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_4_label" aria-expanded="false">
|
||||
<label class="md-nav__title" for="__nav_4">
|
||||
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_6_label" aria-expanded="false">
|
||||
<label class="md-nav__title" for="__nav_6">
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
Chinese
|
||||
</label>
|
||||
|
|
|
|||
764
site/9-generics/index.html
Normal file
764
site/9-generics/index.html
Normal file
|
|
@ -0,0 +1,764 @@
|
|||
|
||||
<!doctype html>
|
||||
<html lang="en" class="no-js">
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="prev" href="..">
|
||||
|
||||
|
||||
<link rel="next" href="../chapter-1/">
|
||||
|
||||
|
||||
<link rel="icon" href="../Go-Logo_Fuchsia.svg">
|
||||
<meta name="generator" content="mkdocs-1.5.2, mkdocs-material-9.3.1">
|
||||
|
||||
|
||||
<title>100 Go Mistakes</title>
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../assets/stylesheets/main.046329b4.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../assets/stylesheets/palette.85d0ee34.min.css">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
|
||||
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
|
||||
|
||||
|
||||
|
||||
<script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce((e,_)=>(e<<5)-e+_.charCodeAt(0),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<body dir="ltr" data-md-color-scheme="default" data-md-color-primary="green" data-md-color-accent="indigo">
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&"object"==typeof palette.color)for(var key of Object.keys(palette.color))document.body.setAttribute("data-md-color-"+key,palette.color[key])</script>
|
||||
|
||||
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
|
||||
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
|
||||
<label class="md-overlay" for="__drawer"></label>
|
||||
<div data-md-component="skip">
|
||||
|
||||
|
||||
<a href="#being-confused-about-when-to-use-generics-9" class="md-skip">
|
||||
Skip to content
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<div data-md-component="announce">
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href=".." title="100 Go Mistakes" class="md-header__button md-logo" aria-label="100 Go Mistakes" data-md-component="logo">
|
||||
|
||||
<img src="../Go-Logo_Fuchsia.svg" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2Z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
100 Go Mistakes
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Being confused about when to use generics (#9)
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
|
||||
|
||||
|
||||
|
||||
<input class="md-option" data-md-color-media="" data-md-color-scheme="default" data-md-color-primary="green" data-md-color-accent="indigo" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_1">
|
||||
|
||||
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_2" hidden>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69Z"/></svg>
|
||||
</label>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<input class="md-option" data-md-color-media="" data-md-color-scheme="slate" data-md-color-primary="blue-grey" data-md-color-accent="indigo" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_2">
|
||||
|
||||
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_1" hidden>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12c0-2.42-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69Z"/></svg>
|
||||
</label>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__option">
|
||||
<div class="md-select">
|
||||
|
||||
<button class="md-header__button md-icon" aria-label="Select language">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m12.87 15.07-2.54-2.51.03-.03A17.52 17.52 0 0 0 14.07 6H17V4h-7V2H8v2H1v2h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04M18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12m-2.62 7 1.62-4.33L19.12 17h-3.24Z"/></svg>
|
||||
</button>
|
||||
<div class="md-select__inner">
|
||||
<ul class="md-select__list">
|
||||
|
||||
<li class="md-select__item">
|
||||
<a href="/" hreflang="en" class="md-select__link">
|
||||
English
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="md-select__item">
|
||||
<a href="/chinese/" hreflang="zh" class="md-select__link">
|
||||
Chinese (Simplified)
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
|
||||
<label class="md-search__icon md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg>
|
||||
</label>
|
||||
<nav class="md-search__options" aria-label="Search">
|
||||
|
||||
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"/></svg>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
</form>
|
||||
<div class="md-search__output">
|
||||
<div class="md-search__scrollwrap" data-md-scrollfix>
|
||||
<div class="md-search-result" data-md-component="search-result">
|
||||
<div class="md-search-result__meta">
|
||||
Initializing search
|
||||
</div>
|
||||
<ol class="md-search-result__list" role="presentation"></ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://github.com/teivah/100-go-mistakes" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
GitHub
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
</header>
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
|
||||
<div class="md-sidebar__scrollwrap">
|
||||
<div class="md-sidebar__inner">
|
||||
|
||||
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||||
<label class="md-nav__title" for="__drawer">
|
||||
<a href=".." title="100 Go Mistakes" class="md-nav__button md-logo" aria-label="100 Go Mistakes" data-md-component="logo">
|
||||
|
||||
<img src="../Go-Logo_Fuchsia.svg" alt="logo">
|
||||
|
||||
</a>
|
||||
100 Go Mistakes
|
||||
</label>
|
||||
|
||||
<div class="md-nav__source">
|
||||
<a href="https://github.com/teivah/100-go-mistakes" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
GitHub
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href=".." class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
📖 Book Presentation
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item md-nav__item--active">
|
||||
|
||||
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-nav__link md-nav__link--active" for="__toc">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Being confused about when to use generics (#9)
|
||||
</span>
|
||||
|
||||
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
</label>
|
||||
|
||||
<a href="./" class="md-nav__link md-nav__link--active">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Being confused about when to use generics (#9)
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-nav__title" for="__toc">
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
Table of contents
|
||||
</label>
|
||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#concepts" class="md-nav__link">
|
||||
Concepts
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#common-uses-and-misuses" class="md-nav__link">
|
||||
Common uses and misuses
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#conclusion" class="md-nav__link">
|
||||
Conclusion
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</nav>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../chapter-1/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Go: Simple to learn but hard to master
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../jobs/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
❤️ Go Jobs
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../mistakes/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
💡 Go Mistakes
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item md-nav__item--nested">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_6" >
|
||||
|
||||
<label class="md-nav__link" for="__nav_6" id="__nav_6_label" tabindex="0">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Chinese
|
||||
</span>
|
||||
|
||||
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
</label>
|
||||
|
||||
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_6_label" aria-expanded="false">
|
||||
<label class="md-nav__title" for="__nav_6">
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
Chinese
|
||||
</label>
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../chinese/presentation/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Presentation
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
|
||||
<div class="md-sidebar__scrollwrap">
|
||||
<div class="md-sidebar__inner">
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-nav__title" for="__toc">
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
Table of contents
|
||||
</label>
|
||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#concepts" class="md-nav__link">
|
||||
Concepts
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#common-uses-and-misuses" class="md-nav__link">
|
||||
Common uses and misuses
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#conclusion" class="md-nav__link">
|
||||
Conclusion
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-content" data-md-component="content">
|
||||
<article class="md-content__inner md-typeset">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h1 id="being-confused-about-when-to-use-generics-9">Being confused about when to use generics (#9)</h1>
|
||||
<p>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.</p>
|
||||
<h2 id="concepts">Concepts</h2>
|
||||
<p>Consider the following function that extracts all the keys from a <code>map[string]int</code> type:</p>
|
||||
<pre><code class="language-go">func getKeys(m map[string]int) []string {
|
||||
var keys []string
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
</code></pre>
|
||||
<p>What if we would like to use a similar feature for another map type such as a <code>map[int]string</code>? Before generics, Go developers had a couple of options: using code generation, reflection, or duplicating code.</p>
|
||||
<p>For example, we could write two functions, one for each map type, or even try to extend <code>getKeys</code> to accept different map types:</p>
|
||||
<pre><code class="language-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
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>We can start noticing a couple of issues:</p>
|
||||
<ul>
|
||||
<li>First, it increases boilerplate code. Indeed, whenever we want to add a case, it will require duplicating the <code>range</code> loop.</li>
|
||||
<li>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.</li>
|
||||
<li>Last but not least, as the key type can be either <code>int</code> or <code>string</code>, 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.</li>
|
||||
</ul>
|
||||
<p>Thanks to generics, we can now refactor this code using type parameters.</p>
|
||||
<p>Type parameters are generic types we can use with functions and types. For example, the following function accepts a type parameter:</p>
|
||||
<pre><code class="language-go">func foo[T any](t T) {
|
||||
// ...
|
||||
}
|
||||
</code></pre>
|
||||
<p>When calling <code>foo</code>, 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.</p>
|
||||
<p>Let’s get back to the <code>getKeys</code> function and use type parameters to write a generic version that would accept any kind of map:</p>
|
||||
<pre><code class="language-go">func getKeys[K comparable, V any](m map[K]V) []K { <1>
|
||||
var keys []K <2>
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
</code></pre>
|
||||
<p>To handle the map, we defined two kinds of type parameters. First, the values can be of any type: <code>V any</code>. However, in Go, the map keys can’t be of any type. For example, we cannot use slices:</p>
|
||||
<pre><code class="language-go">var m map[[]byte]int
|
||||
</code></pre>
|
||||
<p>This code leads to a compilation error: <code>invalid map key type []byte</code>. 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 <code>==</code> or <code>!=</code>). Hence, we defined <code>K</code> as <code>comparable</code> instead of <code>a``ny</code>.</p>
|
||||
<p>Restricting type arguments to match specific requirements is called a constraint. A constraint is an interface type that can contain:</p>
|
||||
<ul>
|
||||
<li>A set of behaviors (methods)</li>
|
||||
<li>But also arbitrary type</li>
|
||||
</ul>
|
||||
<p>Let’s see a concrete example for the latter. Imagine we don’t want to accept any <code>comparable</code> type for map key type. For instance, we would like to restrict it to either <code>int</code> or <code>string</code> types. We can define a custom constraint this way:</p>
|
||||
<pre><code class="language-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
|
||||
}
|
||||
</code></pre>
|
||||
<p>First, we define a <code>customConstraint</code> interface to restrict the types to be either <code>int</code> or <code>string</code> using the union operator <code>|</code> (we will discuss the use of <code>~</code> a bit later). Then, <code>K</code> is now a <code>customConstraint</code> instead of a <code>comparable</code> as before.</p>
|
||||
<p>Now, the signature of <code>getKeys</code> enforces that we can call it with a map of any value type, but the key type has to be an <code>int</code> or a <code>string</code>. For example, on the caller-side:</p>
|
||||
<pre><code class="language-go">m = map[string]int{
|
||||
"one": 1,
|
||||
"two": 2,
|
||||
"three": 3,
|
||||
}
|
||||
keys := getKeys(m)
|
||||
</code></pre>
|
||||
<p>Note that Go can infer that <code>getKeys</code> is called with a <code>string</code> type argument. The previous call was similar to this:</p>
|
||||
<pre><code class="language-go">keys := getKeys[string](m)
|
||||
</code></pre>
|
||||
<hr />
|
||||
<p><strong>NOTE:</strong> What’s the difference between a constraint using <code>~int</code> or <code>int</code>? Using <code>int</code> restricts it to that type, whereas <code>~int</code> restricts all the types whose underlying type is an <code>int</code>.</p>
|
||||
<p>To illustrate it, let’s imagine a constraint where we would like to restrict a type to any <code>int</code> type implementing the <code>String() string</code> method:</p>
|
||||
<pre><code class="language-go">type customConstraint interface {
|
||||
~int
|
||||
String() string
|
||||
}
|
||||
</code></pre>
|
||||
<p>Using this constraint will restrict type arguments to custom types like this one:</p>
|
||||
<pre><code class="language-cgo">type customInt int
|
||||
|
||||
func (i customInt) String() string {
|
||||
return strconv.Itoa(int(i))
|
||||
}
|
||||
</code></pre>
|
||||
<p>As <code>customInt</code> is an <code>int</code> and implements the <code>String() string</code> method, the <code>customInt</code> type satisfies the constraint defined.</p>
|
||||
<p>However, if we change the constraint to contain an <code>int</code> instead of an <code>~int</code>, using <code>customInt</code> would lead to a compilation error because the <code>int</code> type doesn’t implement <code>String() string</code>.</p>
|
||||
<hr />
|
||||
<p>Let’s also note the <code>constraints</code> package contains a set of common constraints such as <code>Signed</code> 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.</p>
|
||||
<p>So far, we have discussed examples using generics for functions. However, we can also use generics with data structures.</p>
|
||||
<p>For example, we will create a linked list containing values of any type. Meanwhile, we will write an <code>Add</code> method to append a node:</p>
|
||||
<pre><code class="language-cgo">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
|
||||
}
|
||||
</code></pre>
|
||||
<p>We use type parameters to define <code>T</code> and use both fields in <code>Node</code>. Regarding the method, the receiver is instantiated. Indeed, because <code>Node</code> is generic, it has to follow also the type parameter defined.</p>
|
||||
<p>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:</p>
|
||||
<pre><code class="language-go">type Foo struct {}
|
||||
|
||||
func (Foo) bar[T any](t T) {}
|
||||
</code></pre>
|
||||
<pre><code>./main.go:29:15: methods cannot have type parameters
|
||||
</code></pre>
|
||||
<p>Now, let’s delve into concrete cases where we should and shouldn’t use generics.</p>
|
||||
<h2 id="common-uses-and-misuses">Common uses and misuses</h2>
|
||||
<p>So when are generics useful? Let’s discuss a couple of common uses where generics <strong>are</strong> recommended:</p>
|
||||
<ul>
|
||||
<li>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.</li>
|
||||
<li>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:</li>
|
||||
</ul>
|
||||
<p><code>cgo
|
||||
func merge[T any](ch1, ch2 <-chan T) <-chan T {
|
||||
// ...
|
||||
}</code></p>
|
||||
<ul>
|
||||
<li>Meanwhile, instead of factoring out a type, we can factor out behaviors. For example, the <code>sort</code> package contains functions to sort different slice types such as <code>sort.Ints</code> or <code>sort.Float64s</code>. Using type parameters, we can factor out the sorting behaviors that rely on three methods, <code>Len</code>, <code>Less</code>, and <code>Swap</code>:</li>
|
||||
</ul>
|
||||
<p>```go
|
||||
type sliceFn[T any] struct { // Use type parameter
|
||||
s []T
|
||||
compare func(T, T) bool // Compare two T elements
|
||||
}</p>
|
||||
<p>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] }
|
||||
```</p>
|
||||
<p>Conversely, when is it recommended <strong>not</strong> to use generics?</p>
|
||||
<ul>
|
||||
<li>When just calling a method of the type argument. For example, consider a function that receives an <code>io.Writer</code> and call the <code>Write</code> method:</li>
|
||||
</ul>
|
||||
<p><code>cgo
|
||||
func foo[T io.Writer](w T) {
|
||||
b := getBytes()
|
||||
_, _ = w.Write(b)
|
||||
}</code></p>
|
||||
<ul>
|
||||
<li>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.</li>
|
||||
</ul>
|
||||
<h2 id="conclusion">Conclusion</h2>
|
||||
<p>Though generics can be very helpful in particular conditions, we should be cautious about when to use them and not use them.</p>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</article>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer class="md-footer">
|
||||
|
||||
<div class="md-footer-meta md-typeset">
|
||||
<div class="md-footer-meta__inner md-grid">
|
||||
<div class="md-copyright">
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="md-social">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="https://twitter.com/teivah" target="_blank" rel="noopener" title="twitter.com" class="md-social__link">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc.--><path d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"/></svg>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
<div class="md-dialog" data-md-component="dialog">
|
||||
<div class="md-dialog__inner md-typeset"></div>
|
||||
</div>
|
||||
|
||||
<script id="__config" type="application/json">{"base": "..", "features": [], "search": "../assets/javascripts/workers/search.dfff1995.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}}</script>
|
||||
|
||||
|
||||
<script src="../assets/javascripts/bundle.dff1b7c8.min.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
808
site/chapter-1/index.html
Normal file
808
site/chapter-1/index.html
Normal file
|
|
@ -0,0 +1,808 @@
|
|||
|
||||
<!doctype html>
|
||||
<html lang="en" class="no-js">
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="prev" href="../9-generics/">
|
||||
|
||||
|
||||
<link rel="next" href="../jobs/">
|
||||
|
||||
|
||||
<link rel="icon" href="../Go-Logo_Fuchsia.svg">
|
||||
<meta name="generator" content="mkdocs-1.5.2, mkdocs-material-9.3.1">
|
||||
|
||||
|
||||
<title>100 Go Mistakes</title>
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../assets/stylesheets/main.046329b4.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../assets/stylesheets/palette.85d0ee34.min.css">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
|
||||
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
|
||||
|
||||
|
||||
|
||||
<script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce((e,_)=>(e<<5)-e+_.charCodeAt(0),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<body dir="ltr" data-md-color-scheme="default" data-md-color-primary="green" data-md-color-accent="indigo">
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&"object"==typeof palette.color)for(var key of Object.keys(palette.color))document.body.setAttribute("data-md-color-"+key,palette.color[key])</script>
|
||||
|
||||
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
|
||||
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
|
||||
<label class="md-overlay" for="__drawer"></label>
|
||||
<div data-md-component="skip">
|
||||
|
||||
|
||||
<a href="#go-simple-to-learn-but-hard-to-master" class="md-skip">
|
||||
Skip to content
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<div data-md-component="announce">
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href=".." title="100 Go Mistakes" class="md-header__button md-logo" aria-label="100 Go Mistakes" data-md-component="logo">
|
||||
|
||||
<img src="../Go-Logo_Fuchsia.svg" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2Z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
100 Go Mistakes
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Go: Simple to learn but hard to master
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
|
||||
|
||||
|
||||
|
||||
<input class="md-option" data-md-color-media="" data-md-color-scheme="default" data-md-color-primary="green" data-md-color-accent="indigo" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_1">
|
||||
|
||||
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_2" hidden>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69Z"/></svg>
|
||||
</label>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<input class="md-option" data-md-color-media="" data-md-color-scheme="slate" data-md-color-primary="blue-grey" data-md-color-accent="indigo" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_2">
|
||||
|
||||
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_1" hidden>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12c0-2.42-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69Z"/></svg>
|
||||
</label>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__option">
|
||||
<div class="md-select">
|
||||
|
||||
<button class="md-header__button md-icon" aria-label="Select language">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m12.87 15.07-2.54-2.51.03-.03A17.52 17.52 0 0 0 14.07 6H17V4h-7V2H8v2H1v2h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04M18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12m-2.62 7 1.62-4.33L19.12 17h-3.24Z"/></svg>
|
||||
</button>
|
||||
<div class="md-select__inner">
|
||||
<ul class="md-select__list">
|
||||
|
||||
<li class="md-select__item">
|
||||
<a href="/" hreflang="en" class="md-select__link">
|
||||
English
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="md-select__item">
|
||||
<a href="/chinese/" hreflang="zh" class="md-select__link">
|
||||
Chinese (Simplified)
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
|
||||
<label class="md-search__icon md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg>
|
||||
</label>
|
||||
<nav class="md-search__options" aria-label="Search">
|
||||
|
||||
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"/></svg>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
</form>
|
||||
<div class="md-search__output">
|
||||
<div class="md-search__scrollwrap" data-md-scrollfix>
|
||||
<div class="md-search-result" data-md-component="search-result">
|
||||
<div class="md-search-result__meta">
|
||||
Initializing search
|
||||
</div>
|
||||
<ol class="md-search-result__list" role="presentation"></ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://github.com/teivah/100-go-mistakes" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
GitHub
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
</header>
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
|
||||
<div class="md-sidebar__scrollwrap">
|
||||
<div class="md-sidebar__inner">
|
||||
|
||||
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||||
<label class="md-nav__title" for="__drawer">
|
||||
<a href=".." title="100 Go Mistakes" class="md-nav__button md-logo" aria-label="100 Go Mistakes" data-md-component="logo">
|
||||
|
||||
<img src="../Go-Logo_Fuchsia.svg" alt="logo">
|
||||
|
||||
</a>
|
||||
100 Go Mistakes
|
||||
</label>
|
||||
|
||||
<div class="md-nav__source">
|
||||
<a href="https://github.com/teivah/100-go-mistakes" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
GitHub
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href=".." class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
📖 Book Presentation
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../9-generics/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Being confused about when to use generics (#9)
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item md-nav__item--active">
|
||||
|
||||
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-nav__link md-nav__link--active" for="__toc">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Go: Simple to learn but hard to master
|
||||
</span>
|
||||
|
||||
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
</label>
|
||||
|
||||
<a href="./" class="md-nav__link md-nav__link--active">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Go: Simple to learn but hard to master
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-nav__title" for="__toc">
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
Table of contents
|
||||
</label>
|
||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#go-outline" class="md-nav__link">
|
||||
Go outline
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#simple-doesnt-mean-easy" class="md-nav__link">
|
||||
Simple doesn’t mean easy
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#100-go-mistakes" class="md-nav__link">
|
||||
100 Go mistakes
|
||||
</a>
|
||||
|
||||
<nav class="md-nav" aria-label="100 Go mistakes">
|
||||
<ul class="md-nav__list">
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#bugs" class="md-nav__link">
|
||||
Bugs
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#needless-complexity" class="md-nav__link">
|
||||
Needless complexity
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#weaker-readability" class="md-nav__link">
|
||||
Weaker readability
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#suboptimal-or-unidiomatic-organization" class="md-nav__link">
|
||||
Suboptimal or unidiomatic organization
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#lack-of-api-convenience" class="md-nav__link">
|
||||
Lack of API convenience
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#under-optimized-code" class="md-nav__link">
|
||||
Under-optimized code
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#lack-of-productivity" class="md-nav__link">
|
||||
Lack of productivity
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#summary" class="md-nav__link">
|
||||
Summary
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</nav>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../jobs/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
❤️ Go Jobs
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../mistakes/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
💡 Go Mistakes
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item md-nav__item--nested">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_6" >
|
||||
|
||||
<label class="md-nav__link" for="__nav_6" id="__nav_6_label" tabindex="0">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Chinese
|
||||
</span>
|
||||
|
||||
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
</label>
|
||||
|
||||
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_6_label" aria-expanded="false">
|
||||
<label class="md-nav__title" for="__nav_6">
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
Chinese
|
||||
</label>
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../chinese/presentation/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Presentation
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
|
||||
<div class="md-sidebar__scrollwrap">
|
||||
<div class="md-sidebar__inner">
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-nav__title" for="__toc">
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
Table of contents
|
||||
</label>
|
||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#go-outline" class="md-nav__link">
|
||||
Go outline
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#simple-doesnt-mean-easy" class="md-nav__link">
|
||||
Simple doesn’t mean easy
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#100-go-mistakes" class="md-nav__link">
|
||||
100 Go mistakes
|
||||
</a>
|
||||
|
||||
<nav class="md-nav" aria-label="100 Go mistakes">
|
||||
<ul class="md-nav__list">
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#bugs" class="md-nav__link">
|
||||
Bugs
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#needless-complexity" class="md-nav__link">
|
||||
Needless complexity
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#weaker-readability" class="md-nav__link">
|
||||
Weaker readability
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#suboptimal-or-unidiomatic-organization" class="md-nav__link">
|
||||
Suboptimal or unidiomatic organization
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#lack-of-api-convenience" class="md-nav__link">
|
||||
Lack of API convenience
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#under-optimized-code" class="md-nav__link">
|
||||
Under-optimized code
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#lack-of-productivity" class="md-nav__link">
|
||||
Lack of productivity
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#summary" class="md-nav__link">
|
||||
Summary
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-content" data-md-component="content">
|
||||
<article class="md-content__inner md-typeset">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h1 id="go-simple-to-learn-but-hard-to-master">Go: Simple to learn but hard to master</h1>
|
||||
<p>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</p>
|
||||
<p>Making mistakes is part of everyone’s life. As Albert Einstein once said,</p>
|
||||
<blockquote>
|
||||
<p>A person who never made a mistake never tried anything new.</p>
|
||||
</blockquote>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<h2 id="go-outline">Go outline</h2>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<p>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 <a href="https://go.dev/doc/faq">Go FAQ</a> gives us some insight:</p>
|
||||
<blockquote>
|
||||
<p>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.</p>
|
||||
</blockquote>
|
||||
<p>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:</p>
|
||||
<ul>
|
||||
<li><em>Stability</em>—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.</li>
|
||||
<li><em>Expressivity</em>—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.</li>
|
||||
<li><em>Compilation</em>—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.</li>
|
||||
<li><em>Safety</em>—Go is a strong, statically typed language. Hence, it has strict compiletime rules, which ensure the code is type-safe in most cases.</li>
|
||||
</ul>
|
||||
<p>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.</p>
|
||||
<p>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?</p>
|
||||
<h2 id="simple-doesnt-mean-easy">Simple doesn’t mean easy</h2>
|
||||
<p>There is a subtle difference between simple and easy. <em>Simple</em>, applied to a technology, means not complicated to learn or understand. However, <em>easy</em> means that we can achieve anything without much effort. Go is simple to learn but not necessarily easy to master.</p>
|
||||
<p>Let’s take concurrency, for example. In 2019, a study focusing on concurrency bugs was published: <a href="https://songlh.github.io/paper/go-study.pdf">Understanding Real-World Concurrency Bugs in Go</a>. 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.</p>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<p>This book aims to help accelerate our journey toward proficiency by delving into 100 Go mistakes.</p>
|
||||
<h2 id="100-go-mistakes">100 Go mistakes</h2>
|
||||
<p>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?</p>
|
||||
<p>In a 2011 article, neuroscientists proved that the best time for brain growth is when we’re facing mistakes. 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. 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.</p>
|
||||
<p>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.</p>
|
||||
<blockquote>
|
||||
<p>Tell me and I forget. Teach me and I remember. Involve me and I learn.</p>
|
||||
</blockquote>
|
||||
<p><em>- Unknown</em></p>
|
||||
<p>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</p>
|
||||
<p>We introduce each mistake category next.</p>
|
||||
<h3 id="bugs">Bugs</h3>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<h3 id="needless-complexity">Needless complexity</h3>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<h3 id="weaker-readability">Weaker readability</h3>
|
||||
<p>Another kind of mistake is to weaken readability. As Robert C. Martin wrote in his book <em>Clean Code: A Handbook of Agile Software Craftsmanship</em>, 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.</p>
|
||||
<p>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).</p>
|
||||
<h3 id="suboptimal-or-unidiomatic-organization">Suboptimal or unidiomatic organization</h3>
|
||||
<p>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.</p>
|
||||
<h3 id="lack-of-api-convenience">Lack of API convenience</h3>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<h3 id="under-optimized-code">Under-optimized code</h3>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<h3 id="lack-of-productivity">Lack of productivity</h3>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<h2 id="summary">Summary</h2>
|
||||
<ul>
|
||||
<li>Go is a modern programming language that enables developer productivity, which is crucial for most companies today.</li>
|
||||
<li>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.</li>
|
||||
<li>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.</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</article>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer class="md-footer">
|
||||
|
||||
<div class="md-footer-meta md-typeset">
|
||||
<div class="md-footer-meta__inner md-grid">
|
||||
<div class="md-copyright">
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="md-social">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="https://twitter.com/teivah" target="_blank" rel="noopener" title="twitter.com" class="md-social__link">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc.--><path d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"/></svg>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
<div class="md-dialog" data-md-component="dialog">
|
||||
<div class="md-dialog__inner md-typeset"></div>
|
||||
</div>
|
||||
|
||||
<script id="__config" type="application/json">{"base": "..", "features": [], "search": "../assets/javascripts/workers/search.dfff1995.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}}</script>
|
||||
|
||||
|
||||
<script src="../assets/javascripts/bundle.dff1b7c8.min.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -283,6 +283,44 @@
|
|||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../../9-generics/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Being confused about when to use generics (#9)
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../../chapter-1/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Go: Simple to learn but hard to master
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../../jobs/" class="md-nav__link">
|
||||
|
||||
|
|
@ -332,9 +370,9 @@
|
|||
|
||||
|
||||
|
||||
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4" checked>
|
||||
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_6" checked>
|
||||
|
||||
<label class="md-nav__link" for="__nav_4" id="__nav_4_label" tabindex="0">
|
||||
<label class="md-nav__link" for="__nav_6" id="__nav_6_label" tabindex="0">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
|
|
@ -345,8 +383,8 @@
|
|||
<span class="md-nav__icon md-icon"></span>
|
||||
</label>
|
||||
|
||||
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_4_label" aria-expanded="true">
|
||||
<label class="md-nav__title" for="__nav_4">
|
||||
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_6_label" aria-expanded="true">
|
||||
<label class="md-nav__title" for="__nav_6">
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
Chinese
|
||||
</label>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
|
||||
|
||||
<link rel="next" href="jobs/">
|
||||
<link rel="next" href="9-generics/">
|
||||
|
||||
|
||||
<link rel="icon" href="Go-Logo_Fuchsia.svg">
|
||||
|
|
@ -403,6 +403,44 @@
|
|||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="9-generics/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Being confused about when to use generics (#9)
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="chapter-1/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Go: Simple to learn but hard to master
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="jobs/" class="md-nav__link">
|
||||
|
||||
|
|
@ -450,9 +488,9 @@
|
|||
|
||||
|
||||
|
||||
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4" >
|
||||
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_6" >
|
||||
|
||||
<label class="md-nav__link" for="__nav_4" id="__nav_4_label" tabindex="0">
|
||||
<label class="md-nav__link" for="__nav_6" id="__nav_6_label" tabindex="0">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
|
|
@ -463,8 +501,8 @@
|
|||
<span class="md-nav__icon md-icon"></span>
|
||||
</label>
|
||||
|
||||
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_4_label" aria-expanded="false">
|
||||
<label class="md-nav__title" for="__nav_4">
|
||||
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_6_label" aria-expanded="false">
|
||||
<label class="md-nav__title" for="__nav_6">
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
Chinese
|
||||
</label>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
|
||||
|
||||
<link rel="prev" href="..">
|
||||
<link rel="prev" href="../chapter-1/">
|
||||
|
||||
|
||||
<link rel="next" href="../mistakes/">
|
||||
|
|
@ -289,6 +289,44 @@
|
|||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../9-generics/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Being confused about when to use generics (#9)
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../chapter-1/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Go: Simple to learn but hard to master
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -347,9 +385,9 @@
|
|||
|
||||
|
||||
|
||||
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4" >
|
||||
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_6" >
|
||||
|
||||
<label class="md-nav__link" for="__nav_4" id="__nav_4_label" tabindex="0">
|
||||
<label class="md-nav__link" for="__nav_6" id="__nav_6_label" tabindex="0">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
|
|
@ -360,8 +398,8 @@
|
|||
<span class="md-nav__icon md-icon"></span>
|
||||
</label>
|
||||
|
||||
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_4_label" aria-expanded="false">
|
||||
<label class="md-nav__title" for="__nav_4">
|
||||
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_6_label" aria-expanded="false">
|
||||
<label class="md-nav__title" for="__nav_6">
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
Chinese
|
||||
</label>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
BIN
site/options.png
Normal file
BIN
site/options.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
BIN
site/rune.png
Normal file
BIN
site/rune.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
File diff suppressed because one or more lines are too long
Binary file not shown.
BIN
site/trim.png
Normal file
BIN
site/trim.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 106 KiB |
Loading…
Reference in a new issue